以下的讨论都限于,新式类(个人认为最好限于新式类)
class AA(object):
x = 'x from AA'
class BB(type):
x = 'x from BB'
y = 'y from BB'
class CC(AA):
__metaclass__ = BB
z = 'z from CC'
c = CC()
print CC.__bases__ # 打印得到CC的师父 # (__mian__.AA,)
print CC.__class__ # 打印得到CC的爸爸 # __mian__.BB
print c.__class__ # 打印得到c的爸爸 # __mian__.CC
把实例化称作爸爸,即__class__称作爸爸,也就是c = CC()中,CC是c的爸爸,c由CC实例化得到。
把继承称作师父,即__base__称作师父,也就是 class CC(AA): pass 中,AA是CC的师父,CC继承自AA。
显然对于一个对象来说,爸爸只能有一个,而且必须有一个(可以是默认的爸爸),而师父可以有多个,也可以没有。
本文将着重讨论c和CC的属性访问区别,因为子c++/java里面,我们一般只考虑c来属性访问,很少用CC来访问属性。
python一切皆对象的意思是:如果说对象有爸爸,那么Python里面所有东西都有爸爸;如果对象可以有自己的属性,那么python所有东西都可以有自己的属性。万物皆平等(除了object和type),CC相对于c来说,并没有什么特殊,都是对象而已,都有属性访问问题。
在BB和CC和c的关系中,我们称BB是meta class object,称CC是class object,称c是instance Object。即BB是c的爷爷,CC是c的爸爸。
但在BB和CC的关系中,我们称BB是class object,称CC是instance Object。即BB是CC的爸爸。
但在type和BB的关系中,我们称type是class object,称BB是instance Object。即type是BB的爸爸。
为什么要区分这些呢,下面是两个访问属性的例子,后续的文章也主要是为了区分这两种属性访问方式。
print CC.x # 请问是访问BB中的x,还是AA中的x。即访问爸爸的技能,还是师父的技能。
print c.y # 报错,CC只能遗传CC自己的技能(z)和师父AA的技能(x),不能遗传父亲BB的技能(x和y)
一个对象CC生下来,就拥有了父亲BB的技能(指属性x和y),
但是可以通过后天向师傅AA学习新的技能(指属性x),来增加或覆盖父亲BB的技能,
也可以自己修炼技能(指属性z)来增加或覆盖父亲BB的技能,
CC在生孩子的时候,只能遗传给孩子c,自己CC的技能和师父AA的技能。
'a.__class__==AA、AA.__class__==object、c.__class__==CC、CC.__class__==DD、DD.__base__==BB'
class AA(object):
bar = 123
def foo(self):
print self.bar
def __getattribute__(self, *args, **kwargs):
print("__getattribute__() in AA")
return object.__getattribute__(self, *args, **kwargs)
def __getattr__(self, name):
print("__getattr__() in AA ")
class BB(type):
def __getattribute__(self, *args, **kwargs):
print("__getattribute__() in BB")
return type.__getattribute__(self, *args, **kwargs)
def __getattr__(self, name):
print("__getattr__() in BB ")
class DD(BB):
def __getattr__(self, name):
print("__getattr__() in DD ")
class CC(AA):
__metaclass__ = DD
def __getattribute__(self, *args, **kwargs):
print("__getattribute__() in CC")
return object.__getattribute__(self, *args, **kwargs)
def __getattr__(self, name):
print("__getattr__() in CC ")
a = AA()
c = CC()
print c.attr # __getattribute__() in CC 和 __getattr__() in CC
print CC.attr # __getattribute__() in BB 和 __getattr__() in DD
print a.attr # __getattribute__() in AA 和 __getattr__() in AA
print a.foo() # 调用两次__getattribute__() in AA,一次是为了找'foo',一次是执行 print self.bar
print AA.attr # 抛出异常,因为AA对象的类型,也就是AA.__class__ == type,没有__getattr__方法兜底
print c.__getattribute__('attr')
# 由CC中的__getattribute__负责找'__getattribute__'属性,在CC中找到,并进行调用。
print CC.__getattribute__(c,'attr')
# 由BB中的__getattribute__负责找'__getattribute__'属性,在CC中找到,并进行调用。
object和type中的__getattribute__方法不一样,且会对参数做严格的检查。c用的是object的__getattribute__,
5):抛出异常AttributeError。
class DD(object):
'data descriptor'
def __init__(self,name):
self._name = name
def __get__(self, obj, cls=None): # ,', obj:', obj,', cls:', cls
print 'get in DD, name:', self._name
def __set__(self, obj, val):
print 'set in DD, name:', self._name
class Non_DD(object):
'non-data descriptor'
def __init__(self,name):
self._name = name
def __get__(self, obj, cls=None):
print 'get in Non_DD, name:', self._name
class AA(object):
x = DD('aa1')
y = Non_DD('aa2')
z = DD('aa3')
m = DD('aa4')
n = DD('aa5')
class BB(type):
x = DD('bb1')
y = DD('bb2')
z = Non_DD('bb3')
n = Non_DD('bb4')
k = DD('bb5')
t = Non_DD('bb6')
e = 'bb7'
class CC(AA):
__metaclass__ = BB
x = DD('cc1')
y = 'cc2'
z = Non_DD('cc3')
m = 'cc4'
e = Non_DD('cc7')
c = CC()
print '--------c的属性访问调用object中的__getattribute__方法--------'
print c.x # 步骤 2
print c.y # 步骤4
print c.z # 步骤 4
print c.m # 步骤 4
print c.n # 步骤 2
c.__dict__['n'] = "c.__dict__['n']"
print c.n # 步骤 2
c.__dict__['m'] = "c.__dict__['m']"
print c.m # 步骤 3
c.foo = Non_DD('foo in c obj')
print c.foo # 步骤 3
print '--------CC的属性访问调用type中的__getattribute__方法--------'
print CC.x # 步骤 2
print CC.y # 步骤 2
print CC.z # 步骤 3
print CC.m # 步骤 3
print CC.n # 步骤 3
CC.n = 'CC.n'
print CC.n # 步骤 3
print CC.t # 步骤 4
print CC.e # 步骤 3
CC.foo = Non_DD('foo in CC obj')
print CC.foo # 步骤 3
只有第2步中找到的数据描述符,才是真正的数据描述符,才能执行完整的数据描述符机制。
其它步骤中描述符和普通属性的优先级一样,只是会额外调用描述符的get方法。
class myclassmethod(object):
def __init__(self,method):
self._classmethod=method
def __get__(self,obj=None,cls=None):
print 'myclassmethod: **** ',obj,cls,' **** '
if cls == None:
cls = type(obj)
def _deco(*args,**kwargs):
return self._classmethod(cls,*args,**kwargs)
return _deco
class mystaticmethod(object):
def __init__(self,method):
self._staticmethod=method
def __get__(self,obj=None,cls=None):
print 'mystaticmethod: **** ',obj,cls,' **** '
def _deco(*args,**kwargs):
return self._staticmethod(*args,**kwargs)
return _deco
class method2bounded_function(object):
def __init__(self,method):
self._normalmethod=method
def __get__(self,obj=None,cls=None):
print 'normal function: **** ',obj,cls,' **** '
def _deco(*args,**kwargs):
if obj!=None:
return self._normalmethod(obj,*args,**kwargs)
elif args and isinstance(args[0],cls): # 对应的是从classObj中直接调用方法
return self._normalmethod(*args,**kwargs)
else:
print 'raise TypeError: unbound method'
return _deco
class AA(object):
@method2bounded_function
def foo1(obj):# 不用self为了和下面的cls做对比
print obj
print 'normal method'
@classmethod
def foo2(cls):
print cls
print 'the built-in classmethod decorator'
@myclassmethod
def foo3(cls):
print cls
print 'my classmethod decorator'
@staticmethod
def bar2():
print 'the built-in staticmethod decorator'
@mystaticmethod
def bar3():
print 'my staticmethod decorator'
a = AA()
print repr(a)
a.foo1() # 调用object中的__getattribute__,传给get的参数:obj和type(obj)
a.foo2()
a.foo3()
a.bar2()
a.bar3()
print repr(AA)
AA.foo1() # 调用type中的__getattribute__,传给get的参数:None和obj
AA.foo1(a)
AA.foo1(123)# get方法,会对参数进行检查
AA.foo2()
AA.foo3()
AA.bar2()
AA.bar3()
装饰器知识点补充:
对于静态方法来说只需要__get__方法将原始参数传给原方法即可,
对于类方法来说,__get__方法需要保证将cls和原始参数传给原方法即可,
对于普通方法来说,__get__方法需要保证将obj和原始参数传给原方法。
object和type中的__getattribute__方法的区别可能有很多,本文暂时只讨论这一个。
可以看到每次返回的都是封装后的方法,所以如果测试方法的id,则每次都不一样,如下所示:
print id(a.foo1) # 代码要一行一行的执行
print id(a.foo1)
print id(a.foo1)
首先对于序列 [ C1, C2, C3…, CN ] 来说:
头 head = C1,尾 tail = C2C3…CN。
则C3算法:
L[C(B1B2…BN)] = C + merge(L(B1),L(B2),…,L(BN), B1B2…BN),
其中C是本次考虑的类对象,(B1B2…BN)类C的基类列表,按照定义时的顺序。merge函数:
从merge中从左往右,寻找不存在于任何tail中的head,取出来,然后从头再开始寻找,直至为空。否则,C3算法将会拒绝创建类C并抛出一个错误。
MRO需要满足局部优先级和单调性两个条件:
局部优先级::class Z(F,E): pass ,那么 F 的方法应该优先于 E 的方法。
单调性::在C中基类的线性化中,假如C1优先于C2,那么在C的任何子类中基类的线性化中,C1优先于C2。
class __metaclass__(type):
"All classes are metamagically modified to be nicely printed"
__repr__ = lambda cls: cls.__name__
class ex_1(object):
"Serious order disagreement" #From Guido
O = object
class F(O): pass
class E(F): pass
try:
class Z(F,E): pass #creates Z(A,B) in Python 2.2
except TypeError,e:
print 'error: ',TypeError,e
class ex_2(object):
"Serious order disagreement" #From Guido
O = object
class X(O): pass
class Y(O): pass
class A(X,Y): pass
class B(Y,X): pass
try:
class Z(A,B): pass #creates Z(A,B) in Python 2.2
except TypeError,e:
print 'error: ',TypeError,e
class ex_5(object):
"My second example"
# O = object
class O: pass
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(E,D): pass
class A(B,C): pass
class ex_6(object):
"My first example"
# O = object
class O: pass
class B(O): pass
class A(B): pass
class D(B): pass
class N(O): pass
class M(N): pass
class Z(A,D,M): pass
class ex_9(object):
# O = object
class O: pass
class A(O): pass
class B(O): pass
class C(O): pass
class D(O): pass
class E(O): pass
class K1(A,B,C): pass
class K2(D,B,E): pass
class K3(D,A): pass
class Z(K1,K2,K3): pass
def merge(seqs):
print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
res = []; i=0
while 1:
nonemptyseqs=[seq for seq in seqs if seq]
if not nonemptyseqs: return res
i+=1; print '\n',i,'round: candidates...',
for seq in nonemptyseqs: # find merge candidates among seq heads
cand = seq[0]; print ' ',cand,
nothead=[s for s in nonemptyseqs if cand in s[1:]]
if nothead: cand=None #reject candidate
else: break
if not cand: raise "Inconsistent hierarchy"
res.append(cand)
for seq in nonemptyseqs: # remove cand
if seq[0] == cand: del seq[0]
def mro(C):
"Compute the class precedence list (mro) according to C3"
return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])
def print_mro(C):
print '\nMRO[%s]=%s' % (C,mro(C))
print_mro(ex_5.A)
print_mro(ex_6.Z)
print_mro(ex_9.Z)
ex_5.A举例:
dir()所做的不是查找一个对象的__dict__属性(这个属性有时甚至都不存在),它使用的是对象的继承关系来反馈一个对象的完整的有效属性。
class AA(object):
__slots__ = ('name', 'age')
name = 123
gender = 1259
class BB(AA):
__slots__ = ('height', 'weight')
height = 123
gender = 1259
class CC(AA):
height = 123
gender = 1259
AA中__slots__,name,gender都是只读的,age可写
但是任何对象的__dict__中竟然都找不到'age':25的键值对!?
前面一直说__getattribute__调用描述符的__get__方法。这里需要非常注意的是__get__方法来自哪里?
经过测试可知来自对象的类型类,也就是obj.__class__所指的对象中。如下代码所示:
'----测试get方法的来源---'
def __get__(*args,**kwargs):
print 'get in foo',args,kwargs
def foo(*args,**kwargs):
print 'foo',args,kwargs
class Non_DD(object):
'non-data descriptor'
def __init__(self,name):
self._name = name
def __get__(self, obj, cls=None):
print 'get in Non_DD, name:', self._name
class A(object):
def test(self):
def __get__(*args,**kwargs):
print 'get in test',args,kwargs
print 'test'
foo.__get__ = __get__
A.foo = foo
A.bar = Non_DD('bar in A')
a = A()
A.test(a)
A.foo(a) # 可以看到A对A中的foo和test的访问,都没有调用实例级别的__get__方法。
print A.bar # 可以看到A对A中的bar的访问,调用对象bar的类型级别的__get__方法,即Non_DD中的__get__方法。
a.test()
a.foo() # 可以看到a对A中的foo和test的访问,都没有调用实例级别的__get__方法。
print a.bar # 可以看到a对A中的bar的访问,调用对象bar的类型级别的__get__方法,即Non_DD中的__get__方法。
a.foo = foo
a.foo() # 可以看到a对a中的foo的访问,连foo类型级别的__get__方法都没有调用,即没有对foo进行封装。
a.bar = Non_DD('bar in a')
print a.bar # 可以看到a对a中的bar的访问,连bar类型级别的__get__方法都没有调用,即Non_DD中的__get__方法。
'----测试函数的get属性---'
def foo(*args,**kwargs):
def __get__(*args,**kwargs):
print 'get in foo',args,kwargs
print 'foo',args,kwargs
print foo.__dict__ # 并没有__get__方法
foo.__get__ = __get__
print foo.__dict__ # 有__get__方法
print A.test.__dict__ # 并没有__get__方法
A.test.__get__ = __get__
print A.test.__dict__ # 并没有__get__方法,因为每次都返回的是新的封装后的test对象
'----测试函数的object和type中的__getattribute__---'
A.bar = Non_DD('bar in A')
print A.bar
print type.__getattribute__(A,'bar') # 调用Non_DD的__get__方法
print object.__getattribute__(A,'bar') # 没有调用Non_DD的__get__方法,直接返回Non_DD对象
print object.__getattribute__(A,'bar').__get__(a)
http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html
(如果有什么说的不对的地方,欢迎大家多多批评指正。)