7

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__
BaseRequest.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.

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636

2 Answers2

6

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
samplebias
  • 37,113
  • 6
  • 107
  • 103
1

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 ;)

Tcll
  • 7,140
  • 1
  • 20
  • 23