9.7. Stringify Objects

9.7.1. String

  • for end-user

  • print converts it's arguments to str() before printing

Listing 9.34. 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)        # <__main__.Astronaut object at 0x114175dd0>
str(astro)          # '<__main__.Astronaut object at 0x114175dd0>'
astro.__str__()     # '<__main__.Astronaut object at 0x114175dd0>'
Listing 9.35. 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.2. Representation

  • for developers

  • object representation

  • copy-paste for creating object with the same values

  • useful for debugging

  • printing list will call __repr__ on each element

Listing 9.36. 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")
Listing 9.37. printing list will call __repr__ on each element
class Astronaut:
    def __init__(self, name):
        self.name = name

crew = [
    Astronaut('Jan Twardowski'),
    Astronaut('Mark Watney'),
    Astronaut('Melissa Lewis'),
]

print(crew)
# [
#   <__main__.Astronaut object at 0x107871160>,
#   <__main__.Astronaut object at 0x107c422e8>,
#   <__main__.Astronaut object at 0x108156be0>
# ]
Listing 9.38. printing list will call __repr__ 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.3. String vs Representation

Listing 9.39. __str__ and __repr__
import datetime

str(datetime.datetime.now())
# 1961-04-12 6:07:00.000000

repr(datetime.datetime.now())
# datetime.datetime(1961, 4, 12, 6, 7, 0, 000000)

9.7.4. Format

  • 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':
            return str(self.seconds / MINUTE)

        if unit == 'hours':
            return str(self.seconds / HOUR)

        if unit == 'days':
            return str(round(self.seconds / DAY, 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
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.6 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':
            return f"Point(x={self.x}, y={self.y})"

        if name == 'in_3D':
            return f"Point(x={self.x}, y={self.y}, z={self.z})"

        if name == 'as_tuple':
            return str(tuple(self.__dict__.values()))

        if name == 'as_dict':
            return str(self.__dict__)

        if name == 'as_json':
            import json
            return json.dumps(self.__dict__)


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

9.7.5.1. OOP Stringify Str

English
  1. Use code from "Input" section (see below)

  2. While printing object show: species name and a sum method result

  3. Result of sum round to one decimal place

  4. Compare result with "Output" section (see below)

Polish
  1. Użyj kodu z sekcji "Input" (patrz poniżej)

  2. Przy wypisywaniu obiektu pokaż: nazwę gatunku i wynik metody sumującej

  3. Wynik sumowania zaokrąglij do jednego miejsca po przecinku

  4. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
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


for *features, label in DATA:
    iris = Iris(features, label)
    print(iris)
Output
setosa 9.4
versicolor 16.3
virginica 19.3

9.7.5.2. OOP Stringify Repr

English

#. Use code from "Input" section (see below) #. Print representation of each instance with values (use repr()) #. Result of sum round to two decimal places #. Compare result with "Output" section (see below)

Polish
  1. Użyj kodu z sekcji "Input" (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ą "Output" (patrz poniżej)

Input
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


result = [Iris(X,y) for *X,y in DATA]
print(result)
Output
result: List[Iris]
# [Iris(features=[7.6, 3.0, 6.6, 2.1], label='virginica'),
#  Iris(features=[7.6, 3.0, 6.6, 2.1], label='virginica'),
#  Iris(features=[7.6, 3.0, 6.6, 2.1], label='virginica')]

9.7.5.3. OOP Stringify Nested

English

#. Use code from "Input" section (see below) #. Overload str and repr to achieve desired printing output #. Compare result with "Output" section (see below)

Polish
  1. Użyj kodu z sekcji "Input" (patrz poniżej)

  2. Przeciąż str i repr aby osiągnąć oczekiwany rezultat wypisywania

  3. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
Listing 9.40. Address Book
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
Output
melissa = Astronaut('Melissa Lewis')

print(f'Commander: \n{melissa}\n')
# Commander:
# Melissa Lewis
mark = Astronaut('Mark Watney', experience=[
    Mission(2035, 'Ares 3'),
])

print(f'Space Pirate: \n{mark}\n')
# 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}')
# Crew:
# Jan Twardowski veteran of [
#       1969: Apollo 11,
#       2024: Artemis 3]
# José Jiménez
# Mark Watney veteran of [
#       2035: Ares 3]
The whys and wherefores
Hint
  • Define Crew.__str__()

  • Define Astronaut.__str__() and Astronaut.__repr__()

  • Define Mission.__repr__()