如何创建一个带有元类的类属性,该元类仅在类本身没有定义时才覆盖?

时间:2014-03-17 18:48:09

标签: python python-2.7

我有这样的设置:

class Meta(type):
    @property
    def test(self):
        return "Meta"


class Test(object):
    __metaclass__ = Meta
    test = "Test"


class TestSub(object):
    test = "TestSub"

print(Test.test, TestSub.test)

产生以下输出:

('Meta', 'TestSub')

我期望的是:

('Test', 'TestSub')

我知道为什么会发生这种情况:在执行元类test之前,Test 上会分配Meta。但我不知道如何实现一种改变这种行为的简洁方法。我知道我可以在__init__类的__new__Meta中进行攻击,但这看起来很脏,因为我必须为每个新属性修改它。那么有一个干净的方式(比如写一个新的装饰器)来获得这个吗?

我也不喜欢创建一个中间类只是为了解决这个问题,但是会接受它作为最后的手段。

3 个答案:

答案 0 :(得分:1)

如果这是您想要的,请不要使用元类。使用继承:

class Base(object):
    @property
    def test(self):
        return "Meta"

class Test(Base):
    test = "Test"

class TestSub(object):
    test = "TestSub"

print(Test.test, TestSub.test)

产量

('Test', 'TestSub')

答案 1 :(得分:1)

事实上,您的Test班级test属性不会被覆盖。它还在那里:

>>> Test.__dict__['test']
'Test'

但是,Test.test无法访问它,因为根据the documentation

  

如果实例的字典具有与数据描述符同名的条目,则数据描述符优先。

property创建数据描述符。因此,通过在元类中使用属​​性,可以阻止访问类上的普通类变量。

如何最好地解决这个问题尚不清楚,因为我们不清楚你用这种结构试图完成什么。鉴于您发布的代码,您不清楚为什么要使用元类。如果您只想在子类上覆盖类属性,则可以使用简单继承来完成它,如unutbu的答案中所述。

答案 2 :(得分:0)

我不确定你需要什么?

class Meta(type):
    @property
    def test(self):
        return 'Meta'

    def __new__(cls, name, bases, dct):
        if 'test' not in dct:
            dct['test'] = Meta.test
        return super(Meta, cls).__new__(cls, name, bases, dct)

class Test(object):
    __metaclass__ = Meta
    test = "Test"

class TestSub(object):
    test = "TestSub"

class TestWithoutTest(object):
    __metaclass__ = Meta

print(Test.test, TestSub.test, TestWithoutTest.test)