我应该使用元类、类装饰器还是重写 __new__ 方法?

Posted

技术标签:

【中文标题】我应该使用元类、类装饰器还是重写 __new__ 方法?【英文标题】:Should I use a metaclass, class decorator, or override the __new__ method? 【发布时间】:2011-01-31 00:19:15 【问题描述】:

这是我的问题。我希望下面的类有一堆属性。我可以像foobar 一样将它们全部写出来,或者根据我见过的其他一些示例,看起来我可以使用类装饰器、元类或覆盖__new__ 方法来设置属性自动。我只是不确定“正确”的做法是什么。

class Test(object):
    def calculate_attr(self, attr):
        # do calculaty stuff
        return attr

    @property
    def foo(self):
        return self.calculate_attr('foo')

    @property
    def bar(self):
        return self.calculate_attr('bar')

【问题讨论】:

如有疑问,请始终使用简单明了的方法。如果不能轻易地弄清楚它,那么将被要求维护你的代码的傻瓜(比如我)也不能。 当您改变问题的性质时,通常最好只打开一个新问题。 【参考方案1】:

元类的__new__ 不会成为您创建的类的__new__——它用于创建类本身。 实际类对象由元类返回。 __new__ 返回一个类的新实例。

考虑以下(疯狂的)代码:

def MyMetaClass(name, bases, dict):
    print "name", name
    print "bases", bases
    print "dict", dict
    return 7

class C('hello', 'world'):
    __metaclass__ = MyMetaClass

    foo = "bar"

    def baz(self, qux):
        pass

print "C", C

(我使用函数而不是类作为元类。任何可调用对象都可以用作元类,但许多人选择将他们的类作为继承自 type 并覆盖新的类。两者之间的区别功能很微妙。)

输出

name C
bases ('hello', 'world')
dict 'baz': <function baz at 0x4034c844>, '__module__': '__main__', 'foo': 'bar', '__metaclass__': <function MyMetaClass at 0x40345c34>
C 7

这是否有助于您更好地理解元类是什么

您将很少需要定义自己的元类。

【讨论】:

不应该是“实际类对象”而不是“实际类对象”吗? 我真的很喜欢“类”C 现在是“7”...直观【参考方案2】:

魔法不好。它使您的代码更难理解和维护。您几乎不需要元类或__new__

看起来你的用例可以用非常简单的代码来实现(只有一点点魔法):

class Test(object):
    def calculate_attr(self, attr):
        return something

    def __getattr__(self, name):
        return self.calculate_attr(name)

【讨论】:

@你几乎不需要元类或__new__:我非常不同意。在我的公司,我们已经有过两次使用元类的动机。首先是为我们使用的自定义数据库创建抽象层,另一个是为 ldap 创建我们自己的模型系统。我真的不明白,这种对元类的怨恨从何而来。这是一个容易掌握的概念,在某些情况下非常有用。 @gruszczy,元类并不难理解,它们实际上是一个非常简单的想法,尽管它们在 Python 中的语法和用法让一些人感到麻烦。然而,由于它们的传播方式,它们很容易出错,并且有时难以组合——即使这是一个完全合理的操作,也不能自动让一个类在其构造中使用两个元类。元类通常不作为系统公共 API 的一部分公开,这一事实加剧了这个问题。【参考方案3】:

在创建新类而不是实例时使用元类。通过这种方式,您可以例如注册类(django 会这样做并使用它例如在数据库中创建表)。由于class 是一条指令,您可以将其视为类的装饰器。

【讨论】:

以上是关于我应该使用元类、类装饰器还是重写 __new__ 方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用两种不同的装饰器实现来装饰所有类方法的元类

在类中的所有实例方法自动调用之后调用方法:元类还是装饰器?

使用装饰器设置类的元类

Python之路:描述符,类装饰器,元类

__new__、__init__ 和元类(和超类)

类装饰器禁用 __init_subclass__