【发布时间】:2012-05-17 05:00:20
【问题描述】:
有没有办法编写一个元类,使基于这个元类的类中定义的所有方法默认都是类方法(即不需要,例如,@classmethod 装饰等)?
谢谢!
编辑:我对 Python 2.7.3 感兴趣。遗漏请见谅。
【问题讨论】:
标签: python metaclass class-method
有没有办法编写一个元类,使基于这个元类的类中定义的所有方法默认都是类方法(即不需要,例如,@classmethod 装饰等)?
谢谢!
编辑:我对 Python 2.7.3 感兴趣。遗漏请见谅。
【问题讨论】:
标签: python metaclass class-method
当然。真正的问题是你为什么要这样做。我将假设这仅用于教育。实际上,无论这可以用于什么,都可能最好明确地完成(即不使用元类并添加@classmethod)。
我知道两个选项:类定义的自定义命名空间,以及__new__ 中的后处理。第一个非常酷,在某些情况下实际上是不可避免的(例如记住方法定义的顺序),但后者更简单、更灵活,不会意外破坏同名的多个赋值,并且在 Python 2 中可用。你的目的,这排除了前者,但我会把它留给所有可以使用 Python 3 的人。
要提供自定义命名空间,您必须在元类中定义 __prepare__ 并返回一个类似字典的对象(Ferdinand Beyer 在评论中指出:collections 中定义的映射接口就足够了)。在这种情况下,字典应该覆盖__setitem__ 以检查值是否为callable,如果是,则在存储之前将其包装在classmethod 中。我非常感谢下面的 cmets,他们极大地改进了下面的代码。 Ferdinand Beyer 注意到 callable(classmethod(...)) 是错误的,并且 agf 指出了与 @classmethod 和 Python 3 兼容性的不一致。
import types
# Either this, in a dictionary class instanciated in __prepare__
# Again, Python 3 only
def __setitem__(self, name, value):
if isinstance(value, types.FunctionType):
value = classmethod(value)
super().__setitem__(name, value)
# __prepare__ would be: return AutomaticClassMethodNamespace() or something
# Or this in the metaclass
def __new__(mcls, clsname, bases, ns):
for name, value in ns.items():
if isinstance(value, types.FunctionType):
ns[name] = classmethod(value)
# Any other metaclass behaviour
【讨论】:
types.FunctionType 而不是callable。你的意思也是and not。我不确定在元类中使用 __new__ 会如何弄乱同名的多个定义,因为它会得到一个字典,其中每个标识符都已被解析为单个值?
__prepare__返回的对象不需要继承自dict,只要“支持映射接口”即可(例如使用collections.MutableMapping抽象基班级)。见docs.python.org/py3k/reference/…
classmethod 的实例不可调用,因此不需要额外检查。
classmethod 不是函数的可调用对象。
callable 还是检查FunctionType 是一个品味问题......
类似这样的东西 (2.7)
def m(name, bases, dct):
for k, v in dct.items():
if type(v) is type(m):
dct[k] = classmethod(v)
return type(name, bases, dct)
class A:
__metaclass__ = m
def foo(self):
print self
A.foo() # <class '__main__.A'>
【讨论】:
if isinstance(v, types.FunctionType):。如果您使用比m、k 和v 更有意义的名称,这也会更好。
type 的类而不是函数;您的代码中断。这就是为什么您使用您所指的实际类型而不是间接找到它的原因。使用有意义的名称,尤其是在向某人解释某事时,是基本常识。
if isinstance(v, types.FunctionType): 没有什么神秘之处。阅读。 “如果 v 是函数类型的实例”。您的版本显示“如果 v 的类型与 m 的类型是相同的对象”,因此您谈论函数类型的事实并不明确。使用isinstance 也是标准,因此应该首选,除非有充分的理由使用不同的东西。