После пары про объекты и специальные методы в 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={}