过去有这样的概念,一直没有深究它的意义.今天同事问到,刚好也好好了解下.
#===============================================Python中的元类编程===============================================
#淆元类和基类的不同。从“定义”类的表面上来区分,两者看上去是类似的。可一旦深入下去,两个概念就区别开来。
#在举例之前,明确一些术语是非常有意义的。
#实例 是由类“制造”的 Python 对象;类充当实例的一种模版。每一个实例只能属于一个类(但一个类可以有多个实例)。
#我门经常说的实例对象――或“简单实例”――是“最终的”,即意味着不能作为其他对象的模版(但它仍然可能是一个 工厂或 代表,这二者的目的有重叠)。
#某些实例对象就是类本身,并且所有的类都是相应 元类的实例,甚至类也只能通过实例化机制生成。
#通常,类是内建的标准的元类 type 的实例;只有当我们指定元类而不是指定 type 时,才需要考虑元类编程。我们也称用来实例化对象的类为那个对象的 类型。
#运行实例化想法的 正交是继承的概念。这里,一个类可以有一个或多个父亲,而不是只有一个惟一的类型。
#并且父亲还可以有父亲,创建一个传递的子类关系后,可以方便地利用内建的 issubclass() 函数来判断继承关系。
#例如,如果我们定义一些类和一个实例:
#代码清单 1. 典型的继承层次
class A(object):
a1 = 'A'
class B(object):
a2 = 'B'
class C(A, B):
a3 = 'C(AB)'
class D(C):
a4 = 'D(C)'
#然后我们可以测试这些关系:
d = D()
d.a5 = "instance d of D"
print d.a5
#输出结果:instance d of D
#代码清单 2. 测试祖先
print issubclass(D,C)
#输出结果:True
print issubclass(D,A)
#输出结果:True
print issubclass(A,B)
#输出结果:False
#print issubclass(d,D)
#输出结果:TypeError: issubclass() arg 1 must be a class
#现在,一个很有趣的问题——理解超类与元类的不同所必需的一个问题——是像 d.attr 这样的属性是如何被解析的。
#简单起见,我们只讨论标准的查找规则,而不是回到 .__getattr__() 。
#这个解决方案的第一步是在 d.__dict__ 中查找名字 attr ,如果找到了,那么就是它,但如果不是,一些有趣的事情将会发生,
#例如:
print d.__dict__
#输出结果:{'a5': 'instance d of D'}
print d.a5
#输出结果:instance d of D
print d.a1
#输出结果:A
#查找一个不与实例关联的属性时有一个技巧,即先在实例的类中查找,然后再在从所有的超类中查找。
#这一过程中父类被检查的顺序被称为这个类的 方法解析顺序,您可以利用以下这个(元)方法 .mro()
#看出该顺序 (但只能是从类对象中看到):
print [k.__name__ for k in d.__class__.mro()]
#输出结果:['D', 'C', 'A', 'B', 'object']
#换句话说,访问 d.attr 时首先是在 d.__dict__ 中,然后在 D.__dict__ , C.__dict__ , A.__dict__ , B.__dict__ ,
#最后才是在 object.__dict__ 中查找,如果在任意一个地方都未找到这个名字,就会产生一个 AttributeError 。
#注意,从未在查找过程中提到过元类。
print "----------------------------------------------------------------------------------------------------------------"
#===============================================元类与祖先===============================================
#这里有一个普通的继承的简单例子,我们定义了一个 Noble 基类,它有子类 Prince , Duke , Baron 等等。
#代码清单 3. 属性继承
for s in "Power Wealth Beauty".split(): exec '%s="%s"'%(s,s)
# ...in fairy tale world
class Noble(object):
attributes = Power, Wealth, Beauty
class Prince(Noble):
pass
print Prince.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
#类 Prince 继承了 Noble 的属性, Prince 类的实例仍然遵循上述的查找链。
#代码清单 4. 实例中的属性
charles=Prince()
print charles.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
print "----------------------------------------------------------------------------------------------------------------"
#如果 Duke 碰巧有一个定制元类,那么它能够以以下方式获得一些属性:
class Nobility(type):
attributes = Power, Wealth, Beauty
class Duke(object):
__metaclass__ = Nobility
#Duke是一个类,也是元类 Nobility 的一个实例——属性的查找过程与其他对象一致:
print Duke.attributes
#但是 Nobility 不是 Duke 的超类,所以这就是为什么 Duke 的 实例会找到 Nobility.attributes 的原因:
#代码清单 5. 属性与元类
print Duke.mro()
#输出结果:[<class '__main__.Duke'>, <type 'object'>]
earl = Duke()
#print earl.attributes
#输出结果:AttributeError: 'Duke' object has no attribute 'attributes'
#元类属性的可用性是不会传递的,也就是说,元类的属性是对它的实例是可用的,
#但是对它的实例的实例是不可用的。这正是元类与超类的主要不同。下图强调了继承与实例化的异同。
#图 1. 实例化与继承
# Nobility
# |
# |
# Noble----->Prince V
# | Duke
# | |
# V |
# charles V
# earl
#
#因为 earl 仍然有一个类,因而您可以间接地得到其属性:
print earl.__class__.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
#图1对比了只涉及继承或是元类的简单情况,而不是同时涉及的情况。但有些时候,一个类 C 同时有定制元类M和基类 B:
#代码清单 6. 结合元类与超类
class M(type):
a = 'M.a'
x = 'M.x'
class N(object):
a = 'N.a'
class P(N):
__metaclass__ = M
p=P()
#图 2. 超类与元类的结合
# M(M.a)
# |
# |
# V
# N(N.a)------->P(?)
# |
# |
# V
# p(?)
#
#根据前面的解释,我们能够想像 P.a 将解析成 M.a 或 N.a 。当解析发生时,在查找实例化元类之前,会先遵循它的 mro() 对类进行查找。
#代码清单 7. 解析元类与超类
print P.a,P.x
#输出结果:N.a M.x
print p.a
#输出结果:N.a
#print p.x
#输出结果:AttributeError: 'P' object has no attribute 'x'
#您仍然可以使用元类来增强属性,只需在被实例化的类对象上进行设置,而不是像元类的属性那样。
#清单 8. 在元类中设置属性
class M2(type):
def __init__(cls, *args):
cls.a = 'M2.a'
class P2(N):
__metaclass__= M2
print P2.a, P2().a
#输出结果:M2.a M2.a
#===============================================Python中的元类编程===============================================
#淆元类和基类的不同。从“定义”类的表面上来区分,两者看上去是类似的。可一旦深入下去,两个概念就区别开来。
#在举例之前,明确一些术语是非常有意义的。
#实例 是由类“制造”的 Python 对象;类充当实例的一种模版。每一个实例只能属于一个类(但一个类可以有多个实例)。
#我门经常说的实例对象――或“简单实例”――是“最终的”,即意味着不能作为其他对象的模版(但它仍然可能是一个 工厂或 代表,这二者的目的有重叠)。
#某些实例对象就是类本身,并且所有的类都是相应 元类的实例,甚至类也只能通过实例化机制生成。
#通常,类是内建的标准的元类 type 的实例;只有当我们指定元类而不是指定 type 时,才需要考虑元类编程。我们也称用来实例化对象的类为那个对象的 类型。
#运行实例化想法的 正交是继承的概念。这里,一个类可以有一个或多个父亲,而不是只有一个惟一的类型。
#并且父亲还可以有父亲,创建一个传递的子类关系后,可以方便地利用内建的 issubclass() 函数来判断继承关系。
#例如,如果我们定义一些类和一个实例:
#代码清单 1. 典型的继承层次
class A(object):
a1 = 'A'
class B(object):
a2 = 'B'
class C(A, B):
a3 = 'C(AB)'
class D(C):
a4 = 'D(C)'
#然后我们可以测试这些关系:
d = D()
d.a5 = "instance d of D"
print d.a5
#输出结果:instance d of D
#代码清单 2. 测试祖先
print issubclass(D,C)
#输出结果:True
print issubclass(D,A)
#输出结果:True
print issubclass(A,B)
#输出结果:False
#print issubclass(d,D)
#输出结果:TypeError: issubclass() arg 1 must be a class
#现在,一个很有趣的问题——理解超类与元类的不同所必需的一个问题——是像 d.attr 这样的属性是如何被解析的。
#简单起见,我们只讨论标准的查找规则,而不是回到 .__getattr__() 。
#这个解决方案的第一步是在 d.__dict__ 中查找名字 attr ,如果找到了,那么就是它,但如果不是,一些有趣的事情将会发生,
#例如:
print d.__dict__
#输出结果:{'a5': 'instance d of D'}
print d.a5
#输出结果:instance d of D
print d.a1
#输出结果:A
#查找一个不与实例关联的属性时有一个技巧,即先在实例的类中查找,然后再在从所有的超类中查找。
#这一过程中父类被检查的顺序被称为这个类的 方法解析顺序,您可以利用以下这个(元)方法 .mro()
#看出该顺序 (但只能是从类对象中看到):
print [k.__name__ for k in d.__class__.mro()]
#输出结果:['D', 'C', 'A', 'B', 'object']
#换句话说,访问 d.attr 时首先是在 d.__dict__ 中,然后在 D.__dict__ , C.__dict__ , A.__dict__ , B.__dict__ ,
#最后才是在 object.__dict__ 中查找,如果在任意一个地方都未找到这个名字,就会产生一个 AttributeError 。
#注意,从未在查找过程中提到过元类。
print "----------------------------------------------------------------------------------------------------------------"
#===============================================元类与祖先===============================================
#这里有一个普通的继承的简单例子,我们定义了一个 Noble 基类,它有子类 Prince , Duke , Baron 等等。
#代码清单 3. 属性继承
for s in "Power Wealth Beauty".split(): exec '%s="%s"'%(s,s)
# ...in fairy tale world
class Noble(object):
attributes = Power, Wealth, Beauty
class Prince(Noble):
pass
print Prince.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
#类 Prince 继承了 Noble 的属性, Prince 类的实例仍然遵循上述的查找链。
#代码清单 4. 实例中的属性
charles=Prince()
print charles.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
print "----------------------------------------------------------------------------------------------------------------"
#如果 Duke 碰巧有一个定制元类,那么它能够以以下方式获得一些属性:
class Nobility(type):
attributes = Power, Wealth, Beauty
class Duke(object):
__metaclass__ = Nobility
#Duke是一个类,也是元类 Nobility 的一个实例——属性的查找过程与其他对象一致:
print Duke.attributes
#但是 Nobility 不是 Duke 的超类,所以这就是为什么 Duke 的 实例会找到 Nobility.attributes 的原因:
#代码清单 5. 属性与元类
print Duke.mro()
#输出结果:[<class '__main__.Duke'>, <type 'object'>]
earl = Duke()
#print earl.attributes
#输出结果:AttributeError: 'Duke' object has no attribute 'attributes'
#元类属性的可用性是不会传递的,也就是说,元类的属性是对它的实例是可用的,
#但是对它的实例的实例是不可用的。这正是元类与超类的主要不同。下图强调了继承与实例化的异同。
#图 1. 实例化与继承
# Nobility
# |
# |
# Noble----->Prince V
# | Duke
# | |
# V |
# charles V
# earl
#
#因为 earl 仍然有一个类,因而您可以间接地得到其属性:
print earl.__class__.attributes
#输出结果:('Power', 'Wealth', 'Beauty')
#图1对比了只涉及继承或是元类的简单情况,而不是同时涉及的情况。但有些时候,一个类 C 同时有定制元类M和基类 B:
#代码清单 6. 结合元类与超类
class M(type):
a = 'M.a'
x = 'M.x'
class N(object):
a = 'N.a'
class P(N):
__metaclass__ = M
p=P()
#图 2. 超类与元类的结合
# M(M.a)
# |
# |
# V
# N(N.a)------->P(?)
# |
# |
# V
# p(?)
#
#根据前面的解释,我们能够想像 P.a 将解析成 M.a 或 N.a 。当解析发生时,在查找实例化元类之前,会先遵循它的 mro() 对类进行查找。
#代码清单 7. 解析元类与超类
print P.a,P.x
#输出结果:N.a M.x
print p.a
#输出结果:N.a
#print p.x
#输出结果:AttributeError: 'P' object has no attribute 'x'
#您仍然可以使用元类来增强属性,只需在被实例化的类对象上进行设置,而不是像元类的属性那样。
#清单 8. 在元类中设置属性
class M2(type):
def __init__(cls, *args):
cls.a = 'M2.a'
class P2(N):
__metaclass__= M2
print P2.a, P2().a
#输出结果:M2.a M2.a