[EMAIL PROTECTED] wrote: > Bruno Desthuilliers wrote: > >>[EMAIL PROTECTED] 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 '[EMAIL PROTECTED]'.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 -- http://mail.python.org/mailman/listinfo/python-list