Why is property.fget a read-only attribute?
I'm currently patching a property of a class from a library to make it more versatile.
I'm doing this using the following code which works fine:
_orig_is_xhr = BaseRequest.is_xhr.fget
_orig_is_xhr_doc = BaseRequest.is_xhr.__doc__
Bas开发者_Go百科eRequest.is_xhr = property(lambda self: _orig_is_xhr(self) or
'_iframe-xhr' in request.form, doc=_orig_is_xhr_doc)
However, it would be much nicer if i could simply overwrite the getter function so the docstring is preserved:
_orig_is_xhr = BaseRequest.is_xhr.fget
BaseRequest.is_xhr.fget = lambda self: (_orig_is_xhr(self) or
'_iframe-xhr' in request.form)
This doesn't work because property.fget
is a read-only attribute (TypeError: readonly attribute
when trying to assign to it).
I'm curious if there is a special reason for this or it the python developers just thought it makes no sense to modify a property after creating it without replacing it with a new one.
You're probably right, that it's just a convention to make those attributes read-only, chosen to make the property "all-or-nothing". Seems it'd be a bit more "Pythonic" to allow these to be assigned after the fact, but can't find the rationale in the Python 2.2 release notes (when properties were introduced).
In Objects/descrobject.c the property's member attributes are defined as read-only:
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}
};
Aside: if you replace READONLY
with 0
and compile, that's all it takes to allow fget, fset, ..
to be assigned:
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
Output:
True
False
tested with Anaconda 2.3.0 (Python 3.4.3) in an IDLE shell
>>> 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
>>>
doesn't look so readonly to me ;)
精彩评论