Python’s Special Methods

Python uses some special methods to implement behavior. These methods are called dunder (double under) methods.

We already discovered a dunder method, the constructor method __init__.

Some other important special methods:

  • __str__ return a string for printable output

  • __repr__ return a string representation of the object

  • __len__ return the length of an object (mostly interesting for container objects)

  • __add__ allow to use the + operator with the object

  • __eq___ allow to compare objects with the == operator

You can find additional special methods in the Python docs.

Example:

class Fruit:
    def __init__(self, kind, amount=1):
        self.kind = kind
        self.amount = amount

    def add(self, fruit):
        if isinstance(fruit, Fruit) and self.kind == fruit.kind:
            self.amount += fruit.amount
            return True
        return False

    def __add__(self, other):
        self.add(other)
        return self

    def __len__(self):
        return self.amount

    def __str__(self):
        return f"{len(self)} {self.kind}"


class Basket:
    def __init__(self) -> None:
        self.fruits = []

    def add(self, new_fruit):
        for fruit in self.fruits:
            if fruit.add(new_fruit):
                return True

        self.fruits.append(new_fruit)
        return True

    def __len__(self):
        return len(self.fruits)

    def __add__(self, other):
        self.add(other)
        return self

    def __str__(self):
        output = ["The basket contains:"]
        for fruit in self.fruits:
            output.append(str(fruit))
        return "\n".join(output)


class Apple(Fruit):
    def __init__(self):
        super().__init__("apple")


class Pear(Fruit):
    def __init__(self):
        super().__init__("pear")


class Orange(Fruit):
    def __init__(self):
        super().__init__("orange")


class Kiwi(Fruit):
    def __init__(self):
        super().__init__("kiwi")


class BoxOf(Fruit):
    def __init__(self, fruit, amount):
        super().__init__(fruit.kind, amount)

    def __str__(self):
        return f"A box of {len(self)} {self.kind}s"


class Container(Basket):
    def __str__(self):
        output = ["------------------"]
        for fruit in self.fruits:
            output.append(f"|  {str(fruit)}")
        output.append("------------------")
        return "\n".join(output)


apple = Apple()
pear = Pear()
orange = Orange()
kiwi = Kiwi()
box_of_oranges = BoxOf(Orange(), 6)
container = Container()

basket = Basket()
basket += apple  # basket += apple is the same as basket = basket + apple
basket += box_of_oranges
basket += container
basket += pear
basket += kiwi
basket += orange

print(basket)
# prints out:
# The basket contains:
# 1 apple
# A box of 7 oranges
# ------------------
# | A container with
# |  1 pear
# |  1 kiwi
# ------------------