如何访问 python 中 class 定义内的非本地范围?
How to access nonlocal scope inside class definition in python?
我想这样做(虚拟示例):
def func():
nonlocal var
print (var)
class A:
var = 'hola'
func()
但我得到:"SyntaxError: no binding for nonlocal 'var' found"
我真正想做的是将方法名称附加到 class 范围内的列表中(如果该方法已修饰)。像这样:
def decorator(func):
nonlocal decorated
decorated.append(func.__name__)
return func
class A:
decorated = []
@decorate
def f(self):
pass
怎么样?
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
是 class 变量,因此必须将其引用为 A.var
但您不能在代码中这样做,因为您尝试在定义 A
之前使用它。如果不同 classes 则可能:
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
。
y = 20
def f():
x = 7
def g():
nonlocal x # 7
global y # 20
nonlocal
限定符指的是外部函数范围内的名称,不包括模块范围。而 global
做互补。所以你使用 nonlocal
不正确。
使用装饰器来标记函数,然后装饰器是一个class方法,returns所有装饰函数。
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>]
Python 只是不允许你这样做。您可以使用 locals()
访问 class 命名空间。但是此时,你还不如将你感兴趣的变量传递给装饰器。
# 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 的末尾创建函数名称集可能更简单。例如
class C:
def func(self):
pass
func_names = [f.__name__ for f in [func]]
我想这样做(虚拟示例):
def func():
nonlocal var
print (var)
class A:
var = 'hola'
func()
但我得到:"SyntaxError: no binding for nonlocal 'var' found"
我真正想做的是将方法名称附加到 class 范围内的列表中(如果该方法已修饰)。像这样:
def decorator(func):
nonlocal decorated
decorated.append(func.__name__)
return func
class A:
decorated = []
@decorate
def f(self):
pass
怎么样?
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
是 class 变量,因此必须将其引用为 A.var
但您不能在代码中这样做,因为您尝试在定义 A
之前使用它。如果不同 classes 则可能:
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
。
y = 20
def f():
x = 7
def g():
nonlocal x # 7
global y # 20
nonlocal
限定符指的是外部函数范围内的名称,不包括模块范围。而 global
做互补。所以你使用 nonlocal
不正确。
使用装饰器来标记函数,然后装饰器是一个class方法,returns所有装饰函数。
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>]
Python 只是不允许你这样做。您可以使用 locals()
访问 class 命名空间。但是此时,你还不如将你感兴趣的变量传递给装饰器。
# 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 的末尾创建函数名称集可能更简单。例如
class C:
def func(self):
pass
func_names = [f.__name__ for f in [func]]