【问题标题】:Attempting to create a read-only property attribute - getter returns initialized value, direct access returns changed value尝试创建只读属性属性 - getter 返回初始化值,直接访问返回更改值
【发布时间】:2013-07-01 20:28:15
【问题描述】:

虽然我可能对property() 函数的作用感到非常困惑,但我正在尝试使用它来创建一个只读属性。理想情况下,我希望能够直接引用该属性,但不允许分配给它。在实验时,我得到了这个非常奇怪的行为:

>>> class Boo(object):
...     def __init__(self, x):
...             self.__x = x
...     def getx(self):
...             return self.__x
...     x = property(getx)
... 
>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.__x
2

我想补充一点,当我使用x_x 作为属性名称时,重新分配属性会导致getter 返回更改后的值,即b.getx()b.x/b._x给了我2

我意识到我使用x 作为属性名称,但是当我尝试以下操作时,我在__init__() 中得到了一个AttributeError

>>> class Boo(object):
...     def __init__(self, x):
...             self.__x = x
...     def getx(self):
...             return self.__x
...     __x = property(getx)
... 
>>> b = Boo(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
AttributeError: can't set attribute

【问题讨论】:

  • 我想补充一点,我并没有试图获得一个真正的私有变量,只是一个在分配后无法修改的变量。 (我意识到我不能吃蛋糕也不能吃 - Python 不是 Java 等等。只是好奇property() 的行为以及我能走多远。)

标签: python oop python-2.7 properties


【解决方案1】:

这里的问题与property无关,而是双下划线属性,受制于所谓的"Private name mangling"

>>> b = Boo(1)
>>> '__x' in dir(b)
False
>>> '_Boo__x' in dir(b)
True

所以,当你这样做时:

>>> b.__x = 2

您没有更改 getx 函数正在查看的属性的值,您正在创建一个 new 属性。

如果您只是使用不以两个下划线开头的属性名称(例如_x),那么一切都会按您的预期进行。

作为一般规则,“advisory private”使用单下划线——例如,“这个对象的用户可能不应该关心这个值”,只有当你真正需要修改时才使用双下划线(因为复杂很少出现的继承问题)。

如果您想要“真正的私有”,比如 C++ 或 Java,该怎么办?你不能拥有它。如果您足够好地隐藏或保护该属性,则有人会猴子修补getx 方法或x 属性。因此,Python 没有提供隐藏属性的方法。

【讨论】:

  • 啊!这就是我听说的那个名字修饰的东西。我没有意识到它是如此的字面意思。在做了b._Boo__x = 2 b.x 之后给了我2。 (另外,我没想过把这个东西称为x,但你的回答让我很好奇。)
  • @2rs2ts:没错。经验法则是:除非您想修改名称,否则切勿使用双下划线名称。如果你不理解名字修饰,你永远不会想要它。一旦你理解了名字修饰,你想要它几天只是为了好玩,然后再也不使用它了。 :)
  • 作为对您上次编辑的回应 - 我想我想要的是将该属性称为x,并且不允许对其进行更改。我现在明白这里实际上有两个属性......
  • 不可能“不允许对其进行更改”。即使您设法将真实属性隐藏在某个地方,也有人可以替换您的 x 属性或它所依赖的 getx 方法。
  • @2rs2ts:没错。在您的第二个版本中,当类定义完成时,Boo.__dict__['__x'] 是属性,因此当您尝试在实例中分配给 self.__x 时,它会尝试调用该属性的 __set__,这会引发该错误。
【解决方案2】:

您的问题是使用双下划线属性名称​​会破坏名称。因此,当您在类定义内部处理__x 时,在类外部它实际上看起来像_Boo__x。也就是说,

_+(类名)+(双下划线属性名)

为了演示,

>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.x            # NOTE: same as calling getx
1
>>> b.__x          # why didn't x return 2 if we changed it?
2
>>> b._Boo__x      # because it's actually saved in this attribute
1
>>> b._Boo__x = 3  # setting it here then works
>>> b.x
3
>>> b.getx()
3

【讨论】:

    【解决方案3】:

    真的只是想评论(而不是回答)您的问题。我想你会发现以下信息:

    >>> b = Boo(1)
    >>> print b.__dict__
    {'_Boo__X': 1}
    >>> b.__x = 2
    >>> print b.__dict__
    {'__x': 2, '_Boo__X': 1}
    

    可能会提供有关行为的提示(我理解得不够好,无法解释)。

    【讨论】:

      【解决方案4】:

      这是因为你没有定义 setx 函数。

      #!/usr/bin/python
      
      class Boo(object):
      
              def __init__(self, initialize_x):
                      self.x = initialize_x
      
              def getx(self):
                      print
                      print '\t\tgetx: returning the value of x', self.__x
                      print
                      return self.__x
      
              def setx(self, new_x):
                      print
                      print '\t\tsetx: setting x to new value',new_x
                      print
                      self.__x = new_x
      
              x = property(getx, setx)
      
      print '1  ########'
      print
      print '\tinitializing Boo object with a default x of 20'
      print
      o = Boo(20)
      print '\treading the value of x through property o.x'
      t = o.x
      print
      
      print '2  ########'
      print
      print '\tsetting x\'s value through the property o.x'
      o.x = 100
      print
      
      
      print '3  ########'
      print
      print '\treading the value of x through the property o.x'
      t = o.x
      

      运行时产生:

      1  ########
      
          initializing Boo object with a default x of 20
      
      
              setx: setting x to new value 20
      
          reading the value of x through property o.x
      
              getx: returning the value of x 20
      
      
      2  ########
      
          setting x's value through the property o.x
      
              setx: setting x to new value 100
      
      
      3  ########
      
          reading the value of x through the property o.x
      
              getx: returning the value of x 100
      

      【讨论】:

      • 我认为setx 违背了我拥有只读属性的目的,但我想我可以让这样的函数引发异常以帮助强调只读行为。
      • 虽然我明白为什么没有setx 会影响分配风格。
      【解决方案5】:

      我使用property 作为装饰器。方便计算数据。

      可能是为了使只读属性更好地使用魔术函数作为__set____get__

      【讨论】:

      • 这没有回答问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多