动态添加带有或不带有元类的方法

2024-02-12

更新 - 2012/12/13

只是为了澄清 - 我对如何向类添加方法不太感兴趣 - 正如您在下面我的问题和人们的回答中看到的那样,有不止一种方法可以做到这一点(开玩笑和帽子提示我的 Perl 自我)。

我感兴趣的是了解使用不同方法向类添加方法的根本区别是什么,而最大的问题实际上是为什么我需要使用元类。例如Python 元类编程入门 http://onlamp.com/pub/a/python/2003/04/17/metaclasses.html指出:

也许元类最常见的用途是:为生成的类中定义的方法添加、删除、重命名或替换方法。

由于有更多方法可以做到这一点,我很困惑并寻求解释。

Thanks!

原文 - 2012/12/12

我需要动态地将方法添加到类(以及基于该类的新生成的类)。我想出了两种方法,一种涉及元类,另一种不涉及元类。除了后者不涉及“黑魔法”元类这一事实之外,我看不出这两种方法给我带来了什么区别;)

方法#1 with元类:

class Meta(type):
        def __init__(cls, *args, **kwargs):
                setattr(cls, "foo", lambda self: "foo@%s(class %s)" % (self,
                        cls.__name__))

class Y(object):
        __metaclass__ = Meta

y = Y()
y.foo() # Gives 'foo@<__main__.Y object at 0x10e4afd10>(class Y)'

方法#2 without元类:

class Z(object):
        def __init__(self):
                setattr(self.__class__, "foo",
                        lambda self: "foo@%s(class %s)" %
                        (self, self.__class__.__name__))

z = Z()
z.foo() # Gives 'foo@<__main__.Z object at 0x10c865dd0>(class Z)'

据我所知,这两种方法都给了我相同的结果和“表现力”。即使当我尝试使用创建新类时type("NewClassY", (Y, ), {}) or type("NewClassZ", (Z, ), {})我得到了相同的预期结果,这两种方法之间没有区别。

所以,我想知道这些方法是否真的存在任何“潜在”差异,或者如果我使用其中任何一个,是否有什么东西会在以后“咬”我#1 or #2或者它只是一个语法糖?

PS: 是的,我确实阅读了这里讨论 Python 和 pythonic 数据模型中的元类的其他线程。


使用元类的明显原因是,一旦类已知,它们就会真正提供有关该类的元数据,而与对象是否存在无关。微不足道,对吧?好吧,让我们展示一下我在您的原始版本上执行的一些命令Z and Y类看看这意味着什么:

In [287]: hasattr(Y,'foo')
Out[287]: True

In [288]: hasattr(Z,'foo')
Out[288]: False

In [289]: Y.__dict__
Out[289]:
<dictproxy {..., 'foo': <function __main__.<lambda>>}>

In [290]: Z.__dict__
Out[290]:
<dictproxy {...}>

In [291]: z= Z()

In [292]: hasattr(Z,'foo')
Out[292]: True

In [293]: Z.__dict__
Out[293]:
<dictproxy {..., 'foo': <function __main__.<lambda>>}>

In [294]: y = Y()

In [295]: hasattr(Y,'foo')
Out[295]: True

In [296]: Y.__dict__
Out[296]:
<dictproxy {..., 'foo': <function __main__.<lambda>>}>

正如您所看到的,第二个版本实际上在声明 Z 类后显着​​改变了它,这是您经常希望避免的。对于Python来说,对“类型”(类对象)进行操作当然不是一个不常见的操作,并且您可能希望它们尽可能一致,尤其是在这种情况下(该方法在运行时并不是真正动态的,只是在声明时)时间)。

我首先想到的一个应用程序是文档。如果您添加文档字符串foo使用元类,文档可能会通过__init__方法这是非常不可能的。

它还可能导致难以发现的错误。考虑一段使用类元信息的代码。可能在 99.99% 的情况下,这是在实例之后执行的Z已经创建了,但是 0.01% 可能会导致奇怪的行为,甚至崩溃。

在层次结构链中它也可能会变得棘手,你必须小心在哪里调用父级的构造函数。例如,像这样的类可能会出现问题:

class Zd(Z):
    def __init__(self):
        self.foo()
        Z.__init__(self)
a = Zd()
...
AttributeError: 'Zd' object has no attribute 'foo'

虽然这工作正常:

class Yd(Y):
    def __init__(self):
        self.foo()
        Y.__init__(self)
a = Yd()

在没有相关信息的情况下调用方法可能看起来很愚蠢__init__正在使用,但当您不小心时,可能会发生这种情况,尤其是在 MRO 并不立即明显的更复杂的层次结构中。而且很难发现这个错误,因为大多数情况下 Zd() 就会成功Z.__init__之前在某个地方被调用过。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态添加带有或不带有元类的方法 的相关文章

随机推荐