python2.x中的新类型类(New-style class)与python3.x的类一致,均继承object类,而不继承object的类称为经典类(classic class),而对于这两种类,通常实例属性截取函数(generic instance attribute interception methods)的行为有所不一样,其在3.x和2.x的新类型类中,再也不被__x__操做符重载函数名(operator overloading name)的内建操做调用,对于该操做符重载函数名的搜索直接在类中搜索,而非实例中,而对于显式名的属性获取,包括__x__名,仍然要路经__getattr__,所以这是对于内建操做行为的主要影响,这种影响进而又影响到属性截取以及代理类。好比一个类定义了__getitem__索引重载函数,x是该类的一个实例,对于经典类来讲,x[I]与x.__getitem__(I)等价,而对于新类型类来讲,x[I]再也不被__getattr__获取,而显式x.__getitem__仍然能够被获取。python
1.对属性截取的影响:shell
首先看__getattr__在经典类与新类型类中表现的差别。python3.x
(1)在新类型类中(下列代码在3.x中实现):函数
构造了一个名为c的类,类长__getattr__方法可截取实例属性,而后打印截取到的属性名,最后返回实例对象的data对象的name方法结果。而对于x[0]内建操做表达式,则抛出了异常,该异常为c对象不支持索引,所以能够看出x[0]是直接在类中进行搜索,而跳过了实例属性截取函数__getattr__。测试
>>> getattr->__getitem__ x.__getitem__(0) getattr->__getitem__ 's'
而x.__getitem__(0)方法能够被__getattr__获取,相似的,对于其余内建操做,好比,x+'eggs',与x.__add__('eggs'),也有相同的反应。ui
>>> getattr->__add__ x.__add__('eggs') getattr->__add__ 'spameggs' >>> x+'eggs' Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> x+'eggs' TypeError: unsupported operand type(s) for +: 'c' and 'str'
>>> type(x).__getitem__(x,0)
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
type(x).__getitem__(x,0)
AttributeError: type object 'c' has no attribute '__getitem__'
当用x的类(即c)调用__getitem__,能够预想到的,抛出AttributeError,由于c并无__getitem__方法。spa
(2)以上代码在经典类中(在2.x中实现):代理
>>> class c: data='spam' def __getattr__(self,name): print('getattr->'+name) return getattr(self.data,name) File "<pyshell#0>", line 2 class c: ^ IndentationError: unexpected indent >>> class c: data='spam' def __getattr__(self,name): print('getattr->'+name) return getattr(self.data,name) >>> x=c() >>> x[0] getattr->__getitem__ 's' >>> getattr->__getitem__ x.__getitem__(0) getattr->__getitem__ 's' >>> getattr->__add__ x.__add__('eggs') getattr->__add__ 'spameggs' >>> x+'eggs' getattr->__coerce__ getattr->__add__ 'spameggs'
能够看到,在经典类型中,测试所有经过。code
>>> type(x).__getitem__(0) Traceback (most recent call last): File "<pyshell#8>", line 1, in <module> type(x).__getitem__(0) TypeError: descriptor '__getitem__' requires a 'instance' object but received a 'int'
可是,尝试用c类调用__getitem__,却抛出异常,主要是描述符(descriptor)的参数错误形成的,关于描述符的总结,将在后面的文章中专门整理。对象
2.对代理类的影响
实际上,在属性截取中,已经提到,在新类型类中,当直接用隐式的内建操做表达式,如x[i],x+等,抛出AttributError的异常,由于这种状况下,是直接从类开始搜索的,而c类中没有,因此才抛出了异常,那该怎么办呢?一个很天然的办法就是在类中,对要代理的隐式内建操做表达式进行从新定义,因此类就具有了要代理操做属性。
>>> class c: data='spam' def __getattr__(self,name): print('getattr->'+name) return getattr(self.data,name) def __getitem__(self,i): print('getitem:'+str(i)) return self.data[i] def __add__(self,other): print('add->'+other) return getattr(self.data,'__add__')(other)
上述代码在3.x中实现,经过对类c从新定义__getitem__,__add__从新定义实现了代理索引和加操做。
>>> x=c() >>> x.upper() getattr->upper 'SPAM'
能够看到__getattr__截取了通常方法upper()。
>>> x[0] getitem:0 's' >>> x.__getitem__(0) getitem:0 's' >>> x+'eggs' add->eggs 'spameggs' >>> x.__add__('eggs') add->eggs 'spameggs'
能够看到,代理成功。
(3)进一步的理解
事实上,子类继承基类(超类)的属性或者方法若在子类中没有重载,而子类实例若调用该属性,将不被__getattr__拦截,直接调用基类的属性。以下代码:
>>> class c: def test(self): print('test from c') >>> class d(c): def __getattr__(self,attr): print('getattr'+attr) >>> x=d() >>> x.test() test from c