19.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

19.2.1. Default

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

>>> class User:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> mark = User('Mark', 'Watney')
>>>
>>> mark  
<__main__.User object at 0x...>
>>>
>>> repr(mark)  
'<__main__.User object at 0x...>'
>>>
>>> mark.__repr__()  
'<__main__.User object at 0x...>'

19.2.2. Overloaded

>>> class User:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __repr__(self):
...         clsname = self.__class__.__name__
...         firstname = self.firstname
...         lastname = self.lastname
...         return f'{clsname}({firstname=}, {lastname=})'
>>>
>>>
>>> mark = User('Mark', 'Watney')
>>>
>>> mark
User(firstname='Mark', lastname='Watney')
>>>
>>> repr(mark)
"User(firstname='Mark', lastname='Watney')"
>>>
>>> mark.__repr__()
"User(firstname='Mark', lastname='Watney')"

19.2.3. Nested

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

>>> data = [1,2,3]
>>> print(data)
[1, 2, 3]
>>> class MyClass:
...     def __repr__(self): return 'repr'
...     def __str__(self): return 'str'
>>>
>>> data = [
...     MyClass(),
...     MyClass(),
...     MyClass(),
... ]
>>>
>>>
>>> print(data)
[repr, repr, repr]
>>>
>>> data
[repr, repr, repr]
>>>
>>> str(data)
'[repr, repr, repr]'
>>>
>>> repr(data)
'[repr, repr, repr]'

19.2.4. Use Case - 1

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

>>> class User:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __repr__(self):
...         return f'{self.firstname} {self.lastname}'
>>>
>>> admins = [
...     User('Mark', 'Watney'),
...     User('Melissa', 'Lewis'),
...     User('Rick', 'Martinez'),
... ]
>>>
>>> print(admins)
[Mark Watney, Melissa Lewis, Rick Martinez]

19.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

19.2.6. Assignments

# %% 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

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

# %% About
# - Name: Operator String Repr
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% 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ść

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

>>> mark = User('Mark', 'Watney')
>>> mark
User(firstname='Mark', lastname='Watney')
"""

# Modify `User` to overwrite `__repr__()` method
class User:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname


# %% 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

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

# %% About
# - Name: Operator String Repr
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

# %% 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ść

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

>>> mark = User('Mark', 'Watney')
>>> mark
User(firstname='Mark', lastname='Watney')

>>> melissa = Admin('Melissa', 'Lewis')
>>> melissa
Admin(firstname='Melissa', lastname='Lewis')
"""

# Modify `User` to overwrite `__repr__()` method
# Method should return proper class name on inheritance
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


# %% 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

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

# %% About
# - Name: Operator String Repr
# - Difficulty: easy
# - Lines: 3
# - Minutes: 3

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

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

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

>>> from inspect import isclass, ismethod
>>> assert isclass(Iris)
>>> iris = Iris(DATA)

>>> assert hasattr(Iris, '__repr__')
>>> assert ismethod(iris.__repr__)

>>> repr(iris)
"Iris(values=[4.7, 3.2, 1.3, 0.2], species='setosa')"
"""

DATA = (4.7, 3.2, 1.3, 0.2, 'setosa')

# Modify `Iris` to overwrite `__repr__()` method
# repr() -> Iris(values=[4.7, 3.2, 1.3, 0.2], species='setosa')
class Iris:
    values: list
    species: str

    def __init__(self, data):
        self.values = list(data[:-1])
        self.species = str(data[-1])


# %% 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

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

# %% About
# - Name: Operator String Nested
# - Difficulty: medium
# - Lines: 9
# - Minutes: 8

# %% English
# 1. Modify classes to overwrite `__str__()` and `__repr__()` methods
# 2. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasy aby nadpisać metody `__str__()` and `__repr__()`
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - Define `Accounts.__str__()`
# - Define `User.__str__()`
# - Define `Group.__repr__()`
# - Printing list will call repr on all elements

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

>>> result = User('Mark', 'Watney')
>>> print(result)
Mark Watney
>>> result = User('Melissa', 'Lewis')
>>> print(result)
Melissa Lewis
>>> result = User('Rick', 'Martinez')
>>> print(result)
Rick Martinez

>>> Group(gid=1, name='admins')
1(admins)
>>> Group(gid=2, name='staff')
2(staff)
>>> Group(gid=3, name='users')
3(users)

>>> result = Accounts(users=[])
>>> print(result)
<BLANKLINE>

>>> result = User('Mark', 'Watney', groups=[
...     Group(gid=2, name='staff'),
... ])
>>> print(result)
Mark Watney member of [2(staff)]

>>> result = User('Melissa', 'Lewis', groups=[
...     Group(gid=1, name='admins'),
...     Group(gid=2, name='staff'),
... ])
>>> print(result)
Melissa Lewis member of [1(admins), 2(staff)]

>>> result = Accounts([
...     User('Mark', 'Watney', groups=[
...         Group(gid=2, name='staff'),
...     ]),
...     User('Melissa', 'Lewis', groups=[
...         Group(gid=1, name='admins'),
...         Group(gid=2, name='staff'),
...     ]),
...     User('Rick', 'Martinez'),
... ])
>>>
>>> print(result)  # doctest: +NORMALIZE_WHITESPACE
Mark Watney member of [2(staff)]
Melissa Lewis member of [1(admins), 2(staff)]
Rick Martinez
"""


class Accounts:
    def __init__(self, users):
        self.users = users


class User:
    def __init__(self, firstname, lastname, groups=None):
        self.firstname = firstname
        self.lastname = lastname
        self.groups = groups if groups else []


class Group:
    def __init__(self, gid, name):
        self.gid = gid
        self.name = name