我正在使用 Python 3 中的元类:
class M(type):
def __new__(cls, clsname, bases, attrs):
for name, attr in attrs.items():
if callable(attr):
attrs[name] = attr
return type.__new__(cls, clsname, bases, attrs)
class C(metaclass=M):
def f(self, x):
print(x)
if __name__ == '__main__':
c = C()
c.f(1)
c.f(2)
到目前为止,没有什么特别的,我只是加入了一个类的创建,并将其方法替换为......好吧,它本身,所以难怪一切正常。但是:
class M(type):
def __new__(cls, clsname, bases, attrs):
for name, func in attrs.items():
if callable(func):
attrs[name] = lambda *args, **kwds: func(*args, **kwds)
return type.__new__(cls, clsname, bases, attrs)
有时有效,有时无效:
user$ python test.py
1
2
user$ python test.py
Traceback (most recent call last):
File "./meta.py", line 23, in <module>
main()
File "./meta.py", line 19, in main
instance.method(1)
File "./meta.py", line 9, in <lambda>
attrs[name] = lambda *args, **kwds: func(*args, **kwds)
TypeError: 'str' object is not callable
但我只是用 lambda 包装器替换了它的方法! “str”与什么有什么关系?我做错了什么?
(以防万一出现一些奇怪的平台相关实现问题,我使用的是 Ubuntu Server 12.04.3...)
更新:修复了回溯中的名称不匹配问题。
最佳答案
进一步详细说明我的评论:
def makelambda(func):
return lambda *args, **kwds: func(*args, **kwds)
class M(type):
def __new__(cls, clsname, bases, attrs):
for name, func in attrs.items():
if callable(func):
attrs[name] = makelambda(func)
return type.__new__(cls, clsname, bases, attrs)
这是必要的,因为在您的原始代码中,在 lambda 内部,func
引用您的 __new__
方法返回时 func
所具有的任何值,不是创建 lambda 时的值。这是违反直觉的,但您可以验证它:
lambdas = [lambda: x for x in range(10)]
print(lambdas[0]()) # prints 9, as do lambdas[1] through [9]
为了解决这个问题,我们使用一个单独的函数来创建 lambda,从而在创建 lambda 时“卡住”func
变量的值。您还可以使用 lambda 上的默认参数值来执行此操作,但由于您在这里使用 *
和 **
,所以这有点问题。
(该行为与元类无关,您在定义 lambda 的任何地方都会看到相同的行为,并在创建它们后更改其中使用的变量的值。在这方面,lambda 与任何其他函数没有什么不同。 )
关于Python 3 奇怪的元类行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21590275/