只读数据描述符是一种同时定义了__get__
和__set__
方法的描述符,但是当调用__set__
方法时会引发AttributeError
异常。
一个简单的只读属性示例:
class Test():
_i = 1
@property
def i(self):
return self._i
assert hasattr(Test.i, '__get__')
assert hasattr(Test.i, '__set__')
t = Test()
t.i # 1
t.i = 2 # ERROR
如果我有一个类的实例,可以通过以下方式确定实例属性是否为只读数据描述符(尽管我非常不喜欢这样做):
def is_ro_data_descriptor_from_instance(instance, attr):
temp = getattr(instance, attr)
try:
setattr(instance, attr, None)
except AttributeError:
return True
else:
setattr(instance, attr, temp)
return False
如果我知道该类不需要任何参数进行实例化,我可以确定它的类属性是否类似于上面的只读数据描述符:
def is_ro_data_descriptor_from_klass(klass, attr):
try:
setattr(klass(), attr, None)
except AttributeError:
return True
else:
return False
然而,如果我事先不知道类的签名,并尝试以这种方式实例化临时对象,可能会出现错误:
class MyClass():
i = 1
def __init__(self, a, b, c):
'''a, b, and c are required!'''
pass
def is_ro_data_descriptor_from_klass(MyClass, 'i') # Error
怎样确定一个类属性是只读数据描述符?
编辑:添加更多信息。
以下是我正在尝试让其工作的代码:
class StaticVarsMeta(type):
'''A metaclass that will emulate the "static variable" behavior of
other languages. For example:
class Test(metaclass = StaticVarsMeta):
_i = 1
@property
def i(self):
return self._i
t = Test()
assert t.i == Test.i'''
statics = {}
def __new__(meta, name, bases, dct):
klass = super().__new__(meta, name, bases, dct)
meta.statics[klass] = {}
for key, value in dct.items():
if "_" + key in dct:
meta.statics[klass][key] = set()
if hasattr(value, '__get__'):
meta.statics[klass][key].add('__get__')
if hasattr(value, '__set__'):
try:
value.__set__(None, None)
except AttributeError:
continue
else:
meta.statics[klass][key].add('__set__')
return klass
def __getattribute__(klass, attr):
if attr not in StaticVarsMeta.statics[klass]:
return super().__getattribute__(attr)
elif '__get__' not in StaticVarsMeta.statics[klass][attr]:
return super().__getattribute__(attr)
else:
return getattr(klass, '_' + attr)
def __setattr__(klass, attr, value):
if attr not in StaticVarsMeta.statics[klass]:
super().__setattr__(attr, value)
elif '__set__' not in StaticVarsMeta.statics[klass][attr]:
super().__setattr__(attr, value)
else:
setattr(klass, '_' + attr, value)
class Test(metaclass = StaticVarsMeta):
_i = 1
def get_i(self):
return self._i
i = property(get_i)
请注意以下内容:
type(Test.i) # int
type(Test.__dict__['i']) # property
Test().i = 2 # ERROR, as expected
Test.i = 2 # NO ERROR - should produce an error
StaticVarsMeta
,它将模拟其他语言的静态变量行为。这些类将允许您通过类本身或实例获取和设置“静态变量”。我已经完成了大部分工作,但意识到当描述符是只读数据描述符时,我必须在StaticVarsMeta.__setattr__
中引发AttributeError
。 - Ricksuper().__setattr__(attr, value)
进行传递,它会覆盖描述符对象。它不会引发错误。 - Rick