在django 0.96,CharField的的最大长度使用maxlength 在svn的代码中,这个已经修改为max_length(这样,更符合python一般的命名规范,当然,事实上python并没有像ruby一样对命名规范有严格的要求,比方说unittest,wxpython就使用两个不同的命名规范),但实际上,原先的代码并不会出错,django是如何达成的呢
这里,django 使用了metaclass的功能
1. 首先,查看Field对象的定义,你会发现 class Field(object): __metaclass__ = LegacyMaxlength
LegacyMaxLength定义在django.utils.maxlength 模块中,这个metaclass非常简单 class LegacyMaxlength(type): def __init__(cls, name, bases, attrs):
super(LegacyMaxlength, cls).__init__(name, bases, attrs) #对构造函数进行修饰 cls.__init__ = remove_maxlength(cls.__init__) #增加maxlength属性 cls.maxlength = property(get_maxlength, set_maxlength)
remove_maxlength巧妙的运用了decorate ,对class 构造进行进行处理,流程很简单,就是从参数中取max_length,maxlength,如果有maxlength,则给你个已过期的警告,如果你同时设置了max_length和maxlength,则给你来个错误(因为 django这样无法判断你用 那个),然后将maxlenth设置给max_length,并创建maxlength属性
这样Field class的代码本身就非常干净,并没有为保持兼容性而同时存在maxlength和max_length两个属性. (虽然实际生成的代码还是有两个的,一个是max_length field还有一个是通过metaclass生成的maxlength property)
这里django 巧妙的利用metaclass实现deprecated 功能的.
在django的代码中,对于类似dsl的功能,django主要利用metaclass和descriptor来达成的,比方说model ,django构造了一个ModelBase metaclass,然后让Model使用这个元类,像对于关系处理, ForeignKey最终是将使用ReverseSingleRelatedObjectDescriptor 这样的 descriptor对象,也就是说,当你遇到 class Customer(models.Model): shipping_address=models.ForeignKey(Address)
最后,这个类被解释执行后的shipping_address实际上是一个叫shipping_address的ReverseSingleRelatedObjectDescriptor 的实例.
descriptor和metaclass的基本知识,我推荐参阅这这几个链接 How-To Guide for Descriptors http://users.rcn.com/python/download/Descriptor.htm#static-methods-and-class-methods 我强烈推荐这个文档, 看后你会知道descriptor 包括property,staticmethod,classmethod到底是如何运作的,也会理解bound(bind)在动态语言中的作用. Python 中的元类编程 -将面向对象编程推向新的高度 http://www.ibm.com/developerworks/cn/linux/l-pymeta/index.html Python 中的元类编程,第 2 部分- 理解继承的奥秘和实例创建 http://www.ibm.com/developerworks/cn/linux/l-pymeta2/index.html
|