attributes, properties, and accessors -- philosophy

Ethan Furman ethan at stoneleaf.us
Wed Nov 25 10:47:39 EST 2009


Bruno Desthuilliers wrote:
> Ethan Furman a écrit :
> 
>>
>> Let's head towards murkier waters (at least murkier to me -- hopefully 
>> they can be easily clarified):  some of the attributes are read-only, 
>> such as record count; others are not directly exposed, but still 
>> settable, such as table version; and still others require a small 
>> amount of processing... at which point do I switch from simple 
>> attribute access to method access?
> 
> 
> Short answer : you don't !-)
> 
> Long answer : well, in fact you do, but the client code doesn't have to 
> be aware that it's in fact calling an accessor.
> 
> Before we go into more details, you have to know that Python has a 
> pretty good support for computed attributes, with both a simple generic 
> solution (the property type) and the full monty (custom types 
> implementing the descriptor protocol). So from the "interface" POV, you 
> should never have an explicit accessor method for what is semantically 
> an attribute (wheter the attribute is a plain or a computed one being 
> part of the implementation).
> 
> Let's start with your second point: "not directly exposed but still 
> settable". I assume you mean "not part of the interface, only supposed 
> to be accessed (rw) from the methods" - if not, please pardon my 
> stupidity and provide better explanations !-).

Better explanation: attribute is publicly available, but buried a couple 
layers deep in a private structure (yes, private structure name starts 
with a leading underscore).

> If yes: Python doesn't 
> have "language inforced" access restrictions (private / protected / 
> etc), but a *very strong* naming convention which is that names starting 
> with a leading underscore are implementation details, not part of the 
> official interface, and shouldn't be accessed directly. Kind of a 
> "warranty voided if unsealed".
> 
> So if you have attributes you don't want to "expose" to the outside 
> world, just add a single leading underscore to their names.
> 
> First and third points are solved by using computed attributes - usually 
> a property. The property type takes a few accessor functions as 
> arguments - typically, a getter and a setter, and eventually a 
> "deleter". Used as a class attribute, a property instance will hook up 
> into the attribute lookup / setup mechanism (__getattribute__ and 
> __setattr__), and will call resp. it's getter or setter function, 
> passing it the instance and (for the setter) value.
> 
> This directly solves the third point. For the first one, the obvious 
> solution is to use a property with a setter that raises an exception - 
> canonically, an AttributeError with a message explaining that the 
> attribute is read-only.
> 
> And for something more hands-on:
> 
> class Person(object):
>    def __init__(self, firstname, lastname, birthdate):
>        self.firstname = firstname
>        self.lastname = lastnale
>        self.birthdate = birthdate
>        self._foo = 42 # implementation only
> 
>    def _getfullname(self):
>        return "%s %s" % (self.firstname, self.lastname)
>    def _setfullname(self, value):
>        raise AttributeError("%s.fullname is read-only" % type(self)
>    fullname = property(fget=_getfullname, fset=_setfullname)
> 
>    def _getage(self):
>        return some_computation_with(self.birthdate)
>    def _setage(self, value):
>        raise AttributeError("%s.age is read-only" % type(self)
>    age = property(fget=_getage, fset=_setage)
> 
> 
> For more on computed attributes, you may want to read about the 
> "descriptor protocol" (google is your friend as usual). This and the 
> attribute resolution mechanism are fundamental parts of Python's inner 
> working. Learn how it works if you really want to leverage Python's power.
> 
> HTH

Very helpful, thank you.  Hopefully my brain will be up to the 
descriptor protocol this time... the last couple times were, um, less 
than successful.  :)

~Ethan~



More information about the Python-list mailing list