Что такое Dunder/Magic Methods?

Методы Dunder — это имена, которым предшествуют и за которыми следуют двойные символы подчеркивания, отсюда и название dunder. Их также называют магическими методами, и они могут помочь переопределить функциональность встроенных функций для пользовательских классов.

Описание объектов:

До:

class Data:
    def __init__(self, value: int):
        self.value = value


if __name__ == '__main__':
    data = Data(10)
    print(data)

# Output 
# <__main__.Data object at 0x7f81101a5790>

После:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __repr__(self):
        return f"Data value is: {self.value}"


if __name__ == '__main__':
    data = Data(10)
    print(data)

# Output
# Data value is: 10

Добавление или добавление двух объектов

До:

class Data:
    def __init__(self, value: int):
        self.value = value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    print(data1 + data2)

# Output
# print(data1 + data2)
# TypeError: unsupported operand type(s) for +: 'Data' and 'Data'

После:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __add__(self, other):
        return self.value + other.value

    def __sub__(self, other):
        return self.value - other.value

    def __mul__(self, other):
        return self.value * other.value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    print("Add")
    print(data1 + data2)
    print("Sub")
    print(data1 - data2)
    print("Mul")
    print(data1 * data2)

# Output
# Add
# 30
# Sub
# -10
# Mul
# 200

Сравнение объектов:

До:

class Data:
    def __init__(self, value: int):
        self.value = value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    print(data1 > data2)

# Output
# print(data1 > data2)
# TypeError: '>' not supported between instances of 'Data' and 'Data'

После:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __gt__(self, other):
        print("Greater Than Equal")
        return self.value >= other.value

    def __ge__(self, other):
        print("Greater Than")
        return self.value > other.value

    def __le__(self, other):
        print("Less Than Equal")
        return self.value <= other.value

    def __lt__(self, other):
        print("Less Than ")
        return self.value < other.value

    def __eq__(self, other):
        print("Equals")
        return self.value == other.value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    print(data1 > data2)
    print(data1 < data2)
    print(data1 >= data2)
    print(data1 <= data2)
    print(data1 == data2)

# Output
# Greater Than Equal
# False
# Less Than 
# True
# Greater Than
# False
# Less Than Equal
# True
# Equals
# False

Объект как ключ в Dict и удаление повторяющихся объектов в наборе

До:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __repr__(self):
        return f"Data value is - {self.value}"


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    date3 = Data(10)
    di = {data1: 10, data2: 20, date3: 10}
    se = {data1, data2, date3}
    print("Dict Value")
    print(di)
    print("Set Value")
    print(se)

# Output
# Dict Value
# {Data value is - 10: 10, Data value is - 20: 20, Data value is - 10: 10}
# Set Value
# {Data value is - 20, Data value is - 10, Data value is - 10}

После:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __repr__(self):
        return f"Data value is - {self.value}"

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        return self.value == other.value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    date3 = Data(10)
    di = {data1: 10, data2: 20, date3: 10}
    se = {data1, data2, date3}
    print("Dict Value")
    print(di)
    print("Set Value")
    print(se)

# Output
# Dict Value
# {Data value is - 10: 10, Data value is - 20: 20}
# Set Value
# {Data value is - 10, Data value is - 20}

Сортировка объектов:

До:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __repr__(self):
        return f"{self.value}"


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    date3 = Data(10)
    li = [data1, data2, date3]
    li.sort()
    print(li)

# Output
# li.sort()
# TypeError: '<' not supported between instances of 'Data' and 'Data'

После:

class Data:
    def __init__(self, value: int):
        self.value = value

    def __repr__(self):
        return f"{self.value}"

    def __lt__(self, other):
        return self.value < other.value


if __name__ == '__main__':
    data1 = Data(10)
    data2 = Data(20)
    date3 = Data(10)
    li = [data1, data2, date3]
    print("Before sorting")
    print(li)
    li.sort()
    print("After sorting")
    print(li)

# Output
# Before sorting
# [10, 20, 10]

# After sorting
# [10, 10, 20]

Итерация объектов:

До:

class Data:
    def __init__(self, stream: list):
        self.stream = stream


if __name__ == '__main__':
    data_stream = Data([1, 2, 3, 4])
    for data_s in data_stream:
        print(data_s)

# Output
# for data_s in data_stream:
# TypeError: 'Data' object is not iterable

После:

class Data:
    def __init__(self, stream: list):
        self.stream = stream
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= len(self.stream):
            raise StopIteration
        value = self.stream[self.current]
        self.current += 1
        return value


if __name__ == '__main__':
    data_stream = Data([1, 2, 3, 4])
    for data_s in data_stream:
        print(data_s)

# Output
# 1
# 2
# 3
# 4

Подробнее см. здесь.

Лучшие практики Python, нажмите здесь.

Принципы SOLID в питоне, нажмите здесь.