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

6

这是我的问题。我想让以下类具有一堆属性值。我可以像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')

2
如果你不确定,总是选择简单明了的方式。如果自己不能轻松地理解它,那么像我这样的蠢货也无法维护你的代码。 - S.Lott
当你改变问题的本质时,最好的方法通常是打开一个新的问题。 - Mike Graham
3个回答

5

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

看起来您的用例可以使用相当简单的代码实现(只有少量魔法提示):

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

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

2
你几乎从不需要元类或__new__:我强烈反对这种说法。在我们公司,我们已经有两个场合使用元类是非常有动机的。第一个是为我们使用的自定义数据库创建抽象层,另一个是为ldap创建我们自己的模型系统。我真的无法理解为什么会有这种对元类的厌恶情绪。这是一个容易理解且在某些情况下非常有用的概念。 - gruszczy
2
@gruszczy,元类并不难理解,实际上它们是一个相当简单的概念,但是在Python中的语法和用法使一些人感到困惑。然而,由于它们的传播方式和有时难以组合,它们往往容易出错——即使这是一个完全合理的操作,也不能自动地让一个类使用两个元类来构建。这个问题加剧了元类通常不作为系统公共API的一部分的事实。 - Mike Graham

3
一个元类的 __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并重载其__new__方法的类。这与函数之间的区别微妙。

它输出:

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

这有助于你更好地理解元类的概念吗?

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


这句话应该是“实际的类对象”,而不是“实际上的类对象”,对吗? - Faheem Mitha
我真的很喜欢“类”C现在变成了“7”...直观 - Erik Aronesty

1

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接