无法设置与已删除属性同名的属性
我一直在努力理解Python中的@property是怎么回事。我在这个网站上查阅了类似的问题,但还是有些地方搞不清楚它是怎么运作的。比如说下面这个情况:
class MyClass(object):
def __init__(self):
self.name = 'x'
del self.name
@property
def name(self):
return 'y'
my_class = MyClass()
print my_class.name # AttributeError: can't set attribute (self.name = 'x')
为什么这样不行呢?据我理解,你不能用和已有属性同名的属性来设置一个新属性,所以应该在前面加个_
,比如在我的例子中用self._name
。可是我已经删除了self.name,为什么这还会有影响呢?或者说Python的执行顺序并不是我想的那样?
2 个回答
这个属性在调用 __init__
之前就已经存在了,并不是在 __init__
之后才出现的。当你尝试执行
self.name = 'x'
时,你会调用 name
属性的 __set__
方法,但会出现 AttributeError
错误,因为你没有定义一个设置器。
当你调用
del self.name
时,你会调用 name
属性的 __delete__
方法(如果你真的执行到了那一行)。这也会出现 AttributeError
错误,因为你没有定义一个删除器。
任何对 self.name
的访问在 __init__
方法中都会和你的属性产生互动。你并没有绕过这个属性。
你的属性没有定义设置器(setter)或删除器(deleter),所以不能给 self.name
赋值,也不能用 del self.name
删除它。
如果你添加了设置器或删除器,那么在 __init__
中的代码就能正常工作了:
class MyClass(object):
def __init__(self):
self.name = 'x'
del self.name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@name.deleter
def name(self):
del self._name
我把属性改成使用一个不同的实例属性,叫做 _name
,这样就不会和属性本身冲突了。self._name
不是一个属性,所以你可以直接给它赋值和删除。
更详细的技术解释是,property
对象是一个 描述符对象,而且是特定类型的描述符。它是一个 数据描述符,这意味着它总是在任何实例属性之前被使用。
普通的非数据描述符(比如函数、classmethod
和 staticmethod
对象)只有一个 __get__
方法。而数据描述符至少还定义了一个 __set__
或 __delete__
方法。当 Python 查找实例上的属性时,会进行以下步骤:
- 在类(及其基类)中查找数据描述符
- 在实例上查找属性
- 在类(及其基类)中查找属性,包括非数据描述符。
当你尝试访问 self.name
时,实际上是在查找 self
上的一个属性。Python 在类中找到了 name
属性,这就是一个数据描述符。所以这里使用了第一步,调用 MyClass.name.__get__(self, MyClass)
来(间接地)调用你的 name
函数。
当你尝试给 self.name
赋值时,会调用 MyClass.name.__set__(self, newvalue)
,而对于 del self.name
,则会调用 MyClass.name.__delete__(self)
。如果你没有在类中定义设置器或删除器,property
对象的默认行为就是抛出一个 AttributeError
错误。