使用装饰器设置类'元类

时间:2012-06-18 21:50:34

标签: python decorator metaclass

this answer之后,似乎可以在使用以下*定义类后更改类的元类:

class MyMetaClass(type):
    # Metaclass magic...

class A(object):
    pass

A = MyMetaClass(A.__name__, A.__bases__, dict(A.__dict__))

定义一个功能

def metaclass_wrapper(cls):
    return MyMetaClass(cls.__name__, cls.__bases__, dict(cls.__dict__))

允许我将装饰器应用于类定义,如此,

@metaclass_wrapper
class B(object):
    pass

似乎将元类魔法应用于B,但B没有__metaclass__属性。上面的方法是将元类应用于类定义的合理方法,即使我正在定义并重新定义类,或者我会更好地简单地编写

class B(object):
    __metaclass__ = MyMetaClass
    pass

我认为这两种方法之间存在一些差异。


*请注意,关联问题MyMetaClass(A.__name__, A.__bases__, A.__dict__)中的原始答案会返回TypeError

  

TypeError:type()参数3必须是dict,而不是dict_proxy

似乎__dict__的{​​{1}}属性(类定义)具有类型A,而实例的dict_proxy属性的类型<{1}}的类型为__dict__。为什么是这样?这是Python 2.x与3.x的区别吗?

3 个答案:

答案 0 :(得分:4)

不可否认,我有点迟到了。但是,我觉得这值得补充。

这是完全可行的。话虽如此,还有很多其他方法可以实现同样的目标。但是,装饰解决方案尤其允许延迟评估(obj = dec(obj)),而在课堂内使用__metaclass__则不会。在典型的装饰风格中,我的解决方案如下。

如果您只是在不更改字典或复制其属性的情况下构造类,则可能会遇到一些棘手的问题。该类之前(装饰前)的任何属性似乎都会丢失。因此,复制这些内容绝对必要,然后像我在解决方案中那样调整它们。

就个人而言,我希望能够跟踪对象的包装方式。所以,我添加了__wrapped__属性,这不是绝对必要的。它也使它更像Python 3中的functools.wraps类。但是,内省可能会有所帮助。此外,添加__metaclass__以更像正常的元类用例。

def metaclass(meta):
    def metaclass_wrapper(cls):
        __name = str(cls.__name__)
        __bases = tuple(cls.__bases__)
        __dict = dict(cls.__dict__)

        for each_slot in __dict.get("__slots__", tuple()):
            __dict.pop(each_slot, None)

        __dict["__metaclass__"] = meta

        __dict["__wrapped__"] = cls

        return(meta(__name, __bases, __dict))
    return(metaclass_wrapper)

对于一个简单的例子,请采取以下措施。

class MetaStaticVariablePassed(type):
    def __new__(meta, name, bases, dct):
        dct["passed"] = True

        return(super(MetaStaticVariablePassed, meta).__new__(meta, name, bases, dct))

@metaclass(MetaStaticVariablePassed)
class Test(object):
    pass

这会产生不错的效果......

|1> Test.passed
|.> True

以不太常见但相同的方式使用装饰器...

class Test(object):
    pass

Test = metaclass_wrapper(Test)

......正如预期的那样,收益率也是一样的好结果。

|1> Test.passed
|.> True

答案 1 :(得分:2)

我对你的问题的总结:“我尝试了一种新的棘手的方法来做一件事,而且它没有用。我应该用简单的方法吗?”

是的,你应该这么简单。你还没有说明为什么你有兴趣发明一种新方法。

答案 2 :(得分:1)

该类没有设置__metaclass__属性...因为你从未设置它!

使用哪个元类通常由 在类块中设置的名称__metaclass__确定。元类未设置__metaclass__属性。因此,如果您直接调用元类而不是设置__metaclass__并让Python弄明白,那么就不会设置__metaclass__属性。

实际上,普通类都是元类type的所有实例,因此如果元类始终在其实例上设置__metaclass__属性,则每个类都将具有{ {1}}属性(大多数设置为__metaclass__)。


我不会使用你的装饰器方法。它模糊了一个事实,即涉及元类(以及哪一个),仍然是一行样板,并且从type的3个定义特征创建一个类只是为了将这些3位从得到的类,抛弃类,并用相同的3位创建一个新类!

在Python 2.x中执行此操作时:

(name, bases, attributes)

如果你写了这个,你会得到大致相同的结果:

class A(object):
    __metaclass__ = MyMeta
    def __init__(self):
        pass

实际上计算元类更复杂,因为实际上必须搜索所有基数以确定是否存在元类冲突,以及其中一个基数是否有attrs = {} attrs['__metaclass__'] = MyMeta def __init__(self): pass attrs['__init__'] = __init__ A = attrs.get('__metaclass__', type)('A', (object,), attrs) 作为其元类并且type不包含attrs,则默认元类是祖先的元类而不是__metaclass__。在这种情况下,我希望您的装饰器“解决方案”与直接使用type不同。我不确定如果你在使用__metaclass__会给你一个元类冲突错误的情况下使用你的装饰器会发生什么,但我不希望它是愉快的。

此外,如果涉及任何其他元类,您的方法将导致它们首先运行(可能修改名称,基础和属性!)然后将它们拉出类并使用它来创建新的类。这可能与使用__metaclass__得到的完全不同。

至于__metaclass__没有给你一个真正的字典,那只是一个实现细节;出于性能原因我猜。我怀疑是否有任何规范说明(非类)实例的__dict__必须与类的__dict__类型相同(这也是一个实例btw;只是一个实例一个元类)。类的__dict__属性是“dictproxy”,它允许您查找属性键,就好像它是__dict__但仍然不是dictdict对第三个论点的类型很挑剔;它想要一个真正的字典,而不仅仅是一个“类似字典”的对象(因为破坏鸭子打字而感到羞耻)。它不是2.x vs 3.x的东西; Python 3的行为方式相同,但它为您提供了type更好的字符串表示形式。 Python 2.4(我现有的最早的2.x)也有类dictproxy对象的dictproxy个对象。