11.2. OOP Repr

  • Typing obj into REPL (console) calls repr(obj)

  • Calling repr(obj) calls obj.__repr__()

  • Method obj.__repr__() must return str

  • Intended for developers of your class

  • Shows object representation

  • Copy-paste for creating object with the same values

  • Useful for debugging

  • Printing list will call __repr__() method on each element

11.2.1. Default

  • Object without __repr__() method overloaded prints their memory address

>>> class User:
...     def __init__(self, username, password):
...         self.username = username
...         self.password = password
>>>
>>>
>>> alice = User('alice', 'secret')
>>>
>>> alice
<__main__.User object at 0x...>
>>>
>>> repr(alice)
'<__main__.User object at 0x...>'
>>>
>>> alice.__repr__()
'<__main__.User object at 0x...>'
>>>
>>> f'Hello {alice!r}'
'Hello <__main__.User object at 0x...>'

11.2.2. Overloaded

  • Not all information needs to be printed, ie. passwords

>>> class User:
...     def __init__(self, username, password):
...         self.username = username
...         self.password = password
...
...     def __repr__(self):
...         username = self.username
...         password = '******'
...         return f'User({username=}, {password=})'
>>>
>>>
>>> alice = User('alice', 'secret')
>>>
>>> alice
User(username='alice', password='******')
>>>
>>> repr(alice)
"User(username='alice', password='******')"
>>>
>>> alice.__repr__()
"User(username='alice', password='******')"
>>>
>>> f'Hello {alice!r}'
"Hello User(username='alice', password='******')"

11.2.3. Inheritance

Problem:

>>> class User:
...     def __init__(self, username, password):
...         self.username = username
...         self.password = password
...
...     def __repr__(self):
...         username = self.username
...         password = '******'
...         return f'User({username=}, {password=})'
>>>
>>>
>>> class Admin(User):
...    pass
>>>
>>>
>>> alice = User('alice', 'secret')
>>> bob = Admin('bob', 'qwerty')
>>>
>>> alice
User(username='alice', password='******')
>>>
>>> bob
User(username='bob', password='******')

Solution:

>>> class User:
...     def __init__(self, username, password):
...         self.username = username
...         self.password = password
...
...     def __repr__(self):
...         clsname = self.__class__.__name__
...         username = self.username
...         password = '******'
...         return f'{clsname}({username=}, {password=})'
>>>
>>>
>>> class Admin(User):
...    pass
>>>
>>>
>>> alice = User('alice', 'secret')
>>> bob = Admin('bob', 'qwerty')
>>>
>>> alice
User(username='alice', password='******')
>>>
>>> bob
Admin(username='bob', password='******')

11.2.4. Nested

  • Printing list will call __repr__() method on each element

>>> data = [1,2,3]
>>>
>>> print(data)
[1, 2, 3]
>>> class User:
...     def __init__(self, username): ...
...     def __repr__(self): return 'repr'
...     def __str__(self): return 'str'
>>>
>>> data = [
...     User('alice'),
...     User('bob'),
...     User('carol'),
... ]
>>>
>>>
>>> print(data)
[repr, repr, repr]
>>>
>>> data
[repr, repr, repr]
>>>
>>> str(data)
'[repr, repr, repr]'
>>>
>>> repr(data)
'[repr, repr, repr]'

11.2.5. Case Study

SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
WEEK = 7 * DAY
YEAR = 365.25 * DAY
MONTH = YEAR / 12
SOL = 24*HOUR + 39*MINUTE + 35.244*SECOND


class Mission:
    def __init__(self, name, duration):
        self.name = name
        self.duration = duration

    def __str__(self):
        return f'{self.name}'

    def __format__(self, unit):
        duration = self.duration
        match unit:
            case 's'  | 'sec'   | 'seconds':  duration /= SECOND
            case 'm'  | 'min'   | 'minutes':  duration /= MINUTE
            case 'h'  | 'hour'  | 'hours':    duration /= HOUR
            case 'd'  | 'day'   | 'days':     duration /= DAY
            case 'w'  | 'week'  | 'weeks':    duration /= WEEK
            case 'mth'| 'month' | 'months':   duration /= MONTH
            case 'y'  | 'year'  | 'years':    duration /= YEAR
            case 'sol':                       duration /= SOL
        return f'{duration:.1f} {unit}'


ares3 = Mission('Ares3', duration=543*SOL)

print(f'Mission Ares 3 duration was: {ares3:months}')
# Mission Ares 3 duration was: 18.3 months

11.2.6. Assignments

# %% About
# - Name: OOP Repr Overwrite
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify `User` to overwrite `__repr__()` method
# 2. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasę `User` aby nadpisać metodę `__repr__()`
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> alice = User('Alice', 'Apricot')
# >>> alice
# User(firstname='Alice', lastname='Apricot')

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> alice = User('Alice', 'Apricot')
>>> alice
User(firstname='Alice', lastname='Apricot')
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports

# %% Types
from typing import Callable
User: type
__repr__: Callable[[object], str]

# %% Data

# %% Result
class User:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

# %% About
# - Name: OOP Repr Inheritance
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify `User` to overwrite `__repr__()` method
# 2. Method should return proper class name on inheritance
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasę `User` aby nadpisać metodę `__repr__()`
# 2. Metoda powinna zwracać odpowiednią nazwę klasy przy dziedziczeniu
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> alice = User('Alice', 'Apricot')
# >>> alice
# User(firstname='Alice', lastname='Apricot')

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> alice = User('Alice', 'Apricot')
>>> alice
User(firstname='Alice', lastname='Apricot')

>>> bob = Admin('Bob', 'Blackthorn')
>>> bob
Admin(firstname='Bob', lastname='Blackthorn')
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports

# %% Types
from typing import Callable
User: type
Admin: type
__repr__: Callable[[object], str]

# %% Data

# %% Result
class User:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def __repr__(self):
        firstname = self.firstname
        lastname = self.lastname
        return f'User({firstname=}, {lastname=})'


class Admin(User):
    pass