课时45:魔法方法:属性访问
目录:
一、属性访问
二、课时45课后习题及答案
****************
一、属性访问
****************
通常可以通过(.)操作符的形式去访问对象的属性,在类与对象这一章的最后一节也有谈到如何通过几个BIF适当地去访问属性:
>>> class C: def __init__(self): self.x = 'X-man' >>> c = C() >>> c.x 'X-man' >>> getattr(c,'x','木有这个属性') 'X-man' >>> getattr(c,'y','木有这个属性') '木有这个属性' >>> setattr(c,'y','Yellow') >>> getattr(c,'y','木有这个属性') 'Yellow' >>> delattr(c,'x') >>> c.x Traceback (most recent call last): File "<pyshell#11>", line 1, in <module> c.x AttributeError: 'C' object has no attribute 'x'
然后还介绍了一个叫做property()函数的用法,这个property()使得我们可以用属性去访问属性:
>>> 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 10 >>> c.x = 12 >>> c.x 12 >>> c.size 12 >>> del c.x >>> c.size Traceback (most recent call last): File "<pyshell#29>", line 1, in <module> c.size AttributeError: 'C' object has no attribute 'size'
那么关于属性访问,肯定也有相应得魔法方法来管理。通过对这些魔法方法的重写,可以随心所欲的控制对象的属性访问。
下表列举了属性相关的魔法方法。
__getattr__(self, name) 定义当用户试图获取一个不存在的属性时的行为 __getattribute__(self, name) 定义当该类的属性被访问时的行为 __setattr__(self, name, value) 定义当一个属性被设置时的行为 __delattr__(self, name) 定义当一个属性被删除时的行为
做个小测试:
class C: def __getattribute__(self, name): print('getattribute') # 使用 super() 调用 object 基类的 __getattribute__ 方法 return super().__getattribute__(name) def __setattr__(self, name, value): print('setattr') super().__setattr__(name, value) def __delattr__(self, name): print('delattr') super().__delattr__(name) def __getattr__(self, name): print('getattr')
>>> c = C() >>> c.x getattribute getattr >>> c.x = 1 setattr >>> c.x getattribute 1 >>> del c.x delattr >>> setattr(c,'y','Yellow') setattr
这几个魔法方法在使用上需要注意的是,有一个死循环的陷阱,初学者很容易中招,通过一个实例来讲解。
写一个矩形类,默认有宽和高两个属性;
如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。
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
>>> r1 = Rectangle(4,5) Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> r1 = Rectangle(4,5) File "C:\Users\14158\Desktop\lalallalalal.py", line 3, in __init__ self.width = width File "C:\Users\14158\Desktop\lalallalalal.py", line 11, in __setattr__ self.name = value File "C:\Users\14158\Desktop\lalallalalal.py", line 11, in __setattr__ self.name = value File "C:\Users\14158\Desktop\lalallalalal.py", line 11, in __setattr__ self.name = value [Previous line repeated 987 more times] File "C:\Users\14158\Desktop\lalallalalal.py", line 7, in __setattr__ if name == 'square': RecursionError: maximum recursion depth exceeded in comparison
这是为什么呢?
分析一下:实例化对象,调用__init__()方法,在这里self.width和self.heigth分别初始化赋值。一发生赋值操作,就会自动触发__setattr__()魔法方法,width和height两个属性被赋值,于是执行else的下边的语句,就变成了self.width = value,那么就相当于又触发了__setattr__(),那么这样就依赖基类的方法来实现赋值:
else: super().__setattr__(name,value)
>>> r1 = Rectangle(4,5) >>> r1.getArea() 20 >>> r1.square = 10 >>> r1.getArea() 100
另一种方法就是给特殊属性__dict__赋值。对象有一个特殊属性,叫做__dict__,它的作用是以字典的形式显示出当前对象的所有属性以及相对应的值:
else: self.__dict__[name] = value
运行结果一样。
*******************************
二、课时45课后习题及答案
*******************************
爱笑的男孩运气都不会差