Magic Methods 4

Magic Methods: Attribute Access

We can use the “.” operator to access the attribute. Also, we can use the magic methods to access it.

For example : We can use “.”, getattr() to access it.

1
2
3
4
5
6
7
8
9
10
11
12
>>> class C:
def __init__(self):
self.x = 'X-man'


>>> c = C()
>>> c.x
'X-man'
>>> getattr(c, 'x', 'The attribute you access is nonexistent.')
'X-man'
>>> getattr(c, 'y', 'The attribute you access is nonexistent.')
'The attribute you access is nonexistent.'

We use this to link the x and the size in below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> class C:
def __init__(self, size=10):
self.size = size
def getSize(self):
return self.size
def setSize(self, value):
self.size = value
def delSize(self):
del self.size
x = property(getSize, setSize, delSize)


>>> c = C()
>>> c.x = 1
>>> c.x
1
>>> c.size
1
>>> del c.x
>>> c.size
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
c.size
AttributeError: 'C' object has no attribute 'size'

The magic methods to access attribute :

Magic Methods Definition
__getattr__(self, name) 定义当试图获取一个不存在的属性时的行为
__getattribute__(self, name) 定义当类的属性被访问时的行为
__setattr__(self, name) 定义当一个属性被设置时的行为
__delattr__(self, name) 定义当一个属性被删除时的行为

先访问getattribute,如果有值返回值,没有返回getattr的内容。

Try

  • Define a rectangle class, which defaults with width and height.
  • Define a square attribute, the returned value is the length of side, and declare the square.

Endless Loop :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle:
def __init__(self, width=0, height=0):
self.width = width
self.height = height

def __setattr__(self, name, value):
if name == 'square':
self.width = value
self.height = value
else:
self.name = value

def getArea(self):
return self.width * self.height

In this part, the endless loop is resulted by the evaluation. In the ‘int’ magic methods, ‘self.width = width’ will call the ‘setattr’ methods. In the ‘setattr’, there is evaluation again, which leads to the endless loop.
-> Endless iteration

We need to be careful about the endless iteration of accessing attribute. Try the ‘super()’ to call the base class to avoid the endless loop.

In the code above, we can change the Line 11, ‘self.name = value’ to ‘super().__setattr__(name, value)’ or ‘self.__dict__[name] = value’. The first one is recommended.

Here is the result after modification.

1
2
3
4
5
6
7
8
9
10
11
12
>>> r1 = Rectangle(4, 5)
>>> r1.getArea()
20
>>> r1.square = 10
>>> r1.width
10
>>> r1.height
10
>>> r1.getArea()
100
>>> r1.__dict__
{'width': 10, 'height': 10}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!