为什么property.fget是只读属性?

7 投票
2 回答
2097 浏览
提问于 2025-04-16 16:18

我现在正在修改一个库里类的属性,让它变得更灵活。

我用下面的代码来实现这个功能,效果很好:

_orig_is_xhr = BaseRequest.is_xhr.fget
_orig_is_xhr_doc = BaseRequest.is_xhr.__doc__
BaseRequest.is_xhr = property(lambda self: _orig_is_xhr(self) or
    '_iframe-xhr' in request.form, doc=_orig_is_xhr_doc)

不过,如果我能直接重写获取这个属性的函数,那就更好了,这样文档说明也能保留下来:

_orig_is_xhr = BaseRequest.is_xhr.fget
BaseRequest.is_xhr.fget = lambda self: (_orig_is_xhr(self) or
    '_iframe-xhr' in request.form)

但是这样做不行,因为 property.fget 是一个只读属性(尝试给它赋值时会报错:TypeError: readonly attribute)。我很好奇这样设计是否有特别的原因,还是说 Python 的开发者觉得在创建属性后再去修改它没有意义,除非用一个新的属性来替换。

2 个回答

1

在IDLE环境中测试了Anaconda 2.3.0(Python 3.4.3)

>>> p = property()
>>> p.fget
>>> p.__init__( lambda obj: None )
>>> p.fget
<function <lambda> at 0x0121FF18>
>>> p.fget = lambda obj: None
Tracebabk (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    p.fget = lambda obj: None
AttributeError: readonly attribute
>>>

在我看来,这看起来并不是只读的哦;)

6

你说得对,这些属性被设定为只读其实是一种约定,目的是让这个属性要么有值,要么没有值,不能部分有值。其实如果能在之后再给这些属性赋值,可能会更符合“Python风格”,但我在Python 2.2的发布说明中找不到相关的理由(那时候引入了属性这个概念)。

Objects/descrobject.c文件中,这个属性的成员属性被定义为只读:

    static PyMemberDef property_members[] = {
        {"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY},
        {"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
        {"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
        {"__doc__",  T_OBJECT, offsetof(propertyobject, prop_doc), READONLY},
        {0}
    };

顺便说一下,如果你把READONLY替换成0并重新编译,就可以让fget, fset, ..这些属性被赋值了:

class Test(object):
    def __init__(self):
        self.flag = True
    prop = property(lambda self: self.flag)

obj = Test()
print obj.prop
Test.prop.fget = lambda self: not self.flag
print obj.prop

输出:

True
False

撰写回答