不能以任何其他方式使用的元类可以做什么?
Alex Martelli 告诉过这里有些任务没有元类就无法实现Python 元类与类装饰器 我想知道哪些是?
不能以任何其他方式使用的元类可以做什么?
Alex Martelli 告诉过这里有些任务没有元类就无法实现Python 元类与类装饰器 我想知道哪些是?
如果你想让类对象(而不是类对象的实例)配备“特殊定制行为”,元类是必不可少的,因为对象的行为取决于对象类型的特殊方法,而类对象的类型正是元类的同义词。
例如,如果您想要一个类对象 X 使得“print X”发出“Time is now 8:46am”(上午 8:46,或者更一般地说,当前时间),这必须意味着type(x)
(AKA X 的元类)有一个特殊的自定义__str__
方法——如果你想给表达式赋予意义,比如X + Y
X 和 Y 都是类对象,或者X[23]
(X 又是一个类对象),类似地(使用各种适用的特殊方法),等等。
大多数其他自定义任务现在(在 Python 2.6 或更高版本中)使用类装饰器更容易实现,它可以在class
语句结束后立即更改类对象。还有一些情况下这是不可行的,因为如果要产生任何效果(例如,设置或更改__slots__
),必须尽早进行更改。
在 Python 3 中,元类获得了一点额外的用处:元类现在可以选择指定在class
语句主体执行期间要填充的映射对象(默认情况下,它是一个 normal dict
)。这允许保留和使用类主体中名称绑定的顺序(而正常的dict
顺序丢失),当类必须具有特定特定顺序的“字段”时(例如,将 1:1 映射到C struct
,CSV 文件或 DB 表中的一行等)——在 Python 2.* 中,这必须被冗余指定(通常带有一个额外的类属性,它是一个序列,因此确实保留了顺序),并且这个特性Python 3 元类允许删除冗余。
为您的编程增加额外的灵活性:
但是根据Python 中的这个 Metaclass 编程,您可能不需要它们(还)
元类是比 99% 的用户所担心的更深层次的魔法。如果你想知道你是否需要它们,你就不需要(真正需要它们的人肯定知道他们需要它们,并且不需要解释为什么)。
——Python 大师蒂姆·彼得斯
我经常使用元类,它们是工具箱中非常强大的工具。有时你的问题解决方案可以更优雅,更少的代码,有他们比没有。
我发现自己最常使用元类的事情是在类创建期间对类属性进行后处理。例如,name
在适当的地方设置对象的属性(例如 Django ORM 的工作方式):
class AutonamingType(type):
def __init__(cls, name, bases, attrs):
for k,v in attrs.iteritems():
if getattr(v, '__autoname__', False):
v.name = k
class Autonamer(object):
__metaclass__ = AutonamingType
如果您将此作为工具,并且您正在使用一个必须先知道它的name
类do_something()
:
class Foo(object):
__autoname__ = True
def __init__(self, name=None):
self.name = name
def do_something(self):
if self.name is None:
raise ValueError('name is None')
# now, do something
它可以使您的其余代码在这之间有所不同:
class Bar(object):
myfoo1 = Foo('myfoo1')
myfoo2 = Foo('myfoo2')
myfoo3 = Foo('myfoo3')
还有这个:
class Baaz(Autonamer):
myfoo1 = Foo()
myfoo2 = Foo()
myfoo3 = Foo()
从而减少重复(以及变量名称和分配名称可能不同步的可能性)。
如果您正在寻找使用元类机制的示例,您可以阅读django.forms的源代码。
表单定义的声明式风格是通过元类实现的。
它们很少需要,但在您希望将行为添加到对象的基本行为的地方很有用——比较面向方面的编程,或在 Hibernate 等持久性框架中完成的检测。
例如,您可能需要一个持久化或记录每个新对象的类。
可能没有什么可以完全使用元类来完成,但对于某些人(包括我的)来说,这是一个可以使用的有趣工具。请注意不要滥用,因为这可能很棘手。
例如,我在最近的一个项目中使用了元编程。这是一个 OpenOffice 计算表,它使用一些 pyUNO 宏生成一些包含信息的文件。有一张表格向用户显示要填写的信息,其他表格可用于描述元素的种类及其属性。然后,用户可以选择元素的数量和每个元素的类型,并生成文件。该宏将按照每张纸上的配置通过元编程创建一个类。然后,用户可以实例化每个类并生成对象。
它可以在没有元编程的情况下完成,但对我来说,使用元编程功能来完成它似乎很自然。
看看 Django 源代码——例如元类用于生成模型。
http://code.djangoproject.com/wiki/DynamicModels
在内部,Django 使用元类基于您在源代码中提供的类创建模型。不涉及太多细节,这意味着 Django 接收的不是你的类是实际模型,而是你的类的描述,它用它来创建一个模型来代替它。
The first commentator states that the use of metaclasses here was 'a gemstone' that helped him track down unexpected errors that had been occurring for (many) 'days'.