Python中的元类编程

过去有这样的概念,一直没有深究它的意义.今天同事问到,刚好也好好了解下.
#===============================================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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值