pythonscopedecoratorpython-nonlocal

How to access nonlocal scope inside class definition in python?


I want to do this (dummy example):

def func():
    nonlocal var
    print (var)

class A:
    var = 'hola'
    func()

But I get: "SyntaxError: no binding for nonlocal 'var' found"

What I really intend to do is append a method name to a list in the scope of the class if that method is decorated. Something like this:

def decorator(func):
    nonlocal decorated
    decorated.append(func.__name__)
    return func

class A:
    decorated = []
    @decorate
    def f(self):
        pass

Solution

  • Python just doesn't let you do this. You can access the class namespace by using locals(). But at this point, you might as well just pass the variable you're interested in to the decorator.

    # using locals()
    
    def decorator(class_namespace):
        def _decorator(func):
            class_namespace["decorated"].append(func)
            return func
        return _decorator
    
    class A:
        store = decorator(locals())
    
        decorated = []
    
        @store
        def func(self):
            pass
    
        del store
    

    Generally, it's easy to use a pair of decorators. One to mark the functions you're interested in, and one to collect them.

    from types import FunctionType
    
    def collect(cls):
        for item in vars(cls).values():
            print(item)
            if isinstance(item, FunctionType) and getattr(item, "marked", False):
                cls.marked_funcs.append(item)
        return cls
    
    def mark(func):
        func.marked = True
        return func
    
    @collect
    class B:
        marked_funcs = []
    
        @mark
        def func(self):
            pass
    

    But in your case it might just be simpler to create the set of function names at the end of the class. eg.

    class C:
        def func(self):
            pass
    
        func_names = [f.__name__ for f in [func]]