别摸我
别摸我
文章目录
  1. TODO

Python classmethod中的闭包问题

碰到一个 classmethod 中闭包的局部变量问题, 很有趣。

先来看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# test1.py
from functools import wraps

class A():
ctx = {}

@classmethod
def cm_decorator(cls, value):
def inner(fn):
print("locals: ", locals())
@wraps(fn)
def wrapper(*args, **kwargs):
kwargs["value"] = value
return fn(*args, **kwargs)

return wrapper
return inner

@A.cm_decorator("1")
def test(*args, **kwargs):
print(args, kwargs)

test()

执行结果:

1
2
3
> python3 test1.py
locals: {'fn': <function test at 0x103320598>, 'value': '1'}
() {'value': '1'}

OK, 意料之中, 如果把 wrapper 中的 kwargs["value"] = value 去掉会是怎样的呢? 去掉后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# test2.py
from functools import wraps

class A():
ctx = {}

@classmethod
def cm_decorator(cls, value):
def inner(fn):
print("locals: ", locals())
@wraps(fn)
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)

return wrapper
return inner

@A.cm_decorator("1")
def test(*args, **kwargs):
print(args, kwargs)

test()

运行结果:

1
2
3
> python3 test2.py
locals: {'fn': <function test at 0x103c05598>}
() {}

一眼就能发现, 之前的 value 没有了,

TODO

再看下面这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# test3.py
from functools import wraps

class A():
ctx = {}

@classmethod
def cm_decorator(cls, value):
def inner(fn):
print("locals: ", locals())
@wraps(fn)
def wrapper(*args, **kwargs):

ctx = cls.ctx

if ctx.get("value"):
value = ctx["value"]
else:
ctx["value"] = value
return fn(*args, **kwargs)

return wrapper
return inner

@A.cm_decorator("1")
def test(*args, **kwargs):
print(args, kwargs)

test()

输出:

1
2
3
4
5
6
7
8
> python3 test2.py
locals: {'fn': <function test at 0x103405598>, 'cls': <class '__main__.A'>}
Traceback (most recent call last):
File "/Users/heaven/test/2.py", line 27, in <module>
test()
File "/Users/heaven/test/2.py", line 17, in wrapper
kwargs["value"] = value
UnboundLocalError: local variable 'value' referenced before assignment
支持一下
扫一扫,支持heaven
  • 微信扫一扫
  • 支付宝扫一扫