好吧所以我开始乱用voodoo和带有元类的Python的黑暗面,我想在我进一步修改之前得到一个简单的例子,为什么在执行这段代码时不打印print语句,< / p>
class Versioned(type):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
def __setattr__(cls, key, value):
print "Setting attr %s to %s" % (key, value)
return object.__setattr__(cls, key, value)
class Test(object):
__metaclass__ = Versioned
def __init__(self):
self.a = 1
self.b = 2
a = Test()
a.b = 31
print dir(a)
答案 0 :(得分:2)
要理解的是,元类本身不是类 - 它是一个可调用的,它返回一个类 - 一个类工厂< / em>的。它的作用与type(__name__, __bases__, __dict__)
相同。
>>> type('myclass', (), {})
<class '__main__.myclass'>
定义__metaclass__
时,您只是overriding the default class factory for that specific class or module
。例如,这是一个元类:
def setattr_logging_class(name, bases, dict_):
"""I am a metaclass"""
def __setattr__(self, k, v):
print "{} set attribute {} to {}".format(self, k, v)
super(self.__class__, self).__setattr__(k, v)
dict_['__setattr__'] = __setattr__
cls = type(name, bases, dict_)
return cls
class MyClass(object):
__metaclass__ = setattr_logging_class
def __init__(self):
self.a = 1
obj = MyClass()
obj.b = 2
print obj.__dict__
最重要的是,元类不参与创建的类的方法解析(除非您更改bases
)。这就是您的Versioned.__setattr__
个实例无法看到Test
的原因。所有Versioned
所做的都是返回一个新类(类型为Versioned
而不是类型type
),具有相同的name
,bases
和dict_
Python运行时解析出class Test(object):
块。
类本身可以调用(通过他们的__new__
方法)。 (__new__
适用于__call__
实例的类。)
class MyClass(object):
def __new__(cls_self, *args, **kwargs):
print "I am {} called with {} and {}".format(cls_self, args, kwargs)
return None
myobj = MyClass(1,2,3,a=4,b=5,c=6)
print myobj # == None
由于类是可调用的,因此可以将类用作元类。实际上,type
是一个类。虽然,你也可以使用__call__
方法的实例!这两个例子是相似的,没有任何你可能想要做的事情,这些都无法做到办法。 (事实上,使用对象通常更直接。)
class MetaClass(type):
def __new__(cls, name, bases, dict_):
print "I am {} called with {}, {}, {}".format(cls, name, bases, dict_)
return type.__new__(cls, name, bases, dict_)
class MetaObject(object):
def __call__(self, name, bases, dict_):
print "I am {} called with {}, {}, {}".format(self, name, bases, dict_)
return type(name, bases, dict_)
class MyClass(object):
__metaclass__ = MetaClass
class MyClass2(object):
__metaclass__ = MetaObject()
请注意,除了元类之外,Python还有许多其他的元编程方法。特别是,Python&gt; = 2.6增加了对类装饰器的支持,它涵盖了具有更简单接口的元类的大多数用例。 (而不是自己创建类,您将获得一个已修改的已创建的类对象。)
您可以在this stackoverflow answer I recently wrote查看多种元编程方法的调查。最初的问题是“我如何制作内置的Python容器类型线程安全?”
答案 1 :(得分:1)
我从未真正使用过元类,但我认为你将继承与元类混合在一起。元类用于构建实例,但它们不会向您的实例隐式添加任何内容。没有理由在__setattr__
中导入Test
方法。要明确地添加它,您需要修改Test
dict(dct
)。像dct["__setattr__"] = cls.__setattr__
这样的东西。但是,它不起作用,我不明白为什么。我发现让它发挥作用的唯一方法是:
class Versioned(type):
def __new__(cls, name, bases, dct):
print("BLAH")
def __setattr__(cls, key, value):
print("Setting attr %s to %s" % (key, value))
return object.__setattr__(cls, key, value)
dct["__setattr__"] = __setattr__
return type.__new__(cls, name, bases, dct)
我希望这可以提供帮助: - )
最后,这个link可以帮助你理解python中的元类。
答案 2 :(得分:1)
您正在为类本身定义__setattr__
,而不是为其实例定义Test.key = value
。这将导致print语句执行:
{{1}}