我正在研究描述符,以及它们是如何在属性和函数背后使用的机制。我对在实现描述符与使用属性时如何查找属性感到困惑。
sudo bypass rm -rfv /Volumes/*/.Trashes
输出为:
try:
sudo bypass rm -rfv /Volumes/*/.Trashes
except 'rm: fts_read: No such file or directory':
sudo bypass rm -rfv /Volumes/*/.Trashes
这很有意义,因为属性查找顺序为:
class NonDataDescriptor(object):
def __get__(self, instance, owner):
return 'non-data descriptor'
class DataDescriptor(object):
def __get__(self, instance, owner):
return 'data descriptor'
def __set__(self, instance, value):
pass
class MyClass(object):
descriptor_one = NonDataDescriptor()
descriptor_two = DataDescriptor()
def __init__(self):
self.descriptor_one = 'hello'
self.descriptor_two = 'goodbye'
mc = MyClass()
print(mc.descriptor_one)
print(mc.descriptor_two)
中的由于属性实际上只是实现描述符,所以我想知道为什么似乎不遵循这种顺序的属性查找。请参阅以下内容:
hello
data descriptor
输出为:
instance_obj.__dict__
我一直期待着,因为属性对象class MyClass(object):
def __init__(self, fname, lname):
self._fname = fname
self._lname = lname
@property
def fullname(self):
return '{} {}'.format(self._fname, self._lname)
mc = MyClass('Bob', 'John')
print(mc.fullname)
mc.__dict__['fullname'] = 'testing'
print(mc.__dict__)
print(mc.fullname)
实际上只是一个非数据描述符,因此实例属性Bob John
{'_fname': 'Bob', '_lname': 'John', 'fullname': 'testing'}
Bob John
将具有优先权。
此外,函数也被实现为非数据描述符,并且遵循此属性查找顺序:
fullname
输出为:
fullname
谁能解释为什么属性对象与众不同?
答案 0 :(得分:1)
诀窍在于此处的descriptor protocol
- 如果实例的字典中具有与数据描述符同名的条目,则数据描述符优先。
- 如果实例的词典中具有与非数据描述符同名的条目,则该词典条目优先。
@property
def fullname(self):
return '{} {}'.format(self._fname, self._lname)
这是一个数据描述符,因为装饰器返回的property
对象始终定义__set__
,即使您未定义setter(然后__set__
也会引发{{ 1}},请参阅code here。
AttributeError
因此,数据描述符优先。
由于其他示例的行为已达到您的预期,因此足以完成您的理解。
答案 1 :(得分:0)
@spectras已经回答了您的问题。我只想提供其他方法来检查描述符:
In [517]: import inspect
In [518]: inspect.isdatadescriptor(type(mc).__dict__['fullname'])
Out[518]: True
In [519]: inspect.isdatadescriptor(MyClass.__dict__['fullname'])
Out[519]: True
因此,即使您未定义setter
,@ property始终是数据描述符。在这种情况下,属性分配将引发AttributeError: can't set attribute