python decorator to modify variable in current scope

decorator, python, scope

Solution

You can do what you want with a class decorator (in Python 2.6) or a metaclass. The class decorator version:

def rule(f):
    f.rule = True
    return f

def getRules(cls):
    cls.rules = {}
    for attr, value in cls.__dict__.iteritems():
        if getattr(value, 'rule', False):
            cls.rules[attr] = value
    return cls

@getRules
class RuleClass:
    @rule
    def foo(self):
        pass

The metaclass version would be:

def rule(f):
    f.rule = True
    return f

class RuleType(type):
    def __init__(self, name, bases, attrs):
        self.rules = {}
        for attr, value in attrs.iteritems():
            if getattr(value, 'rule', False):
                self.rules[attr] = value
        super(RuleType, self).__init__(name, bases, attrs)

class RuleBase(object):
    __metaclass__ = RuleType

class RuleClass(RuleBase):
    @rule
    def foo(self):
        pass

Notice that neither of these do what you ask for (modify the calling namespace) because it's fragile, hard and often impossible. Instead they both post-process the class -- through the class decorator or the metaclass's `__init__` method -- by inspecting all the attributes and filling the `rules` attribute. The difference between the two is that the metaclass solution works in Python 2.5 and earlier (down to 2.2), and that the metaclass is inherited. With the decorator, subclasses have to each apply the decorator individually (if they want to set the rules attribute.)

Both solutions do not take inheritance into account -- they don't look at the parent class when looking for methods marked as rules, nor do they look at the parent class `rules` attribute. It's not hard to extend either to do that, if that's what you want.

Problem

Goal: Make a decorator which can modify the scope that it is used in. If it worked: ``` class Blah(): # or perhaps class Blah(ParentClassWhichMakesThisPossible) def one(self): pass @decorated def two(self): pass >>> Blah.decorated ["two"] ``` Why? I essentially want to write classes which can maintain specific dictionaries of methods, so that I can retrieve lists of available methods of different types on a per class basis. errr..... I want to do this: ``` class RuleClass(ParentClass): @rule def blah(self): pass @rule def kapow(self): pass def shazam(self): class OtherRuleClass(ParentClass): @rule def foo(self): pass def bar(self): pass >>> RuleClass.rules.keys() ["blah", "kapow"] >>> OtherRuleClass.rules.keys() ["foo"] ```

Original source