在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的区别吗?
答案 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__
但仍然不是dict
。 dict
对第三个论点的类型很挑剔;它想要一个真正的字典,而不仅仅是一个“类似字典”的对象(因为破坏鸭子打字而感到羞耻)。它不是2.x vs 3.x的东西; Python 3的行为方式相同,但它为您提供了type
更好的字符串表示形式。 Python 2.4(我现有的最早的2.x)也有类dictproxy
对象的dictproxy
个对象。