用编程的方式创建Class

在接下来的部分描述的东西,可用在任何 source-declared class,但是python采用的方式,提供了更有趣的一些特征。在这个场景的背后,class 声明的细节都给发送到内置的type对象中去,由它来为这个class创建一个合适的python对象。对每一个class来说,当它完成解析class 声明的内容之后,都是立即自动进行处理的。

type的构建,接受3个参数,代表了完整的class声明。

1)name -- 类名,字符串类型。
2)bases -- 一个元组,可能为空,类继承链上的类
3)attrs -- 类名字空间上的字典类型。

** 类定义的新方式和老方式 **

这 里描述的都是python新的类定义方式,是在python 2.2中引入的。老的类定义方式不再推荐使用,而且在python 3.0中完全取消。因此这个部分的内容集中讨论新的类定义方式。为了强制使用新的类定义方式,只要简单的确保类继承内置的object类型就可以。

django提供的所有类,都是从object派生而来。因此任何派生的类型都自动是新的类定义方式,不要你增加额外的处理。还有,记住这种不同是重要的,以便你自己程序的任何自定义类能更好地扩展。

****

像任何python对象一样,在任何时候,任何代码块中,一个新的type都能实例化。也就是说,你的代码能在运行时构建一个新类,只要满足已知的条件。下面的代码就举例说明了一个在运行时声明一个类,功能上相当于前面提供的例子里的类。

>>> DynamicClass = type('DynamicClass', (object,), {'spam': 'eggs'})
>>> DynamicClass
<class '__main__.DynamicClass'>
>>> DynamicClass.spam
'eggs'

** 关于type()的注意事项 **

手工使用type(),很容易创建带有重复的names的类出来,甚至模块的位置能被定制,这是在attrs参数中,使用字典中的一个__module__关键字来处理。尽管这些特性很有用,正如在本书后面演示的,但它们也会引发自省的问题。

你有可能有两个不同的类,但名字和模块相同,你的代码将不能区分它们之间的差别。在有些情况下这不是问题,但有时确是问题。

****

Metaclass 改变一切

type 实际上是一个"metaclass"(元类)-- 创建其他类的类 -- 我们一直提到的,称之为“metaprogramming”。从本质上,metaprogramming(元编程)是在运行时创建或改变代码,而不是在编 程时。通过允许一个类定义不同的metaclass(元类),python允许你来自由定制这个处理过程。

如果一个类定义包含一个独立的 类,它有__metaclass__属性,metaclass将会被调用来创建这个类,而不是使用内置的type对象了。这就允许你的代码能读取,改变甚 至完全替换声明的类,以便进一步定制它的功能。__metaclass__属性从技术上可以指定成任何有效的python callable,但是大多数metaclass类都是作为type的子类。metaclass接受第一个参数作为新类,并提供类的声明细节来存取类对 象。

为了有助于说明metaclass参数怎样从一个类定义中派生,看看下面的代码。

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print 'Defining %s' % cls
...         print 'Name: %s' % name
...         print 'Bases: %s' % (bases,)
...         print 'Attributes:'
...         for (name, value) in attrs.items():
...             print '    %s: %r' % (name, value)
...
>>> class RealClass(object):
...     __metaclass__ = MetaClass
...     spam = 'eggs'
...
Defining <class '__main__.RealClass'>
Name: RealClass
Bases: (<type 'object'>,)
Attributes:
    __module__: '__main__'
    __metaclass__: <class '__main__.MetaClass'>
    spam: 'eggs'
>>> RealClass
<class '__main__.RealClass'>

注意:这个类还没有实例化,创建类就触发了metaclass的执行。注意:属性列表中__module__,这个属性是所有python类的标准部分。

这个例子使用了__init__方法执行了一个特殊的处理,新创建类。还有另外一个,__new__,更有用的方法,可以处理更多不同的可能性。正如在后面的章节中介绍的,在配置许多类时,Django使用__new__。

使用带有metaclass的Base class

Metaclass 是相当有用的,但是 __metaclass__ 变量是一个实现细节,当定义类时,它不应该成为处理的一部分。另一个问题是当每一个类需要处理metaclass时,他们不从任何具体类继承。也就是说任 何附加的功能,比如通常的方法和属性,为了能使用起来,必须在metaclass处理过程中提供。

稍加注意一下,一个具体的python 类可以使用metaclass解决这两个问题。因为子类从它的父类那继承了属性,父类中定义的__metaclass__变量也会自动被所有的子类获得, 所以就不必要求每个类都要定义 __metaclass__ 属性。看一下前面的例子, 当我们派生一个ReadClass子类,会发生什么事。

>>> class SubClass(RealClass):
...     pass
...
Defining <class '__main__.SubClass'>
Name: SubClass
Bases: (<class '__main__.RealClass'>,)
Attributes:
    __module__: '__main__'
>>>

注意:这里的子类不用担心背后metaclass是怎样使用的。只要指定一个base class,它就继承了所有的东西。Django采用了这种机制,实现了下一部分要描述的最突出的一个特性。