После пары про объекты и специальные методы в Python мне захотелось сделать более наглядную демонстрацию в каких же случаях что зовется. Вместе с коллегами по преподаванию курса Python мы написали небольшого "шпиона" spy.py, который носит учебный характер и совершенно не претендует на полноту и универсальность. Но любые улучшения приветствуются.

Circle1

Рассмотрим первый пример:

import math

class Circle:

    def __init__(self, x, y, r):
        self.x = x
        self.y = y
        self.r = r

    def __str__(self):
        return "Circle(x={}, y={}, r={})".format(self.x, self.y, self.r)

    def __add__(self, other):
        return Circle(self.x + other.x, self.y + other.y, self.r + other.r)

    def area(self):
        return math.pi * self.r ** 2

Тестировать поведения класса мы будем следующим образом:

def test(cls):
    print(">>> Testing {}".format(cls))
    c = cls(1, 2, 3)
    str(c)
    c.r
    c.r = 4
    c.area()
    c + c

Для Circle1 мы получим:

--> __new__ : args=(<class 'circle1.Circle'>, 1, 2, 3), kwargs={}
--> __init__ : args=(<circle1.Circle object at 0x1097cab10>, 1, 2, 3), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cab10>, 'x', 1), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cab10>, 'y', 2), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cab10>, 'r', 3), kwargs={}
--> __str__ : args=(<circle1.Circle object at 0x1097cab10>,), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'r'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'r'), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cab10>, 'r', 4), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'area'), kwargs={}
--> area : args=(<circle1.Circle object at 0x1097cab10>,), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'r'), kwargs={}
--> __add__ : args=(<circle1.Circle object at 0x1097cab10>,
                    <circle1.Circle object at 0x1097cab10>), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'r'), kwargs={}
--> __getattribute__ : args=(<circle1.Circle object at 0x1097cab10>, 'r'), kwargs={}
--> __new__ : args=(<class 'circle1.Circle'>, 2, 4, 8), kwargs={}
--> __init__ : args=(<circle1.Circle object at 0x1097cabd0>, 2, 4, 8), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cabd0>, 'x', 2), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cabd0>, 'y', 4), kwargs={}
--> __setattr__ : args=(<circle1.Circle object at 0x1097cabd0>, 'r', 8), kwargs={}

Circle2

Теперь добавим в Circle1 property:

import math

class Circle:

    def __init__(self, x, y, r):
        self.x = x
        self.y = y
        self.r = r

    @property
    def r(self):
        return self._r

    @r.setter
    def r(self, r):
        if r < 0:
            raise ValueError("Radius should be nonnegative")
        self._r = r

    def __str__(self):
        return "Circle(x={}, y={}, r={})".format(self.x, self.y, self.r)

    def __add__(self, other):
        return Circle(self.x + other.x, self.y + other.y, self.r + other.r)

    def area(self):
        return math.pi * self._r ** 2

Чтобы разобраться, что делает property, нужно познакомиться с дескрипторами. Найти реализацию property на чистом Python можно в Descriptor HowTo Guide.

Тестируя Circle2 на том же коде, что и Circle1, получаем:

--> __new__ : args=(<class 'circle2.Circle'>, 1, 2, 3), kwargs={}
--> __init__ : args=(<circle2.Circle object at 0x1097d0290>, 1, 2, 3), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, 'x', 1), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, 'y', 2), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, 'r', 3), kwargs={}
--> __set__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>, 3), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, '_r', 3), kwargs={}
--> __str__ : args=(<circle2.Circle object at 0x1097d0290>,), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'r'), kwargs={}
--> __get__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>,
                    <class 'circle2.Circle'>), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, '_r'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'r'), kwargs={}
--> __get__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>,
                    <class 'circle2.Circle'>), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, '_r'), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, 'r', 4), kwargs={}
--> __set__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>, 4), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097d0290>, '_r', 4), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'area'), kwargs={}
--> area : args=(<circle2.Circle object at 0x1097d0290>,), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, '_r'), kwargs={}
--> __add__ : args=(<circle2.Circle object at 0x1097d0290>,
                    <circle2.Circle object at 0x1097d0290>), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'x'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'y'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'r'), kwargs={}
--> __get__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>,
                    <class 'circle2.Circle'>), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, '_r'), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, 'r'), kwargs={}
--> __get__ : args=(<spy.property object at 0x1097a9188>,
                    <circle2.Circle object at 0x1097d0290>,
                    <class 'circle2.Circle'>), kwargs={}
--> __getattribute__ : args=(<circle2.Circle object at 0x1097d0290>, '_r'), kwargs={}
--> __new__ : args=(<class 'circle2.Circle'>, 2, 4, 8), kwargs={}
--> __init__ : args=(<circle2.Circle object at 0x1097bfe10>, 2, 4, 8), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097bfe10>, 'x', 2), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097bfe10>, 'y', 4), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097bfe10>, 'r', 8), kwargs={}
--> __set__ : args=(<spy.property object at 0x1097a9188>,i
                    <circle2.Circle object at 0x1097bfe10>, 8), kwargs={}
--> __setattr__ : args=(<circle2.Circle object at 0x1097bfe10>, '_r', 8), kwargs={}
comments powered by Disqus