J
John Posner
John Posner a écrit :... I was thinking
today about "doing a Bruno", and producing similar pieces on:
* properties created with the @property decorator
* the descriptor protocol
I'll try to produce something over the next couple of days.
Starting to think about a writeup on Python properties, I've
discovered that the official Glossary [1] lacks an entry for
"property" -- it's missing in both Py2 and Py3!
Here's a somewhat long-winded definition -- comments, please:
---------------------------------------------
An attribute, *a*, of an object, *obj*, is said to be implemented as a
property if the standard ways of accessing the attribute:
* evaluation: print obj.a
* assignment: obj.a = 42
* deletion: del obj.a
... cause methods of a user-defined *property object* to be invoked.
Hmmm... a couple remarks:
1/ "property" is actually the name of a Python builtin type. It's also
pretty much used in general OO litterature for what we name
"attributes". So I think it would be better to avoid confusion between
"property" as the builtin type, "property" as synonym for attribute, and
"property" as the more specific concept of "computed attribute" - which
is what you're describing here.
As far as I'm concerned, I prefer to stick to "computed attribute" for
the generic case, and only use "property" when the computed attribute is
actually implemented using the builtin property type.
Yes, I had already decided that the first sentence of the glossary
definition needs to be, "A property is a computed attribute".
2/ depending on how the computed attribute is implemented, the
computation needs not happen on ALL get/set/del access - you can have
non-binding descriptors (that is, not implementing __set__).
Yes, but IMHO that information belongs in the full writeup, not in the
glossary definition. I've added the phrase "some or all" in the glossary
definition (see below).
Also, the "standard" access also include getattr(), setattr() and
delattr() (might be worth a note).
/attribute/user-defined object/ here ?
Yes you can, and it's even actually pretty common:
Of course -- and I realize that I was introducing confusion, rather than
enlightenment, with my example. I think the point is too subtle for a
(necessarily short) glossary definition, so I'm removing the example
from the definition.
# example.py
from somewhere import RGBColor
class Foo(object):
def _get_color(self):
return str(self._color)
def _set_color(self, val):
self._color = RGBColor.from_string(val)
color = property(fget=_get_color, fset=_set_color)
def __init__(self, colorvalue):
self.color = colorvalue
[OFF-TOPIC] Wow, Thunderbird 3.0.2 nuked the indentation in the code
above. :-(
It's actually a type, not a function.
Ah, yes. Thanks.
The "property object" IS an instance of a class that implements the
descriptor protocol. The property type is just a "generic" descriptor:
# naive (and incomplete) python implementation of the property type
class property(object):
def __init__(self, fget, fset=None, fdel=None)
self._fget = fget
self._fset = fset
self._fdel = fdel
def __get__(self, instance, cls):
if instance is None:
return self
return self._fget(instance)
def __set__(self, instance, value):
if not self._fset:
raise AttributeError("can't set attribute")
self._fset(instance, value)
def __del__(self):
if not self._fdel:
raise AttributeError("can't delete attribute")
self._fdel(instance)
Good stuff for the full writeup on properties.
As far as I'm concerned, I'd "plan" such a paper as:
"""
What's a property ? It's a computed attribute implemented using the
builtin "property" type.
Ok, so far it doesn't help much. So
1/ what's a computed attribute, and
2/ what is the property type ?
1/ your above explanation about what's a computed attribute in general,
then a brief explanation of how computed attributes are implemented in
python -> IOW, the descriptor protocol
2/ my above snippet !-)
I agree. I'm not sure whether the "brief explanation of how computed
attributes are implemented in Python" belongs in the *properties*
writeup or the *descriptors* writeup. (I think they should be separate,
to avoid making readers brains explode.) I'll make the (initial) call
when/if I get that far!
"""
I think the way you started explaining computed attributes wrt/
attribute access could be a pretty good way to explain the descriptor
protocol, since the mapping from get/set/del access to __get__, __set__,
and __del__ magic methods is then pretty obvious.
But YMMV of course, so by all mean feel free to discard all or parts of
the above remarks !-)
HTH
Here's my leaner, meaner glossary definition of *property*:
-------------------
A property is a computed attribute: an attribute, a, of an object, obj,
is said to be implemented as a property if some or all of the standard
ways of accessing the attribute:
* evaluation: result = obj.a
* assignment: obj.a = 42
* deletion: del obj.a
.... cause methods of another user-defined object to be invoked. This
other object can be an instance of the built-in type *property*, or as
an instance of a class that implements the descriptor protocol.