更新 - 2012/12/13
只是为了澄清 - 我对如何将方法添加到课程方面并不感兴趣 - 正如您在我的问题和人们的答案中可以看到的那样,有不止一种方法可以做到这一点(舌头在脸颊和帽子尖端到我的Perl自我)。
我感兴趣的是学习使用不同方法向类添加方法的根本区别,而真正的问题是为什么我需要使用元类。例如 Python元类编程入门 说:
也许最常见的使用元类[...]:为生成的类中定义的方法添加,删除,重命名或替换方法。
而且由于有更多的方法可以做到这一点,我很困惑并寻找解释。
谢谢!
原创 - 2012/12/12
我需要动态地向类添加方法(以及基于该类的新生成的类)。我提出了两种方法,一种涉及元类,另一种没有一种。除了事实上后者不涉及“黑魔法”元类之外,我看不出这两种方法给我带来的任何差别;)
途径 #1 同 元类:
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 无 元类:
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, ), {})
要么 type("NewClassZ", (Z, ), {})
我得到了相同的预期结果,这两种方法没有区别。
所以,我想知道这些方法中是否存在任何“潜在的”差异,或者如果我使用过任何东西会“咬”我的话 #1 要么 #2 或者它只是一个语法糖?
PS: 是的,我在这里阅读了其他主题,讨论Python和pythonic数据模型中的元类。
使用元类的显而易见的原因是,一旦知道了类,它们确实提供了关于类的元数据,与对象的存在无关。琐碎,对吗?好吧,让我们展示一下我在原版上执行的一些命令 Z
和 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对'types'(类对象)进行操作当然不是一种不常见的操作,你可能希望它们尽可能保持一致,当然对于这种情况(在运行时方法不是真正动态的,只是在声明时)时间)。
跳到脑海的一个应用是文档。如果您要添加文档字符串 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__
之前被称为某个地方。
如果问题是动态添加方法,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()
输出:
$ python script.py
10
100
1000000
问候!
P.S。:我看到这里没有黑魔法的相似之处(:
编辑:
考虑到马蒂诺的评论,我补充道 data attributes
不是 method function attributes
。我真的可以看到做之间没有区别 Alpha.d = foo
和 Alpha.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
,另一个是对使用该函数的lambda函数的引用 foo
在其定义体中。
如果我说错了,请纠正我。
问候!