动态添加包含或不包含元类的方法

2024-04-27 14:59:55 发布

您现在位置:Python中文网/ 问答频道 /正文

更新-2012/12/13

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

我感兴趣的是学习使用不同方法向类添加方法的根本区别,而真正的大问题是为什么我需要使用元类。例如,A Primer on Python Metaclass Programming声明:

Perhaps the most common use of metaclasses [...]: adding, deleting, renaming, or substituting methods for those defined in the produced class.

既然有更多的方法可以做到这一点,我很困惑,正在寻找解释。在

谢谢!在

原件-2012/12/12

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

使用元类的方法

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)'

无元类的方法:

^{pr2}$

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

所以,我想知道这些方法是否真的有什么“潜在的”差异,或者如果我使用<1或者仅仅是一种语法甜点,是否有什么东西会“咬”我?在

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


Tags: the方法selfobjectfooontype感兴趣
2条回答

如果问题是动态地添加方法,python确实以一种非常简单的方式处理它。具体如下:

#!/usr/bin/python
class Alpha():

    def __init__(self):
        self.a = 10
        self.b = 100

alpha = Alpha()
print alpha.a
print alpha.b

def foo(self):
    print self.a * self.b * self.c

Alpha.c = 1000
Alpha.d = foo

beta = Alpha()
beta.d()

输出:

^{pr2}$

问候!在

注:我看不出这里有什么类似的魔法(:

编辑:

考虑到martineau的评论,我添加了data attributes,而不是{}。我真的看不出做Alpha.d = fooAlpha.d = lambda self: foo(self)之间的区别,除了我将使用lambda函数作为添加的函数foo的包装。在

方法的添加是相同的,python本身将这两个添加命名为相同的:

#!/usr/bin/python
class Alpha():

    def __init__(self):
        self.a = 10
        self.b = 100

alpha = Alpha()
print alpha.a
print alpha.b

def foo(self):
    print self.a * self.b * self.c

Alpha.c = 1000

Alpha.d = lambda self: foo(self)
Alpha.e = foo

print Alpha.d
print Alpha.e

a = Alpha()
a.d()
a.e()

输出:

10
100
<unbound method Alpha.<lambda>>
<unbound method Alpha.foo>
1000000
1000000

如图所示,python本身将这两个结果相加命名为方法,唯一的区别是一个是对函数foo的引用,另一个是对在其定义体中使用函数foo的lambda函数的引用。在

如果我说错了什么,请纠正我。在

问候!在

使用元类的明显原因是,只要知道类,它们就真正提供关于类的元数据,与对象是否存在无关。微不足道,对吧?好吧,让我们展示一下我在原始的ZY类上执行的一些命令,看看这意味着什么:

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来说,对“类型”(类对象)执行操作当然不是一种罕见的操作,而且您可能希望它们尽可能保持一致,当然对于这种情况(方法在运行时不是真正动态的,只是在声明时)。在

想到的一个应用程序是documination。如果使用元类向foo添加docstring,文档可能会通过__init__方法获取它,这是非常不可能的。在

这也可能导致难以发现的漏洞。考虑一段使用类的元信息的代码。在99.99%的情况下,这可能是在已经创建了Z的实例之后执行的,但是0.01%的情况可能会导致奇怪的行为,如果不是崩溃的话。在

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

^{pr2}$

虽然这很好:

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

在没有使用相关的__init__的情况下调用一个方法可能看起来很愚蠢,但这可能发生在不小心的情况下,当然,在MRO不明显的更复杂的层次结构中。而且很难发现这个错误,因为通常Zd()一旦在某个地方被调用了Z.__init__,就会成功。在

相关问题 更多 >