python_魔法方法(四):属性访问
通常可以通过点(.)操作符的形式去访问对象的属性,也可以通过BIF适当地去访问属性,看个例子吧
>>> class A(): def __init__(self): self.x = 'XYZ-xyz' >>> a = A() >>> a.x 'XYZ-xyz' >>> getattr(a,'x','没有这个属性') 'XYZ-xyz' >>> getattr(a,'z','没有这个属性') '没有这个属性' >>> setattr(a,'z','red') >>> getattr(a,'z','red') 'red' >>> delattr(a,'x') >>> a.x Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> a.x AttributeError: 'A' 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#30>", line 1, in <module> c.size AttributeError: 'C' object has no attribute 'size'
关于属性访问,肯定也有相应的魔法方法来管理。通过对这些魔法方法的重写,照样可以随心所欲控制对象的属性访问。
来介绍下属性相关的魔法方法:
魔法方法 | 含义 |
__getattr__(self,name) | 定义当用户试图获取一个不存在的属性十的行为 |
__getattribute__(self,name) | 定义当该类的属性被访问时的行为 |
__setattr__(self,name) | 定义当一个属性被设置时的行为 |
__delattr__(self,name) | 定义当一个属性被删除时的行为 |
好了,我们先来做个测试:
>>> class A(): def __getattribute__(self,name): print('getattribute') #使用super()调用object基类__getattribut__()方法 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') >>> a = A() >>> a.x getattribute getattr >>> a.x = 1 setattr >>> a.x getattribute 1 >>> del a.x delattr >>> setattr(a,'y','yellow') setattr
这几个魔法方法在使用的时候要注意,防止进入死循环陷阱,下面通过实例来说明,写一个矩形类(Rectangle),默认有宽(width)和高(height)两个属性;如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时高宽都应该等于边长
>>> class Rectangle(): def __init__(self,width = 0,height = 0): self.width = width self.height = height def __setattr__(self,key,value): if key == 'square': self.width = value self.height = value else: self.key = value def getArea(self): return self.width *self.height >>> r1 = Rectangle(4,5) Traceback (most recent call last): File "<pyshell#65>", line 1, in <module> r1 = Rectangle(4,5) File "<pyshell#64>", line 3, in __init__ self.width = width File "<pyshell#64>", line 10, in __setattr__ self.key = value File "<pyshell#64>", line 10, in __setattr__ self.key = value File "<pyshell#64>", line 10, in __setattr__ self.key = value [Previous line repeated 325 more times] File "<pyshell#64>", line 6, in __setattr__ if key == 'square': RecursionError: maximum recursion depth exceeded in comparison
出现这个循环的原因在于调用了__init__()方法,在这里给self.width和self.height分别初始化赋值。一旦发生赋值操作就会自动触发__setattr__()魔法方法,width和height两个属性被赋值,于是执行了else的下边的语句,就又变成了self.width=value,那么久相当于又触发了__setattr__()魔法方法,造成了死循环。
解决方法一,就是和刚才的一样,使用super()来调用基类的__setattr__(),那么这样就依赖基类的方法来实现赋值:
>>> class Rectangle(): def __init__(self,width = 0,height = 0): self.width = width self.height = height def __setattr__(self,key,value): if key == 'square': self.width = value self.height = value else: super().__setattr__(key,value) def getArea(self): return self.width *self.height >>> r1 = Rectangle(4,5) >>> r1.getArea() 20 >>> r1.square = 10 >>> r1.getArea() 100
解决方法二:给特殊属性__dict__赋值。对象有一个特殊的属性,__dict__,它的作用是以字典的形式显示出当前对象的所有属性以及相对于的值。
调用死循环的类
>>> r1.__dict__ {'width': 10, 'height': 10} >>> class Rectangle(): def __init__(self,width = 0,height = 0): self.width = width self.height = height def __setattr__(self,key,value): if key == 'square': self.width = value self.height = value
else:
self.__dict__[name] = value
>>> r1 = Rectangle(4,5)