【问题标题】:Questions about details of @property in Python关于Python中@property细节的问题
【发布时间】:2015-05-24 13:09:52
【问题描述】:

假设我有一个类如下:

class MyClass(object):

    def __init__(self, value=None):
        self.attr = value

    @property
    def attr(self):
        # This acts as a getter?
        # Let's call the function "attr_1" as alias
        return self.__attr

    @attr.setter
    def attr(self, value):
        # This acts as a setter?
        # Let's call the function "attr_2" as alias
        self.__attr = value


inst = MyClass(1)

我阅读了Documentation on Descriptor 并查看了property 类的实现。

据我所知,当我输入inst.attr 时,会发生以下情况:

  1. 找到第一个attr(别名为attr_1),attr 现在是property 类的一个实例,它是一个数据描述符
    因此,它将覆盖实例字典,这意味着调用了type(inst).__dict__['attr'].__get__(inst, type(inst))
  2. attr.__get__(inst, type(inst)) 调用 attr.fget(inst),其中 fget() 实际上是 attr(self)(“原始”attr_1 函数)。
  3. 最后,attr.fget(inst) 返回inst.__attr

  • 第一个问题来了:类MyClass没有属性__attr,那么第三步中的inst.__attr怎么解释?

  • 同样,在模拟的setter中,Python如何找到属性inst.__attr来分配value

  • 还有一个小问题:既然property 是一个类,为什么不用Property 而不是property

【问题讨论】:

  • property 实际上被认为是一种类型,而不是一个类(它们是不同的东西)
  • @thefourtheye property 的签名是 property(fget, fset, fdel, doc) 其中前三个参数是函数。问题中文档的链接显示了property 的完整实现。

标签: python python-2.7 descriptor


【解决方案1】:

您的问题实际上与属性以及它们作为数据描述符的工作方式没有直接关系。这只是 Python 伪造标记为以两个下划线开头的私有属性的方式。

>>> inst.__attr

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    inst.__attr
AttributeError: 'MyClass' object has no attribute '__attr'

假设您使用带有单个下划线的内部变量编写代码(通常约定是,您不应该碰这个,但我不会强制执行,风险自负) :

>>> class MyClass2(object):

    def __init__(self, value=None):
        self.attr = value

    @property
    def attr(self):
        # This acts as a getter?
        # Let's call the function "attr_1" as alias
        return self._attr

    @attr.setter
    def attr(self, value):
        # This acts as a setter?
        # Let's call the function "attr_2" as alias
        self._attr = value


>>> inst2 = MyClass2(1)
>>> inst2._attr
1

您可以通过查看对象的__dict__ 来了解其中的技巧

>>> inst2.__dict__
{'_attr': 1}
>>> inst.__dict__
{'_MyClass__attr': 1}

只是为了让你相信这与属性无关:

>>> class OtherClass(object):
    def __init__(self, value):
        self.__attr = value
    def get_attr(self):
        return self.__attr
    def set_attr(self, value):
        self.__attr = value


>>> other_inst = OtherClass(1)
>>> other_inst.get_attr()
1
>>> other_inst.__attr

Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    other_inst.__attr
AttributeError: 'OtherClass' object has no attribute '__attr'
>>> other_inst.__dict__
{'_OtherClass__attr': 1}
>>> other_inst._OtherClass__attr
1
>>> other_inst._OtherClass__attr = 24
>>> other_inst.get_attr()
24
>>> inst._MyClass__attr = 23
>>> inst.attr
23

关于您的最后一个问题,我只是认为 Python 中没有这样的约定,即类必须以大写字母开头。 property 不是一个孤立的案例(datetimeitemgettercsv.reader、...)。

【讨论】:

  • 谢谢。我试过你的示例代码,发现self.attrself._attr 是完全相同的对象(inst.attr is inst._attr 返回True)。为什么会这样?当我们定义两个变量并且其中一个是“受保护的”(至少它被认为是“按约定保护的”)但实际上它们是同一个对象时,这不会令人困惑吗?
  • 它们不是同一个属性。然而,他们持有的是同一个对象。这是您在输入inst.attr is inst._attr 时要测试的内容。就像a = c; b = c; a is b
  • @Zelong 你的第二个问题只是因为例子太简单了。你不会想要一个属性。一个更复杂但更准确的例子是一个类,它在 2D 中保存一个点,具有 x 和 y 属性,并说你想要一个 coordinates 属性,它返回一个包含 x 和 y 的元组。您还可以定义一个从 2 元组中提取 x 和 y 的 setter。删除器将删除 x 和 y。根据您的需要,可以直接访问 x 和 y,或者您可能想要隐藏它们(在 Python 中,您不应该隐藏它们)。
猜你喜欢
  • 2012-02-01
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-05
  • 1970-01-01
  • 2010-12-31
  • 2011-05-21
相关资源
最近更新 更多