【问题标题】:How to access nonlocal scope inside class definition in python?如何在python中访问类定义中的非本地范围?
【发布时间】:2016-08-01 23:14:21
【问题描述】:

我想这样做(虚拟示例):

def func():
    nonlocal var
    print (var)

class A:
    var = 'hola'
    func()

但我得到:“SyntaxError: no binding for nonlocal 'var' found”

如果该方法被修饰,我真正打算做的是将方法名称附加到类范围内的列表中。像这样的:

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

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

【问题讨论】:

  • nonlocal 查看函数定义的范围,而不是函数调用。有多种方法可以查看函数调用的范围,但通常建议尽可能避免使用它们。尝试将列表作为参数提供给装饰器,或使用类装饰器对类进行后处理并设置decorated 列表。

标签: python scope decorator python-nonlocal


【解决方案1】:

Python 就是不允许你这样做。您可以使用locals() 访问类命名空间。但是此时,您不妨将您感兴趣的变量传递给装饰器。

# 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

通常,使用一对装饰器很容易。一个用来标记你感兴趣的功能,一个用来收集它们。

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

但在您的情况下,在类的末尾创建一组函数名称可能会更简单。例如。

class C:
    def func(self):
        pass

    func_names = [f.__name__ for f in [func]]

【讨论】:

    【解决方案2】:

    使用装饰器标记函数,然后装饰成一个类方法,返回所有被装饰的函数。

    import inspect
    
    def decorate(func):
        func.decorated = True
        return func
    
    class A:
        def foo():
            print('foo')
    
        @decorate
        def bar():
            print('bar')
    
        @classmethod
        def decorated(cls):
            def is_decorated_method(obj):
                try:
                    return inspect.isfunction(obj) and obj.decorated
                except AttributeError:
                    # The object has no decorated attribute
                    return False
    
            return [result[1] for result in inspect.getmembers(cls, predicate=is_decorated_method)]
    
    print(A.decorated())
    # [<function A.bar at 0x6ffffc0aa60>]
    

    【讨论】:

      【解决方案3】:
      y = 20
      def f():
          x = 7
          def g():
              nonlocal x # 7
              global y # 20
      

      nonlocal 限定符是指外部函数范围内的名称,不包括模块范围。而global 是互补的。所以你错误地使用了nonlocal

      【讨论】:

        【解决方案4】:

        怎么样?

        decorated = []
        
        def decorator(func):
            decorated.append(func.__name__)
            def wrapper(self):
                print('wrapper called')
                func(self)
            return wrapper
        
        class A:
            @decorator
            def f(self): print('function called')
        
        print(decorated)
        A().f()
        

        输出:

        ['f']
        wrapper called
        function called
        

        注意事项:

        您提供的代码遇到了您所描述的问题,因为 var 是类变量,因此必须将其引用为 A.var 但您不能在代码中这样做,因为您尝试在定义 A 之前使用它.如果它是不同的类,那将是可能的:

        class X:
            decorated = []
        
        def decorator(func):
            X.decorated.append(func.__name__)
            return func
        
        class A:
        
            @decorator
            def f(self):
                pass
        
        print(X.decorated)
        

        请注意,如果您不分配给变量而是调用像append() 这样的方法,则不需要指定nonlocal

        【讨论】:

          猜你喜欢
          • 2010-10-10
          • 1970-01-01
          • 1970-01-01
          • 2011-07-24
          • 2014-04-05
          • 2012-01-15
          • 1970-01-01
          • 2021-03-28
          • 1970-01-01
          相关资源
          最近更新 更多