[Python-Dev] __set_foo__ et al. (PEP-able)

Paul Prescod paul@prescod.net
Thu, 20 Jul 2000 16:18:08 -0500


"M.-A. Lemburg" wrote:
> 
> > def __setattr__( name, val ):
> >     if attribute_to_set=="__class__":
> >         assert PyClass_Check( val ):
> >     elif attribute_to_set=="__dict__":
> >         assert PyDict_Check( val ):
> 
> But that's just an implementation detail (you either do the
> lookup via a mapping, e.g. the instance dict, or by switching
> on the attribute name)... in C and with only a few cases, the
> switching technique is faster than the lookup.

That's not the case. In this case, Guido is doing type checking on sets.
You can only do that in Python with an __setattr__ hack.

> > It's just a cleaner, Pythonic way to do something that is already common
> > either using __getattr__ hacks or in extension modules.
> 
> It's also *much* slower: you'd have to check objects for
> e.g. obj.__set_foo__ prior to every obj.foo=1 assignment.

It's much, much faster for than __getattr__ for gets. It's probably no
slower than __setattr__ for sets because you aren't switching on the
attribute name in Python. Anyhow gets are much more common than sets so
speeding them up is a big win.

> I think that these techniques are better implemented by
> writing a special proxy which then does your proposed
> mapping for a predefined set of attributes.

Proxies are slow and introduce a host of their own "issues." In
practice, I've never seen anyone implement this pattern with proxies
unless the proxies were doing something else already (e.g. COM). They
always use a direct __getattr__/__setattr__.

> I usually use the direct attribute access method for
> information containers and the method access for more
> elaborate objects (ones which care about the data format,
> apply extra checks, depend on state, etc.). Very often
> these methods set more than just one attribute, so using
> __set_foo__ wouldn't work here.

Don't follow. Here's an example that:

 * cares about data format
 * applies extra checks
 * is state dependent
 * sets more than one attribute internally

   def __set_enddate__( self, val ):
        if len(val)==3 and val>self.start_date
           self.endday, self.endmonth, self.endyear=val
        else:
           raise TypeError

   def __get_enddate__(self, val):
        return self.endday, self.endmonth, self.endyear

   obj.enddate=(15, 12, 2000)
   print obj.endday
   print obj.endmonth
   print obj.endyear

   d,m,y=obj.enddate

-- 
 Paul Prescod - Not encumbered by corporate consensus
"Hardly anything more unwelcome can befall a scientific writer than 
having the foundations of his edifice shaken after the work is 
finished.  I have been placed in this position by a letter from 
Mr. Bertrand Russell..." 
 - Frege, Appendix of Basic Laws of Arithmetic (of Russell's Paradox)