一、属性的几种访问方式
1、类.属性名
>>> class C: def __init__(self): self.x = 'X-man' >>> c = C() >>> c.x 'X-man'
2、用内置函数getattr()访问属性
>>> getattr(c,'x','莫有这个属性') 'X-man' >>> getattr(c,'y','莫有这个属性') '莫有这个属性' >>>
3、用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=1 >>> c <__main__.C object at 0x10c012f60> >>> c.x 1 >>> del c.x >>> c.x Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> c.x File "/Users/wufq/Desktop/property.py", line 6, in getsize return self.size AttributeError: 'C' object has no attribute 'size'
4、各类内置函数访问属性
* __getattr__(self,name)
定义当用户试图获取一个不存在的属性时的行为
* __getattribute__(self,name)
定义当该类的属性被访问时的行为
* __setattr__(self,name,value)
定义当一个属性被设置时的行为
* __delattr__(self,name)
定义当一个属性被删除时的行为
class C: def __getattr__(self,nane): print("__getattr__") def __getattribute__(self,name): print("__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) >>> c = C() >>> c.x __getattribute__ __getattr__ >>> c.x =1 __setattr__ >>> c.x __getattribute__ 1 >>> del c.x __delattr__ >>> c.x
'''
代码解析:
|--1、由第一个c.x先后打印出来__getattribute__和__getattr__,可以看出来,程序先找__getattribute__(定义当该类的属性被访问时的行为),然后
|-- 在找__getattr__(属性不存在时的访问行为)
|--2、由c.x =1 打印出来__setattr__,可以得知,当属性被设置时调用的是此方法
|--3、由del c.x=1 打印出来__delattr__可以得知,当属性被删除时调用此方法
'''
5、写一个矩形类,默认有宽和高两个属性
如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长
class Rectangle: def __init__(self,width=0,height=0): self.width = width self.height = height #用square这个属性来标记正方形 def __setattr__(self,name,): 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#15>", line 1, in <module>
r1 = Rectangle(4,5)
File "/Users/wufq/Desktop/正方形.py", line 4, in __init__
self.width = width
File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
self.name = value
File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
self.name = value
File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
self.name = value
[Previous line repeated 987 more times]
File "/Users/wufq/Desktop/正方形.py", line 8, in __setattr__
if name == 'square':
RecursionError: maximum recursion depth exceeded in comparison ''' 代码解析: |-- 为什么会报错:原因:首先实例化对象时给width和height赋值,赋值时就调用了__setattr__方法 |-- 进入__setattr__方法,会首先判断name有没有等于square,明显此时没有,则执行else里面的语句 |-- self.name = value,则又一次循环调用默认的__init__方法,依次这样就会进入死循环 '''
改进的方法
#第一种方法:采用super()方法调用基类 #第二种方法:采用字典的方法,__dict__ class Rectangle: def __init__(self,width=0,height=0): self.width = width self.height = height #用square这个属性来标记正方形 def __setattr__(self,name,value): if name == 'square': self.width = value self.height = value else: #self.name = value #super().__setattr__(name,value) self.__dict__[name]=value #采用__dict__方法的原因是:给属性square所赋值存储到dict字典内 def getArea(self): return self.width * self.height 执行结果: >>> r1 = Rectangle(4,5) >>> r1.getArea() 20 >>> r1.square = 10 >>> r1.getArea() 100 >>> r1.__dict__ {'width': 10, 'height': 10}
二、练习题
1、对象的属性发生赋值操作时,将实际的的值+1赋值给相应的属性。
def __setattr__(self,name,value): self.name = value + 1 错误原因:属性被赋值时,__setattr__()会被调用,而里边的self.name = value +1又会再次触发__setattr__()调用,导致无限递归(死循环)
正确的写法
class C: def __init__(self,name): self.name = name def __setattr__(self,name,value): self.__dict__[name]=value+1 print("name=",self.__dict__[name],"value=",value) 执行结果: >>> c = C(2) name= 3 value= 2
2、自定义该类的属性被访问的行为,你应该重写那个魔法方法
答:__getattribute__(self,name) :属性被访问时调用这个方法
3、当访问一个不存在的属性时,不报错且提示“该属性不存在”
class Demo: def __getattr__(self,name): return "该属性不存在" 执行结果: >>> demo = Demo() >>> demo.x '该属性不存在' >>>
4、编写一个Demo类,使的下边代码可以正常执行
class Demo: def __getattr__(self,name): self.name = 'FishC' return self.name def __getattribute__(self,name): return super().__getattribute__(name) def __setattr__(self,name,value): return super().__setattr__(name,value) 执行结果: >>> d=Demo() >>> d.x 'FishC' >>> d.x = "X-man" >>> d.x 'X-man' >>>
5、编写一个Counter类,用于实时检查对象有多少个属性
#需求:检测对象有多少个属性 class Counter: def __init__(self): super().__setattr__('counter',0) def __setattr__(self,name,value): super().__setattr__('counter',self.counter + 1) super().__setattr__(name,value) def __delattr__(self,name): super().__setattr__('counter',self.counter - 1) super().__delattr__(name) 执行结果: >>> c = Counter() >>> c.x=1 >>> c.counter 1 >>> c.y =2 >>> c.z = 3 >>> c.m =4 >>> c.counter 4 >>> del c.m >>> c.counter 3 >>>