1

I'm trying to do a sort of type handling function registration, using this code:

types = {}
def type_handler(name):
    def wrapper(f):
        types[name] = f
        return f
    return wrapper

@type_handler('a')
def handle_a(a):
    ...

@type_handler('b'):
def handle_b(b):
    ...

def handle(x):
    types[x.name](x)

This works fine, but now I want it to work inside a class.
I tried this:

class MyClass(object):

    types = {}
    def type_handler(name):
        def wrapper(f):
            types[name] = f ## global name 'types' is undefined
            return f
        return wrapper

    @type_handler('a')
    def handle_a(self, a):
        ...

    @type_handler('b'):
    def handle_b(self, b):
        ...

    def handle(self, x):
        self.types[x.name](self, x)

But it says global name 'types' is undefined.
I tried changing it to

    def type_handler(name):
        def wrapper(f):
            MyClass.types[name] = f ## global name 'MyClass' is undefined
            return f
        return wrapper

But now it says global name 'MyClass' is undefined.
What can I do to make this work?

I know I can do something like:

def handle(self, x):
    self.__getattribute__('handle_%s' % x.name)(self, x)

But I prefer function registration rather name based lookup.

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • 1
    The reason for the behaviour you are seeing is that class scopes are skipped during name lookup – see [The scope of names defined in class block doesn't extend to the methods' blocks. Why is that?](http://stackoverflow.com/questions/9505979/the-scope-of-names-defined-in-class-block-doesnt-extend-to-the-methods-blocks) for further explanations. – Sven Marnach Aug 10 '12 at 10:40
  • 1
    There are several ways to fix this, but I can't really advice on a good way without knowing what the context of this decorator is. My general recommendation is to use less magic. – Sven Marnach Aug 10 '12 at 10:41

1 Answers1

3

I agree with Sven Marnach. You should use "less magic". However, here is a quick fix:

#decorator is declared outside of class and type dict is passed in as an argument
def type_handler(name, type_dict):
    def wrapper(f):
        type_dict[name] = f
        return f
    return wrapper

class MyClass(object):

    types = {}

    @type_handler('a', types)
    def foo_a(self, a):
        pass

    @type_handler('b', types)
    def foo_b(self, b):
        pass

VERSION 2

This one uses a class as a decorator:

class TypeHandler(object):
    def __init__(self, type_dict):
        self.types = type_dict

    def __call__(self, name):
        def wrapper(f):
            self.types[name] = f
            return f
        return wrapper


class MyClass(object):
    types = {}
    thandle = TypeHandler(types)

    @thandle('a')
    def foo_a(self, a):
        pass
Joel Cornett
  • 24,192
  • 9
  • 66
  • 88