Access to static members from inside a method decorator?

Steve Holden steve at holdenweb.com
Thu Oct 5 20:01:54 CEST 2006


glen.coates.bigworld at gmail.com wrote:
> Bruno Desthuilliers wrote:
> 
>>glen.coates.bigworld at gmail.com wrote:
>>
>>>I'm developing a library at the moment that involves many classes, some
>>>of which have "exposed" capabilities.  I'm trying to design a nice
>>>interface for both exposing those capabilities, and inspecting
>>>instances to find out what capabilities they have.
>>>
>>>At the moment, I'm leaning towards a superclass (Exposed) that defines
>>>a static method which is a decorator (expose) such that any derived
>>>class can mark a method with @Exposed.expose and it will then be later
>>>returned by getExposedMethods(), a la:
>>>
>>>class Exposed:
>>>  @staticmethod
>>>  def expose( f ):
>>>    ...
>>>
>>>  def getExposedMethods( self ):
>>>    ...
>>>
>>>class Person( Exposed ):
>>>  @Exposed.expose
>>>  def talk( self, ... ):
>>>    ...
>>>
>>>I'm trying to implement the decorator by having it populate a static
>>>member list of whatever class it's in with a reference to the method.
>>>getExposedMethods() would then return the contents of each of those
>>>lists from itself back to Exposed in the class hierarchy.  The first
>>>problem was that having a reference to the method (i.e. talk()) does
>>>not allow you to get a reference to the enclosing class (I had hoped
>>>im_class would lead me there).
>>
>>Not yet. When your decorator is called, the class object is not yet
>>created, and what you are decorating is a plain function.
>>
>>
>>>The real hiccup was that explicitly
>>>passing the class as an argument to the decorator generates a undefined
>>>global name error, presumably because at that point of execution the
>>>class object hasn't been fully created/initialised.
>>
>>Exactly.
>>
>>
>>>So how can this be done?
>>
>>The simplest thing is to use a two-stages scheme : mark the functions as
>>exposed, then collect them:
>>
>>def expose(func):
>>  func._exposed = True
>>  return func
>>
>>def exposed(obj):
>>  return callable(obj) and getattr(obj, '_exposed', False)
>>
>>class Exposing(object):
>>  @classmethod
>>  def get_exposed_methods(cls):
>>    try:
>>      exposeds = cls._exposed_methods
>>    except AttributeError:
>>      exposeds = []
>>      for name in dir(cls):
>>        obj = getattr(cls, name)
>>        if exposed(obj):
>>          exposeds.append(obj)
>>      cls._exposed_methods = exposeds
>>    return exposeds
>>
>>class Parrot(Exposing):
>>  @expose
>>  def parrot(self, what):
>>    return "%s says %s" % (self, str(what))
>>
>>
>>
>>HTH
>>--
>>bruno desthuilliers
>>python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
>>p in 'onurb at xiludom.gro'.split('@')])"
> 
> 
> Thanks Bruno.  I came up with a similar solution today at work, which
> involves an 'init' method which is called at the bottom of each module
> that defines subclasses of Exposed and sets up static mappings for the
> exposed methods.  I guess my solution is slightly less elegant because
> it requires this ugly explicit init call outside the classes that it
> actually deals with, however it is more efficient because the dir()
> pass happens once on module load, instead of every time I want the list
> of exposed methods.
> 
Surely the right place to handle "collection" is in a metaclass, where 
the metaclass's __call__() method can scan the __dict__ and take 
appropriate action on the marked methods? That way it's done just once, 
at class definition time, as it should be.

regards
  Steve
-- 
Steve Holden       +44 150 684 7255  +1 800 494 3119
Holden Web LLC/Ltd          http://www.holdenweb.com
Skype: holdenweb       http://holdenweb.blogspot.com
Recent Ramblings     http://del.icio.us/steve.holden




More information about the Python-list mailing list