9.7. OOP Stringify Objects¶
9.7.1. Rationale¶
str(obj)
->obj.__str__()
repr(obj)
->obj.__repr__()
format(obj, name)
->obj.__format__(name)
print(obj)
->str(obj)
->obj.__str__()
>>> import datetime
>>> date = datetime.datetime(1961, 4, 12, 6, 7)
>>>
>>> str(date)
'1961-04-12 06:07:00'
>>> repr(date)
'datetime.datetime(1961, 4, 12, 6, 7)'
>>> format(date, '%Y-%m-%d')
'1961-04-12'
>>> print(date)
1961-04-12 06:07:00
9.7.2. String¶
Calling function
str(obj)
callsobj.__str__()
Calling function
print(obj)
callsstr(obj)
, which callsobj.__str__()
Method
obj.__str__()
must returnstr
for end-user
>>> class Astronaut:
... pass
>>>
>>> astro = Astronaut()
>>> str(astro) # doctest: +ELLIPSIS
'<Astronaut object at 0x...>'
Object without __str__()
method overloaded prints their memory address:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
>>>
>>>
>>> astro = Astronaut('José Jiménez')
>>>
>>> print(astro) # doctest: +ELLIPSIS
<Astronaut object at 0x...>
>>> str(astro) # doctest: +ELLIPSIS
'<Astronaut object at 0x...>'
>>> astro.__str__() # doctest: +ELLIPSIS
'<Astronaut object at 0x...>'
Objects can verbose print if __str__()
method is present:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
...
... def __str__(self):
... return f'My name... {self.name}'
>>>
>>>
>>> astro = Astronaut('José Jiménez')
>>>
>>> print(astro)
My name... José Jiménez
>>> str(astro)
'My name... José Jiménez'
>>> astro.__str__()
'My name... José Jiménez'
9.7.3. Representation¶
Calling function
repr(obj)
callsobj.__repr__()
Method
obj.__repr__()
must returnstr
for developers
object representation
copy-paste for creating object with the same values
useful for debugging
printing
list
will call__repr__()
method on each element
>>> class Astronaut:
... pass
>>>
>>> astro = Astronaut()
>>> repr(astro) # doctest: +ELLIPSIS
'<Astronaut object at 0x...>'
Using __repr__()
on a class:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
...
... def __repr__(self):
... return f'Astronaut(name="{self.name}")'
>>>
>>>
>>> astro = Astronaut('José Jiménez')
>>>
>>> repr(astro)
'Astronaut(name="José Jiménez")'
>>> astro
Astronaut(name="José Jiménez")
Printing list
will call __repr__()
method on each element:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
>>>
>>> crew = [Astronaut('Jan Twardowski'),
... Astronaut('Mark Watney'),
... Astronaut('Melissa Lewis')]
>>>
>>> print(crew) # doctest: +ELLIPSIS
[<Astronaut object at 0x...>, <Astronaut object at 0x...>, <Astronaut object at 0x...>]
Printing list
will call __repr__()
method on each element:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
...
... def __repr__(self):
... return f'{self.name}'
>>>
>>> crew = [Astronaut('Jan Twardowski'),
... Astronaut('Mark Watney'),
... Astronaut('Melissa Lewis')]
>>>
>>> print(crew)
[Jan Twardowski, Mark Watney, Melissa Lewis]
9.7.4. Format¶
Calling function
format(obj, fmt)
callsobj.__format__(fmt)
Method
obj.__format__()
must returnstr
Used for advanced formatting
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
...
... def __format__(self, mood):
... if mood == 'happy':
... return f"Yuppi, we're going to space!"
... elif mood == 'scared':
... return f"I hope we don't crash"
>>>
>>>
>>> jose = Astronaut('José Jiménez')
>>>
>>> print(f'{jose:happy}')
Yuppi, we're going to space!
>>> print(f'{jose:scared}')
I hope we don't crash
>>> SECOND = 1
>>> MINUTE = 60 * SECOND
>>> HOUR = 60 * MINUTE
>>> DAY = 24 * HOUR
>>>
>>>
>>> class Duration:
... def __init__(self, seconds):
... self.seconds = seconds
...
... def __format__(self, unit):
... if unit == 'minutes':
... result = self.seconds / MINUTE
... elif unit == 'hours':
... result = self.seconds / HOUR
... elif unit == 'days':
... result = self.seconds / DAY
... return str(round(result, 2))
>>>
>>> duration = Duration(seconds=3600)
>>>
>>> print(f'Duration was {duration:minutes} min')
Duration was 60.0 min
>>> print(f'Duration was {duration:hours} hour')
Duration was 1.0 hour
>>> print(f'Duration was {duration:days} day')
Duration was 0.04 day
>>> SECOND = 1
>>> MINUTE = 60 * SECOND
>>> HOUR = 60 * MINUTE
>>> DAY = 24 * HOUR
>>>
>>>
>>> class Duration:
... def __init__(self, seconds):
... self.seconds = seconds
...
... def __format__(self, unit):
... if unit in ('s', 'sec', 'seconds'):
... result = self.seconds / SECOND
... elif unit in ('m', 'min', 'minutes'):
... result = self.seconds / MINUTE
... elif unit in ('h', 'hr', 'hours'):
... result = self.seconds / HOUR
... elif unit in ('d', 'days'):
... result = self.seconds / DAY
... return str(round(result, 2))
>>>
>>>
>>> duration = Duration(seconds=3600)
>>>
>>> print(f'Duration: {duration:s} seconds')
Duration: 3600.0 seconds
>>> print(f'Duration: {duration:m} minutes')
Duration: 60.0 minutes
>>> print(f'Duration: {duration:h} hours')
Duration: 1.0 hours
>>> print(f'Duration: {duration:d} days')
Duration: 0.04 days
>>> class Temperature:
... def __init__(self, kelvin):
... self.kelvin = kelvin
...
... def to_fahrenheit(self):
... return (self.kelvin-273.15) * 1.8 + 32
...
... def to_celsius(self):
... return self.kelvin - 273.15
...
... def __format__(self, unit):
... if unit == 'kelvin':
... value = self.kelvin
... elif unit == 'celsius':
... value = self.to_celsius()
... elif unit == 'fahrenheit':
... value = self.to_fahrenheit()
... return f'{value:.2f}'
>>>
>>>
>>> temp = Temperature(309.75)
>>>
>>> print(f'Temperature is {temp:kelvin} K')
Temperature is 309.75 K
>>> print(f'Temperature is {temp:celsius} C')
Temperature is 36.60 C
>>> print(f'Temperature is {temp:fahrenheit} F')
Temperature is 97.88 F
>>> class Point:
... def __init__(self, x, y, z=0):
... self.x = x
... self.y = y
... self.z = z
...
... def __format__(self, name):
...
... if name == 'in_2D':
... result = f"Point(x={self.x}, y={self.y})"
... elif name == 'in_3D':
... result = f"Point(x={self.x}, y={self.y}, z={self.z})"
... elif name == 'as_dict':
... result = self.__dict__
... elif name == 'as_tuple':
... result = tuple(self.__dict__.values())
... elif name == 'as_json':
... import json
... result = json.dumps(self.__dict__)
... return str(result)
>>>
>>>
>>> point = Point(x=1, y=2)
>>>
>>> print(f'{point:in_2D}')
Point(x=1, y=2)
>>> print(f'{point:in_3D}')
Point(x=1, y=2, z=0)
>>> print(f'{point:as_tuple}')
(1, 2, 0)
>>> print(f'{point:as_dict}')
{'x': 1, 'y': 2, 'z': 0}
>>> print(f'{point:as_json}')
{"x": 1, "y": 2, "z": 0}
9.7.5. Assignments¶
"""
* Assignment: OOP Stringify Str
* Complexity: easy
* Lines of code: 3 lines
* Time: 5 min
English:
1. Use code from "Given" section (see below)
2. While printing object show: species name and a sum of `self.features`
3. Result of sum round to one decimal place
4. Compare result with "Tests" section (see below)
Polish:
1. Użyj kodu z sekcji "Given" (patrz poniżej)
2. Przy wypisywaniu obiektu pokaż: nazwę gatunku i sumę `self.features`
3. Wynik sumowania zaokrąglij do jednego miejsca po przecinku
4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> for *features, label in DATA:
... iris = Iris(features, label)
... print(iris)
setosa 9.4
versicolor 16.3
virginica 19.3
"""
# Given
DATA = [(4.7, 3.2, 1.3, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica')]
class Iris:
def __init__(self, features, label):
self.features = features
self.label = label
"""
* Assignment: OOP Stringify Repr
* Complexity: easy
* Lines of code: 3 lines
* Time: 5 min
English:
1. Use code from "Given" section (see below)
2. Print representation of each instance with values (use `repr()`)
3. Result of sum round to two decimal places
4. Compare result with "Tests" section (see below)
Polish:
1. Użyj kodu z sekcji "Given" (patrz poniżej)
2. Wypisz reprezentację każdej z instancji z wartościami (użyj `repr()`)
3. Wynik sumowania zaokrąglij do dwóch miejsc po przecinku
4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> result = [Iris(X,y) for *X,y in DATA]
>>> result # doctest: +NORMALIZE_WHITESPACE
[Iris(features=[4.7, 3.2, 1.3, 0.2], label='setosa'),
Iris(features=[7.0, 3.2, 4.7, 1.4], label='versicolor'),
Iris(features=[7.6, 3.0, 6.6, 2.1], label='virginica')]
"""
# Given
DATA = [(4.7, 3.2, 1.3, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica')]
class Iris:
def __init__(self, features, label):
self.features = features
self.label = label
"""
* Assignment: OOP Stringify Format
* Complexity: easy
* Lines of code: 8 lines
* Time: 5 min
English:
1. Use code from "Given" section (see below)
2. Overload `__format__()` to convert length units
3. Compare result with "Tests" section (see below)
Polish:
1. Użyj kodu z sekcji "Given" (patrz poniżej)
2. Przeciąż `__format__()` aby konwertował jednostki długości
3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Hints:
* 1 km = 1000 m
* 1 m = 100 cm
Tests:
>>> result = Distance(meters=1337)
>>> format(result, 'km')
'1.337'
>>> format(result, 'cm')
'133700'
>>> format(result, 'm')
'1337'
"""
# Given
class Distance:
def __init__(self, meters):
self.meters = meters
"""
* Assignment: OOP Stringify Nested
* Complexity: medium
* Lines of code: 9 lines
* Time: 21 min
English:
1. Use code from "Given" section (see below)
2. Overload `str` and `repr` to achieve desired printing output
3. Compare result with "Tests" section (see below)
Polish:
1. Użyj kodu z sekcji "Given" (patrz poniżej)
2. Przeciąż `str` i `repr` aby osiągnąć oczekiwany rezultat wypisywania
3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Hints:
* Define `Crew.__str__()`
* Define `Astronaut.__str__()` and `Astronaut.__repr__()`
* Define `Mission.__repr__()`
Tests:
>>> melissa = Astronaut('Melissa Lewis')
>>> print(f'Commander: \\n{melissa}\\n') # doctest: +NORMALIZE_WHITESPACE
Commander:
Melissa Lewis
>>> mark = Astronaut('Mark Watney', experience=[
... Mission(2035, 'Ares 3')])
>>> print(f'Space Pirate: \\n{mark}\\n') # doctest: +NORMALIZE_WHITESPACE
Space Pirate:
Mark Watney veteran of [
2035: Ares 3]
>>> crew = Crew([
... Astronaut('Jan Twardowski', experience=[
... Mission(1969, 'Apollo 11'),
... Mission(2024, 'Artemis 3'),
... ]),
... Astronaut('José Jiménez'),
... Astronaut('Mark Watney', experience=[
... Mission(2035, 'Ares 3'),
... ]),
... ])
>>> print(f'Crew: \\n{crew}') # doctest: +NORMALIZE_WHITESPACE
Crew:
Jan Twardowski veteran of [
1969: Apollo 11,
2024: Artemis 3]
José Jiménez
Mark Watney veteran of [
2035: Ares 3]
"""
# Given
class Crew:
def __init__(self, members=()):
self.members = list(members)
class Astronaut:
def __init__(self, name, experience=()):
self.name = name
self.experience = list(experience)
class Mission:
def __init__(self, year, name):
self.year = year
self.name = name