【问题标题】:Metaclass to make class methods the default?元类使类方法成为默认值?
【发布时间】:2012-05-17 05:00:20
【问题描述】:

有没有办法编写一个元类,使基于这个元类的类中定义的所有方法默认都是类方法(即不需要,例如,@classmethod 装饰等)?

谢谢!

编辑:我对 Python 2.7.3 感兴趣。遗漏请见谅。

【问题讨论】:

    标签: python metaclass class-method


    【解决方案1】:

    当然。真正的问题是你为什么要这样做。我将假设这仅用于教育。实际上,无论这可以用于什么,都可能最好明确地完成(即不使用元类并添加@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 的实例不可调用,因此不需要额外检查。
    • @FerdinandBeyer 这仍然会尝试classmethod 不是函数的可调用对象。
    • @agf:没错。我只是在评论“无限递归”(?)位。我可能会选择您的解决方案,但最终是使用callable 还是检查FunctionType 是一个品味问题......
    【解决方案2】:

    类似这样的东西 (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):。如果您使用比mkv 更有意义的名称,这也会更好。
    • @agf:我不相信咒语。
    • 我不知道该评论是什么意思。假设我重命名元类函数;您的代码中断。假设我将其切换为使用继承自 type 的类而不是函数;您的代码中断。这就是为什么您使用您所指的实际类型而不是间接找到它的原因。使用有意义的名称,尤其是在向某人解释某事时,是基本常识。
    • if isinstance(v, types.FunctionType): 没有什么神秘之处。阅读。 “如果 v 是函数类型的实例”。您的版本显示“如果 v 的类型与 m 的类型是相同的对象”,因此您谈论函数类型的事实并不明确。使用isinstance 也是标准,因此应该首选,除非有充分的理由使用不同的东西。
    • @arg:我相信 OP 将能够根据他们的需要调整我的功能。我们在这里分享想法,而不是可用于生产的代码块。
    猜你喜欢
    • 2011-12-15
    • 1970-01-01
    • 2013-04-27
    • 2011-03-06
    • 2013-02-22
    • 2011-06-11
    • 2021-02-17
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多