How do I do this without class methods ?

Jacek Generowicz jmg at ecs.soton.ac.uk
Wed May 2 10:57:30 EDT 2001


Jacek Generowicz <jmg at ecs.soton.ac.uk> writes:

> (Full version performing to spec. below . . .)
> 
> -----------------------------------------------------------
> 
> class woderwick:
>     bwian = [ 'zero', 'one', 'two', 'many' ]
>     _wodger_index = 0 # default
>     def __getattr__ ( self, name ):
>         if name == 'wodger':
>             return self.bwian[self._wodger_index]

What should I do if the operation which is to be performed here is
very expensive, and hence should only be performed once and cached for
future use ?

>         raise AttributeError, name
>     def __init__ ( self ):
>         print self.wodger
> 
> def welease_bwian( index ):
>     woderwick._wodger_index = index
> 
> class rodrigo(woderwick):
>     bwian = [ 'cero', 'uno', 'dos', 'demasiados' ]
> 
> class roderich(woderwick):
>     bwian = [ 'gar nichts', 'eins', 'zwei', 'viele' ]  
> 
> class roderik(roderich): # :-)
>     bwian = [ 'geen bal', 'een', 'twee', 'te veel' ]
> 
> class rafal(woderwick):
>     bwian = [ 'figa z makiem', 'raz', 'dwa', 'kupa' ]
>     
> # Defaults work
> w = woderwick() # zero
> r = rodrigo() # cero
> 
> # Change number for all present and future instances
> welease_bwian( 1 )
> 
> print w.wodger # one
> woderwick() # one
> 
> print r.wodger # uno
> rodrigo() # uno 


I've invented a different example, which more closely reflects what I
need. Unfortuanately, with all the alternatives and comments, it has
turned out a little longer that I would like . . . sorry.


import math

# (A hierarchy of) objects whose behaviour changes as a function of a
# globally settable parameter
class polygon:
    def __init__ ( self, size ):
        self.radius = size
        
    # Called once for each parameter setting, used by all instances
    # Hence it would seem natural to implement it as a CLASS method
    def set_class_cache ( self, N ):
        # Imagine that this is an expensive operation, and hence
        # should only be called once for a given choice of N
        # Consequently, I can't use the trick of using __getattr__ to
        # calculate the value each time.
        self.__class__.unit_area = N * math.sin( 2 * math.pi / N ) 
        
    # Called once for each instance, at a particular parameter setting
    def set_instance_cache ( self ):
        # Imagine that this is expensive too;
        # so calculate it only once for each instance
        # after the paremeter has been set
        self.area = self.radius * self.unit_area 

    # Called frequently for any particular instance
    def use_instance ( self, coefficient ):
        # Called many times for each instance
        # should use the instance's cached value
        return self.area * coefficient


# Would like to invoke like this (this does NOT work)
def set_parameter_ideal ( klass, N ):
    klass.set_class_cache( N )

# Horrible but functioning solution 1: dummy instance
def set_parameter_horrible ( klass, N ):
    dummy = klass( 1 )
    dummy.set_class_cache( N )

# Horrible but functioning solution 2:
# Implement set_class_cache outside the class
# I really hate this `hacking in polymorphism by hand'.
def set_class_cache_polygon( N ):
    polygon.unit_area = N * math.sin( 2 * math.pi / N )
# Imagine there is a class hierarchy here, so I would have to have
# def set_class_cache_wallygon( N ):
# def set_class_cache_schmolygon( N ):
# def set_class_cache_PollyGone( N ):  # Dead Parrot
# etc. etc. . . .


# Almost elegant but horribly slow solution: getattr
# Slow because unit_area is recalculated repeatedly
# but (almost) never changes
# Inelegant, because this is a daft place to put the calculation of
# unit_area
def ga ( object, name ):
    if name == 'unit_area':    
        return  object.N * math.sin( 2 * math.pi / object.N ) 
# Normally, this would be defined in the class, but it is done like
# this to avoid clashes with the other solutions
polygon.__getattr__ = ga

def set_parameter_getattr( klass, N ):
    klass.N = N


# Trivial example of usage
def use_polygons ( coeff ):
    # Create some instances
    lots_of_instances = map( polygon, range( 1,3 ) )
    # Prepare instances for use, by setting the caches
    for instance in lots_of_instances:
        instance.set_instance_cache()
    # Use the instances
    for instance in lots_of_instances:
        print instance.use_instance( coeff )
    print


# Use the various solutions
set_parameter_getattr( polygon, 3 )
use_polygons( 1 )

set_class_cache_polygon( 4 )
use_polygons( 2 )

set_parameter_horrible( polygon, 3 )
use_polygons( 1 )

# Alas, this does not work
set_parameter_ideal( polygon, 3 )
use_polygons( 2 )


Class methods would make this all so simple :-)

Any edifying comments or suggestions ?

Thanks,

Jacek



More information about the Python-list mailing list