使用kwargs时Python 2和3元类兼容性

时间:2013-05-18 22:20:07

标签: python django python-3.x metaclass

我正在创建一个元类,我在其中自定义__new__方法,以根据kwargs中提供的值自定义新类的创建方式。这可能在一个例子中更有意义:

class FooMeta(type):
    def __new__(cls, name, bases, kwargs):
        # do something with the kwargs...
        # for example:
        if 'foo' in kwargs:
            kwargs.update({
                'fooattr': 'foovalue'
            })
        return super(FooMeta, cls).__new__(cls, name, bases, kwargs)

我的问题是如何使Python 2和3兼容。Six是一个很好的兼容性库,但它不能解决我的问题。您可以将其用作:

class FooClass(six.with_metaclass(FooMeta, FooBase)):
    pass

这不起作用,因为六个使用给定的元类创建一个新的基类。下面是六个代码(link)(从1.3开始):

def with_metaclass(meta, base=object):
    return meta("NewBase", (base,), {})

因此,我的__new__方法将被调用而没有任何kwargs因此基本上“破坏”我的功能。问题是如何在不破坏Python 2和3的兼容性的情况下完成我想要的行为。请注意我不需要Python 2.6支持,所以如果只有Python 2.7.x和3.x可能的话我就是很好。

背景

我需要这个才能在Django中工作。我想创建一个模型工厂元类,它取决于模型属性将自定义模型的创建方式。

class ModelMeta(ModelBase):
    def __new__(cls, name, bases, kwargs):
        # depending on kwargs a different model is constructed
        # ...

class FooModel(six.with_metaclass(ModelMeta, models.Model):
    # some attrs here to be passed to the metaclass

1 个答案:

答案 0 :(得分:2)

如果我理解你的话,那就没问题了,它已经有效了。你真的检查过你的元类是否有预期的效果吗?因为我认为它已经存在了。

with_metaclass返回的类并不意味着扮演你的类FooClass的角色。它是一个虚拟类,用作类的基类。由于此基类是元类FooMeta,因此派生类也将具有元类FooMeta,因此将使用适当的参数再次调用元类。

class FooMeta(type):
    def __new__(cls, name, bases, attrs):
        # do something with the kwargs...
        # for example:
        if 'foo' in attrs:
            attrs['fooattr'] = 'foovalue'
        return super(FooMeta, cls).__new__(cls, name, bases, attrs)
class FooBase(object):
    pass
class FooClass(with_metaclass(FooMeta, FooBase)):
    foo = "Yes"

>>> FooClass.fooattr
'foovalue'

顺便说一下,调用元类kwargs的第三个参数会很困惑。它不是真正的关键字参数。它是要创建的类的__dict__ - 即类的属性及其值。根据我的经验,此属性通常称为attrsdct(请参阅例如herehere)。