元类(Metaclass)

Metaclass

面向对象编程语言中,元类是指实例为类的类。定义确定的类及其实例的行为。并非所有面向对象的语言都支持元类。每一种语言都拥有他自己的元对象原型,为一系列描述对象、类、元类之间如何相互作用的规则。

Python example

Python里,内置的类type是一个元类。考虑这样一个简单的Python类:

1
2
3
4
5
6
7
8
9
10
11
class Car(object):
def __init__(self, make, model, year, color):
self.make = make
self.model = model
self.year = year
self.color = color

@property
def description(self):
""" Return a description of this car. """
return "%s %s %s %s" % (self.color, self.year, self.make, self.model)

在程序运行时,Car本身是类type的一个实例,类Car的源代码如上所示,有很多内容没有直接呈现出来,例如Car对象的大小、在内存中的二进制布局、它们是如何被分配内存的、每当一个Car实例被创建时初始化__init__方法会被自动调用等等。这些细节不仅在一个新的Car实例被创建时,还会在每次访问car的属性时起作用。没有元类的语言,这些细节是被语言本身所确定的,且无法被重载。在Python中,元类type控制了这些细节是实现的方式。具体的控制方式可以用另一个元类代替type来改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
class AttributeInitType(type):
def __call__(self, *args, **kwargs):
""" Create a new instance. """

# First, create the object in the normal default way.
obj = type.__call__(self, *args)

# Additionally, set attributes on the new object.
for name, value in kwargs.items():
setattr(obj, name, value)

# Return the new object.
return obj

这个元类只重载了类的创建方式,类的其他方面及对象的行为仍然由type进行处理。 现在类Car可以使用新定义的元类重写。在Python2中通过在类定义中赋值给__metaclass__来实现:

1
2
3
4
5
6
7
class Car(object):
__metaclass__ = AttributeInitType

@property
def description(self):
""" Return a description of this car. """
return " ".join(str(getattr(self, attr, "Unknown")) for attr in self.__dict__)

Python3中需要提供一个命名参数:

1
2
3
4
5
class Car(object, mateclass=AttributeInitType):
@property
def description(self):
""" Return a description of this car. """
return " ".join(str(value) for value in self.__dict__.values())

Car的实例于是可以像下面这样实例化:

1
2
new_car = Car(make='Toyota', model='Prius', year=2005, color='Green')
old_car = Car(make='Ford', model='Prefect', year=1979)

关键字参数实例化的效果也可以不用元类实现:

1
2
3
4
5
6
7
class Car(object):
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
@property
def description(self):
return " ".join(str(value) for value in self.__dict__.values())