# -*- coding: utf-8 -*-
# 首发在 http://page.renren.com/601001752/note/775449636
'''
PyPy中的extendabletype与pairtype解析
玩过C#的同学都知道,C#中有一个partial关键字,可以将一个类的实现分在多个文件中,最后由编译器组合起来。这样可以实现多人协作,分离手写和自动生成的代码。
可能有人会抱怨,为什么Python没有提供这种功能呢?其实是提供了的,只不过比较隐蔽而已——这种问题可以使用元类(metaclass)来解决~
什么是元类呢?用一句话来说,元类就是类的类,一般的类是元类的实例。Python下可以使用type(obj)来获得一个变量的类型。type(一个类的实例)会返回那个类,type(一个类)则会返回这个类的元类。在不指明元类的情况下,旧版类的元类是classobj,新版类的元类就是type。这里有一个很有趣的事实:type的元类还是type,type的基类是object,object的元类是type……
说了这么多不相关的,到底PyPy的代码是怎样实现partial class的呢? 请看pypy/tool/pairtype.py:
'''
class extendabletype(type):
"""A type with a syntax trick: 'class __extend__(t)' actually extends
the definition of 't' instead of creating a new subclass."""
def __new__(cls, name, bases, dict): #当创建新类的时候,调用这个函数。此时cls相当与self,不过指的是当前的元类;name是新类的名字;bases是新类的基类;dict是新类的内容,也就是class定义后的那些东西,函数,类变量等等。
if name == '__extend__':
for cls in bases:
for key, value in dict.items():
if key == '__module__':
continue
# XXX do we need to provide something more for pickling?
setattr(cls, key, value)
return None
else:
return super(extendabletype, cls).__new__(cls, name, bases, dict) # 相当于type.__new__(xxxx)
'''
可以看出,这个元类实现的功能很简单:如果新类的名字是'__extend__',那么不创建新类,而是把新类的dict合并到基类中,这样就相当与扩充了基类!
举个例子:
'''
class MyClass(object):
__metaclass__ = extendabletype
def myprint1():
print "Proton rocks!"
class __extend__(MyClass):
def myprint2():
print "Proton really rocks!"
#与
class MyClass(object):
def myprint1():
print "Proton rocks!"
def myprint2():
print "Proton really rocks!"
'''
是完全一样的!
知道了这些,就留个小的练习吧: C#中的sealed关键字(不能被继承)在Python中怎么实现呢?只要发现违规用法报错就可以。
pairtype则是一个更有趣的东西:它的目的是将两个类组合在一起,来作为一个新的类。这么说很容易让人产生误解,还是看源码吧~
'''
def pair(a, b):
"""Return a pair object."""
tp = pairtype(a.__class__, b.__class__) #根据a和b的类找到相应的pairtype
return tp((a, b)) # tp is a subclass of tuple, 根据a和b创建pairtype的实例
pairtypecache = {}
def pairtype(cls1, cls2):
"""type(pair(a,b)) is pairtype(a.__class__, b.__class__)."""
try:
pair = pairtypecache[cls1, cls2] # 如果pairtype已经存在,就直接返回
except KeyError: # 如果不存在,就创建一个相应的类
name = 'pairtype(%s, %s)' % (cls1.__name__, cls2.__name__)
bases1 = [pairtype(base1, cls2) for base1 in cls1.__bases__] # 寻找所有的基类,比如pairtype(Human, Girl) 和 pairtype(Boy, Human)就应该是pairtype(Boy, Girl)的基类
bases2 = [pairtype(cls1, base2) for base2 in cls2.__bases__]
bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base
pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {}) # <- 注意是extendabletype,不是type哦
return pair
'''
恩 这个东西有什么好玩的呢? 在这里举个例子:
'''
class Human(object):
def __init__(self, name):
self.name = name
class Boy(Human): pass
class Girl(Human): pass
class __extend__(pairtype(Human, Human)):
def talk((a, b)): # 注意pairtype的基类是tuple, 所以可以直接这样写
print "%s and %s are talking." % (a.name, b.name)
class __extend__(pairtype(Boy, Boy)):
def duel((a, b)):
print "%s and %s try to prove they are real men, decided to duel." % (a.name, b.name)
class __extend__(pairtype(Girl, Girl)):
def unlimited_talk((a, b)):
print "Girls are really scary, %s and %s kept talking for hours without drinking a single gulp of water." % (a.name, b.name)
class __extend__(pairtype(Boy, Girl), pairtype(Girl, Boy)):
def makelove((a, b)):
print "%s and %s are... hey you knew it!" % (a.name, b.name)
reimu = Girl("Reimu")
marisa = Girl("Marisa")
moku = Boy("Moku")
rinnosuke = Boy("Rinnosuke")
cp1 = pair(reimu, marisa)
cp1.talk()
cp1.unlimited_talk()
cp2 = pair(moku, rinnosuke)
cp2.talk()
cp2.duel()
cp3 = pair(rinnosuke, reimu)
cp3.talk()
cp3.makelove()