Python 为什么instance.attribute=value可以工作,而属性中未实现_set__方法?

Python 为什么instance.attribute=value可以工作,而属性中未实现_set__方法?,python,properties,descriptor,Python,Properties,Descriptor,我正在通过属性学习描述符协议,我正在编写自己的属性,如下所示: class my_property(object): def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None:

我正在通过属性学习描述符协议,我正在编写自己的属性,如下所示:

class my_property(object):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
           raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel)

class test_my_property(object):
    def __init__(self, value):
        self._val = value

    @my_property
    def val(self):
        return self._val

     @val.setter
     def val(self, value):
         self._val = value

def main():
     c = test_my_property(5)
     print c.val
     c.val = 10
     print c.val
     print type(c).__dict__['val'].__set__
我得到:

5
10
AttributeError: 'my_property' object has no attribute '__set__'
我的问题是,既然没有定义“\uuuu set\uuuuu”,那么“c.val=10”如何工作呢? 如果“\uuuu set\uuuu”是由my\u属性从对象继承的,那么为什么它会报告AttributeError?

\uuuu set\uuuuu
不是从对象继承的。获取和设置
val
属性是有效的,因为当访问对象的属性时,首先检查实例,然后检查类。因为您设置了实例属性
val
,所以它使用该属性。我认为这一点特别明显,如果你看一个没有描述符的简单例子

>>> class Foo(object):
...     val = 5
... 
>>> f = Foo()
>>> f.val  # f doesn't have val so fallback on Foo
5
>>> f.val = 10
>>> f.val  # f now has val so use it
10
>>> del f.val  # oops what now
>>> f.val  # class again
5
上述示例与您的示例之间的唯一区别是,您的类
val
是(当您完成时)一个属性

尽管如此,您通常不希望将属性命名为与保存其内容的实例属性相同的东西。通常的表述是这样的

class Foo(object):
    def __init__(self, value):
        self._val = value

    @property
    def val(self):
        return self._val

@杰瑞德的回答是正确的<代码>\uuuu集合\uuuuu不是从对象继承的。我将尝试用另一种方式解释,这可能会更清楚

首先,正如您已经了解的,如果您的描述符确实有一个
\uuu set\uu
方法,那么在运行
c.val=10
时会调用它。这意味着解释器将查找
\uuuuu set\uuuuu
方法,如果找到它,它将通过调用它将其视为描述符

现在,由于
my_属性
没有
\uuu设置
方法,因此在运行
c.val=10
时,它不会得到描述符处理。解释器返回到“标准”处理,这大致相当于
c.\uu dict\uu['val']=10

您可以通过以下方式轻松验证:

print c.__dict__  # no 'val'
c.val = 10
print c.__dict__  # 'val' was added
现在,
c.\uu dict\uuuu
(在对象级别)中的“val”超出了您的属性(在类级别定义),并将在访问
c.val
时使用


如果你想禁止转让你的财产,你需要明确地这样做。您需要定义一个
\uuuu set\uuu
方法并在其中引发错误。

您对
setter
的实现是错误的。您在这里实现的只是属性行为的一半。请参阅:可能值得注意的是,您的
setter
只会被调用一次,而不会在您设置属性值时被调用。另见@Jared的回复。嗨,即使我更改了代码,同样的事情也在发生。非常感谢,@shx2。现在更清楚了。因此,关键点是,当为描述符对象分配新值时,应该存在_set__方法。否则,将以“立场”的方式处理。对吗?