New PEP: Attribute Access Handlers
Christian Tanzer
tanzer at swing.co.at
Sun Jul 23 03:56:08 EDT 2000
> Paul Prescod <paul at prescod.net> wrote:
>
> http://python.sourceforge.net/peps/pep-0213.html
>
> PEP: 213
> Title: Attribute Access Handlers
> Version: $Revision: 1.3 $
> Owner: paul at prescod.net (Paul Prescod)
> Python-Version: 2.0
> Status: Incomplete
(snip)
> Current Solution
>
> To make some attributes read-only:
>
> class foo:
> def __setattr__( self, name, val ):
> if name=="readonlyattr":
> raise TypeError
> elif name=="readonlyattr2":
> raise TypeError
> ...
> else:
> self.__dict__["name"]=val
>
> This has the following problems:
>
> 1. The creator of the method must be intimately aware of whether
> somewhere else in the class hiearchy __setattr__ has also been
> trapped for any particular purpose. If so, she must specifically
> call that method rather than assigning to the dictionary. There
> are many different reasons to overload __setattr__ so there is a
> decent potential for clashes. For instance object database
> implementations often overload setattr for an entirely unrelated
> purpose.
>
> 2. The string-based switch statement forces all attribute handlers
> to be specified in one place in the code. They may then dispatch
> to task-specific methods (for modularity) but this could cause
> performance problems.
>
> 3. Logic for the setting, getting and deleting must live in
> __getattr__, __setattr__ and __delattr__. Once again, this can
> be mitigated through an extra level of method call but this is
> inefficient.
>
> Proposed Syntax
>
> Special methods should declare themselves with definitions of the
> following form:
>
> class x:
> def __attr_XXX__(self, op, val ):
> if op=="get":
> return someComputedValue(self.internal)
> elif op=="set":
> self.internal=someComputedValue(val)
> elif op=="del":
> del self.internal
(snip)
I agree that 1. and 2. are sometimes problems, but IMHO point 3. is a
good thing, not a problem.
In general, get/set/del are not related. For instance:
$ dir ~/ttt/ttptools/*/*.py | wc -l
426
$ grep ' def __setattr__' ~/ttt/ttptools/*/*.py | wc -l
5
$ grep ' def __getattr__' ~/ttt/ttptools/*/*.py | wc -l
44
$ grep ' def __delattr__' ~/ttt/ttptools/*/*.py | wc -l
0
In short, folding get/set/del into one function sucks. Besides, it
doesn't really help when you want to overload just get, but would like
to keep the set/del of the ancestor.
To disentangle the accessors of different attributes, I would suggest
to use a dictionary of attribute-specific accessor functions.
For instance:
class Foo :
def _set_bar (self, name, value) : ...
def _set_baz (self, name, value) : ...
def _dont_set (self, name, value) : raise Readonly_Attribute, name
__attr_setters__ = { "bar" : _set_bar
, "baz" : _set_baz
, "ro1" : _dont_set
, "ro2" : _dont_set
}
def __setattr__ (self, name, value):
if self.__attr_setters__.has_key (name) :
self.__attr_setters__ [name] (self, name, value)
else :
...
class Bar (Foo) :
def _set_baz (self, name, value) : ...
def dict (* dicts, ** kw) :
result = {}
for d in dicts + (kw, ) : result.update (d)
return result
class Baz (Bar) :
__attr_setters__ = dict (Bar.__attr_setters__, baz = Bar._dont_set)
This tackles problem 1 and half of problem 2.
If the performance side of problem 2 is really an issue, move the
checking of `__attr_setters__/__attr_getters__/__attr_deleters__' from
`__setattr__/__getattr__/__delattr__' into the interpreter (other
names for the magic dictionaries might be useful then).
--
Christian Tanzer tanzer at swing.co.at
Glasauergasse 32 Tel: +43 1 876 62 36
A-1130 Vienna, Austria Fax: +43 1 877 66 92
More information about the Python-list
mailing list