1

好的,所以我开始使用元类来处理 voodoo 和 Python 的阴暗面,我想在进一步修改它之前获得一个简单的示例,为什么在执行此代码时它不打印 print 语句,

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)
4

3 回答 3

2

要理解的是,元类本身并不是一个——它是一个返回类的可调用对象——一个工厂。它的目的与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而不是类型typenamebases并且Python 运行时从您的块中dict_解析出来。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 >= 2.6 添加了对类装饰器的支持,它涵盖了元类的大多数用例,具有更简单的接口。(而不是自己创建类,你会得到一个你修改的已经创建的类对象。)

您可以在我最近写的这个 stackoverflow 答案中看到对多种元编程方法的调查。最初的问题是“如何使内置的 Python 容器类型线程安全?”

于 2012-12-28T17:10:53.870 回答
1

我从未真正使用过元类,但我认为您将继承与元类混合在一起。元类用于构建实例,但它们不会隐式地向您的实例添加任何内容。没有理由将__setattr__方法导入Test. 要显式添加它,您需要修改Testdict ( 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)

我希望这可以帮助:-)

最后,这个链接可以帮助你理解python中的元类。

于 2012-12-28T15:40:30.313 回答
1

您正在__setattr__为类本身定义,而不是为它的实例定义。这将导致 print 语句执行:

Test.key = value
于 2012-12-28T17:16:10.863 回答