2010-01-05 43 views
30

不能以任何其他方式使用元类可以做些什么?什么是Python元类有用吗?

亚历克斯马尔泰利告诉说,有任务不能在这里没有元类实现Python metaclasses vs class decorators 我想知道哪些是?

+7

我记得我读过类似这样的关于元类的地方,松散地引用了这句话:“如果你要问什么是它们有用的,那意味着你不需要它们”。 +1,有趣的问题。 – 2010-01-05 12:16:52

+3

我不确定“不能以其他方式完成”是一个有用的标准。几乎所有事情都可以用许多不同的方式完成,这都是一个多么简单/自然的问题。 – 2010-01-05 12:23:47

+0

应该有一个 - 最好只有一个 - 明显的方法来做到这一点。 :) – 2010-01-05 12:30:01

回答

18

元类是必不可少的,如果你想拥有类对象(而不是实例类对象的)配备了“特别定制的行为”,因为对象的行为取决于对类型的对象特殊的方法,而类对象的类型就是元类的同义词。例如,如果您想要一个类对象X,使得“print X”发出“Time is now 8:46 am”(上午8:46,或者更一般地说,当前时间),则这必定表示type(x) (AKA X的元类)有一个特殊的自定义方法 - 类似地(使用各种适用的特殊方法),如果要给诸如X + Y(其中X和Y都是类对象)或X[23](其中X,再次,是一个类对象),等等。

大多数其他自定义任务现在(在Python 2.6或更高版本中)更易于使用类装饰器实现,该装饰器可以在class语句结束后立即更改类对象。还有一些情况是不可行的,因为如果它们有任何作用(例如,设定或改变__slots__),必须尽早进行改变。

在Python 3中,元类获得了一点额外的实用性:元类现在可以选择指定要在执行class语句正文期间填充的映射对象(默认情况下,它是正常的dict)。这允许保留和使用类体中名称绑定的订单(而正常的dict失去了订单),当类必须具有特定顺序的“字段”时(例如映射1: 1到C struct,CSV文件或数据库表中的一行等) - 在Python 2中。*必须重复指定(通常带有额外的类属性,这是一个序列,因此保持顺序),而Python 3元类的这一特性允许删除冗余。

10

添加额外的灵活性,以你的编程:

但根据本Metaclass programming in Python你可能并不需要它们(还)

元类魔法更深比99%的用户应该永远不用担心。如果你想知道你是否需要他们,你就不会(实际需要他们的人确切地知道他们需要他们,并且不需要为什么解释)。

- Python高手蒂姆·彼得斯

+15

我还想知道。 – 2010-01-05 12:24:42

+0

思考面向方面的编程。我发布的链接包含一个全面的解释和代码示例。 – OscarRyz 2010-01-05 12:38:21

+0

我在发布问题之前阅读它,并且只有1个示例。 – 2010-01-05 13:30:01

1

他们很少用到,但派上用场当你想添加行为对象的基本行为的地方 - 比较面向方面编程,或者像Hibernate这样的持久化框架中完成的工具。

例如,您可能需要一个持久存储或记录每个新对象的类。

+2

这不能用装饰器完成吗? – 2010-01-05 12:22:32

4

如果您正在寻找使用元类机制的示例,您可以阅读django.forms的源代码。

表单定义的声明式样式是通过元类实现的。

1

也许没有什么东西可以完全用元类来完成,但对于某些人(包括我的),这是一个您可以使用的有趣工具。只是要小心不要滥用,因为它可能会很棘手。

例如,我在最近的一个项目中使用了元编程。这是一张OpenOffice计算表,它使用一些pyUNO宏来生成一些带有信息的文件。有一张表格向用户展示要填充的信息,其他表格可以用来描述元素的种类及其属性。然后用户可以选择元素的数量和类型,并生成文件。 该宏将通过元编程创建一个类,在每个工作表上进行配置。然后,用户可以实例化每个类并生成对象。

它可以在没有元编程的情况下完成,但对我来说,使用元编程功能来做它似乎很自然。

1

看看Django的源代码 - 例如元类用于生成模型。

http://code.djangoproject.com/wiki/DynamicModels

内部,Django使用元类基于一个类你 在你的源代码提供 创建模型。如果没有 进入太多细节,这 意味着,而不是你的类 是实际的模型,Django的 收到你的类, 它用来建立在其 代替模型的描述。

8

我使用具有某种频率的元类,它们是工具箱中非常强大的工具。有时候,对于问题的解决方案可以更优雅,更少代码,而不是没有代码。

我发现自己最经常使用元类的事情是在类创建期间后处理类属性。例如,设置在对象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() 

因此减少重复(和变量名和分配的名称可能会走出不同步的机会)。

0

第一评论员指出,使用metaclasses here是'宝石',帮助他追踪(多)'天'发生的意外错误。