之前我发过一个类似的问题,但我解释了我的问题是错误的,所以我将其标记为删除,并提出了新的正确问题。
我的总体目标如下:我想在类上使用property
,因此我使用我的问题{{3}上建议的属性在元类上实现它}。此外,我希望用户能够子类化基类并使用静态值覆盖此属性。这里的事情是,如果用户没有提供属性,我想计算一个合理的默认值,因为所有配置都是在类级别完成的,所以我需要一个类级别的属性。
一个基本的例子将显示:
class Meta(type):
@property
def test(self):
return "Meta"
class Test(object):
__metaclass__ = Meta
class TestSub(Test):
test = "TestSub"
class TestSubWithout(Test):
pass
print(TestSub.test, TestSubWithout.test)
以下是它的印刷品:
('Meta', 'Meta')
我希望它打印出来:
('TestSub', 'Meta')
基本上,在TestSub
上,用户自己会覆盖test
属性。现在,TestSub
是正确的输出,因为用户提供了它。在TestSubWithout
的情况下,用户不提供属性,因此应该计算默认值(当然,实际计算不仅仅是静态返回,这只是为了显示它)。
我知道这里发生了什么:首先将属性test
分配给TestSub
,然后元类覆盖它。但我不知道如何以干净和pythonic 的方式阻止它。
答案 0 :(得分:1)
我能想到的最干净的方法是创建一个处理这种情况的property
子类:
class meta_property(property):
def __init__(self, fget, fset=None, fdel=None, doc=None):
self.key = fget.__name__
super(meta_property, self).__init__(fget, fset, fdel, doc)
def __get__(self, obj, type_):
if self.key in obj.__dict__:
return obj.__dict__[self.key]
else:
return super(meta_property, self).__get__(obj, type_)
通过存储函数的名称并返回被覆盖的值(如果存在)来处理这种情况。这似乎是一个很好的解决方案,但我愿意接受更高级的建议。
答案 1 :(得分:1)
property
是一个“数据描述符”,这意味着它在属性搜索路径中优先于存储在实例字典中的值(或者对于一个类,其MRO中其他类的实例字典) )。
相反,编写自己的非数据描述符,其作用类似于property
:
class NonDataProperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, type):
if obj:
return self.fget(obj)
else:
return self
# don't define a __set__ method!
以下是对它的测试:
class MyMeta(type):
@NonDataProperty
def x(self):
return "foo"
class Foo(metaclass=MyMeta):
pass # does not override x
class Bar(metaclass=MyMeta):
x = "bar" # does override x
class Baz:
x = "baz"
class Baz_with_meta(Baz, metaclass=MyMeta):
pass # inherited x value will take precedence over non-data descriptor
print(Foo.x) # prints "foo"
print(Bar.x) # prints "bar"
print(Baz_with_meta.x) # prints "baz"