"properties" idiom

Alex Martelli aleax at aleax.it
Thu Nov 21 05:49:26 EST 2002


Terry Hancock wrote:

> I could swear I saw a conversation about this on list, but I'm having
> trouble finding anything relevant.
> 
> Let's say I have a class that exposes an attribute  x as part of it's
> public API:
> 
> class spam:
>     def __init__(self, x):
>         self.x = x
> 
> Due to later development, however, it's clear (hypothetically)
> that setting or getting the value of x, requires a method call
> (to update a database, for example).

Perfectly right, and a frequent occurrence: you are designing a
class, thus implicitly an interface that the class 'publishes' to
other code using it ('client code'); right now the implementation
naturally suggests making attribute 'x' a client-code-accessible
data attribute, but you want to reserve the option to change
things in the future so that client code accessing to setting x
causes some method to execute.

That's exactly motivation number one for the "property" concept: it
lets you expose your attributes WITHOUT worrying that this will
restrict your options in the future, so you never have to design
boilerplace accessor and mutator methods just to keep flexibility.


> Is there a standard idiom for calling that method when an outside
> caller does something like:
> 
> spaminstance.x = 42
> 
> or
> 
> y = spaminstance.x

Yes: there are in fact two of them.  One works in every version of
Python since at least 1.5.2 (earlier I believe) and with every kind
of Python class, but is a bit clunky.  The other standard works
only with Python 2.2 and later and with "new-style" classes, but
is slicker.

The classic approach hinges on methods __setattr__ and __getattr__.

If your class C defines a C.__setattr__, then ANY setting of an
attribute of an instance I of C actually calls __setattr__ instead:
    I.x = 42
actually becomes
    I.__setattr__('x', 42)

You must code your __setattr__ method to deal with ALL attribute
settings appropriately, e.g. via self.__dict__[name] = value when
you want to treat such a setting of attribute name to value in
the "normal/usual" way -- that's what makes the approach a bit
clunky.  There is no distinction between code "inside" vs "outside"
the class.  __getattr__ is different -- it's only called when the
NORMAL ways to look for an attribute (in the object's dictionary,
in the object's class, in the bases of said class) have failed --
so it's really quite a bit handier.  But exactly because it's only
called for otherwise-failing lookups, this gives constraints:
  -- __getattr__ must raise AttributeError for attributes you
     don't want to appear to be there
  -- you must keep the actual value of the attribute somewhere
     the normal search process won't find it, so __getattr__ gets
     a chance -- popular approaches are [a] using attribute '_x'
     to hold the value of what's accessed from outside as 'x', or
     [b] having _another_ per-instance dict besides self.__dict__
     and using the OTHER dict to keep attributes such that attempts
     to access them must go through __getattr__

So, all in all, it's quite feasible, and enough to remove worries
in interface-design work, but when called upon to operate, it's
a bit clunky.  Which is why, in 2.2 and only for new-style classes,
the alternative 'property' built-in was introduced -- I see others
have already covered that, so I won't repeat it.

My suggestion: use 'property' when you possibly can, but don't
worry, since if for any reason it's an old-style class you need,
or in order to keep compatibility with old Python versions, you
can get the same effect in whatever kind of class and whatever
version of Python, albeit with more work and less speed.


> (i.e. when we see this, my class actually calls an internal method,
> spaminstance.__set_x(42)).  If so, I'd have the same flexibility as if I
> had used get_x()/set_x() accessor methods, but my code would
> be simpler for the most common case.  Can you do this with Python
> today?  I was thinking there might be a way using __getattr__ and
> __setattr__, but it initially looks very complex -- is there a preferred
> way to do it?

Right on target on both scores.


> I'm defining an interface now, and I'm wondering whether I should
> go with attributes or accessor-methods.  I've been using accessor
> methods, but it bloats the interface quite a bit, and I'm wondering if I
> can make the interface simpler this way.

And here, too.  Cluttering your class with accessors is quite
unPythonic.  Use properties and you'll be much happier!


Alex




More information about the Python-list mailing list