python在实现类时有很多魔法,这里做个汇总。
__call__
在调用该方法时,无需显示地写出方法名
class Student(object): def __init__(self, name): self.name = name def m(self): print(3) def __call__(self): self.data=3 print('My name is %s.' % self.name) s = Student('Michael') s.m() # 3 显示地写出方法名 s() # My name is Michael. 无需显示地写出方法名
__dict__
含义见代码
class B(object): foo=1.3 print(B.__dict__) # 类属性 # {'__dict__': <attribute '__dict__' of 'B' objects>, # '__module__': '__main__', # 'foo': 1.3, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None} b=B() print b.__dict__ # {} # 实例属性 b.bar=13 print b.__dict__ # {'bar': 13} print b.bar # 13 ### # __dict__返回的是 dict类型,包括属性名和属性值 # 类的__dict__存储类的属性方法,不包括实例的 # 实例的__dict__只存储实例的属性方法,不包括类的,这点要注意。 print dir(B) print dir(b) ### # dir 返回的是一个list,只包括属性值 # dir 返回的是一个对象的所有属性
__getattr__
在访问不存在的属性和方法时,调用__getattr__方法
# 访问不存在的属性 class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99 s = Student() print s.name # 'Michael' print s.score # 99 # 不存在的属性去getattr里找 print s.t # None # getattr里找不到返回None,不报错 # 访问不存在的方法 class Student(object): def __getattr__(self, attr): if attr=='age': return lambda: 25 # 返回了函数的引用 s = Student() print s.age() # 25 # 方法也在getattr中找,但是找到的那个方法必须返回 函数 的引用 print s.age # <function <lambda> at 0x026672B0> # print s.ss() # TypeError: 'NoneType' object is not callable
__getattribute__
属性拦截器
在python中,所有类都要继承于object,object有很多内建的属性和方法,我们自定义的类自然也继承了这些属性和方法,但是这些属性方法很少被用到,而且很多属性方法需要被用户重写才能使用,__getattribute__就是其中一个。
先上代码
class Student(object): country = "china" def __init__(self,name,age): self.name = name self.age = age def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值 print("开始属性校验拦截功能") print(attr) return object.__getattribute__(self, attr) #返回属性值 s1 = Student("tom",19) print(Student.country,s1.country,s1.name,s1.age) #调用属性,会调用__getattribute__方法 # 开始属性校验拦截功能 # country # 开始属性校验拦截功能 # name # 开始属性校验拦截功能 # age # ('china', 'china', 'tom', 19)
1. 可以看到我们重写了__getattribute__,并返回了object的该方法
2. 类属性没有经过属性拦截器,实例属性经过属性拦截器,因为这是实例方法 (Student.country没有输出“开始属性校验...”)
3. __getattribute__可以被重写,从而实现定制的返回
重写属性拦截器
class Student(object): country = "china" def __init__(self,name,age): self.name = name self.age = age def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值 print("开始属性校验拦截功能") print(attr) if attr == "name": #注意这里引用原属性名不用self,直接引号引起来即可。 print("现在开始调用的是name属性") elif attr =="age": print("age属性将被更改") self.age = 1000 return 5555 # 如果这里没有return,会在后面那个return时返回1000 else: print("现在调用的是其他属性") return object.__getattribute__(self, attr) #返回属性名 s2 = Student("tom",19) print(s2.name,s2.age,s2.country) # 开始属性校验拦截功能 # name # 现在开始调用的是name属性 # 开始属性校验拦截功能 # age # age属性将被更改 # 开始属性校验拦截功能 # country # 现在调用的是其他属性 # ('tom', 1000, 'china')
可以看到age属性被更改。
__getitem__ and __setitem__
如果在类中定义了上述方法,那么该类的实例可以这样取值 c[key];如果类的实例执行c[key]运算,就会调用__getitem__方法。
class S(object): def __init__(self): self.s = 3 def __getitem__(self, item): return self.s def __setitem__(self, key, value): setattr(self, key ,value) s = S() print s['a'] # 3 s['s'] = 100 print(s['s']) # 100
注意,这里写的比较简单,在 getitem 时,不管 item 是什么,都返回 self.s ,所以 s['a'] 返回了3,重点是理解用法。
__slots__ 槽
__str__ and __repr__
__str__ 用于显示类的真实内容,多用于类的 print
如果没有 __str__
class Student(object): def __init__(self, name): self.name = name print Student('Michael') # <__main__.Student object at 0x109afb190> # 打印出一堆<__main__.Student object at 0x109afb190>,不好看
如果定义了 __str__
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % self.name print Student('Michael') # Student object (name: Michael)
显示的内容很直观了
但是我们平常不这么用啊,print class,很少这么用,那怎么办呢
s = Student('Michael') s # <__main__.Student object at 0x109afb310>
没有 print,内容还是不好看
直接显示变量调用的不是 __str__,而是 _repr___;
二者区别在于
__str__ 返回用户看到的字符串;
__repr__() 返回程序开发者看到的字符串,多用于调试
解决办法是再定义一个__repr__()。但是通常 __str__() 和 __repr__() 代码都是一样的,所以,有个偷懒的写法
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__
未完待续...