Python 闭包不支持修改 upvalue,有什么替代的解决方案?

2016-01-19 09:20:31 +08:00
 tabris17

如下代码:

def test_closure():
    x = 1
    def closure():
        x = 2
    closure()
    print x
test_closure()

x 仍然是 1

6221 次点击
所在节点    Python
67 条回复
est
2016-01-19 10:34:21 +08:00
@tabris17 你给我演示一下如何返回了?
clino
2016-01-19 10:38:13 +08:00
@tabris17 你要这么理解也行 不过明明变了但又说不可变 你的描述是很糟糕的
tabris17
2016-01-19 10:39:51 +08:00
@est

import sys

def test_closure():
----x = 1
----def closure():
--------print sys._getframe().f_back.f_locals
----closure()
----return closure

closure = test_closure()
closure()
tabris17
2016-01-19 10:42:20 +08:00
@clino 变了以及不变,两个主语不同,变的是 upvalue 引用的对象,不变的是 upvalue
clino
2016-01-19 10:45:40 +08:00
upvalue 指向的值变了情况 你说 upvalue 没变 这是很误导人的
jmc891205
2016-01-19 10:58:39 +08:00
这是 python 作用域导致的问题
由于 python 规定在内部函数里给变量赋值的时候会屏蔽外部函数的同名变量,所以你给的例子里, closure 里的 x 已经不是对 test_closure 里的 x 的引用了。在我看来,这个例子中的 closure 并不是一个闭包。
clino
2016-01-19 11:06:34 +08:00
@jmc891205 不是作用域 不信你只打印不赋值看看
clino
2016-01-19 11:08:46 +08:00
@jmc891205 没仔细看你已经说了赋值的情况了
est
2016-01-19 11:11:05 +08:00
@tabris17 呃,我再想一下,其他语言如果一个函数都返回了,你还能修改这函数里面的局部变量?
jmc891205
2016-01-19 11:11:26 +08:00
@clino 不赋值的话外层函数的变量就不会被屏蔽咯 这也是 python 对变量作用域的规定吧
tabris17
2016-01-19 11:12:00 +08:00
@est 是啊。 Javascript 、 PHP (通过引用实现)、 Lua 都支持
jmc891205
2016-01-19 11:12:23 +08:00
@clino 这。。。麻烦你下次回别人帖子的时候 先把别人的帖子看完
Mark3K
2016-01-19 11:13:39 +08:00
我觉得闭包挺好用的,但是不应该这样使用, 我一般只在闭包里面使用外层作用域的变量,如果你需要改变外层作用域的变量,应该使用 x=closure()的方式。
tabris17
2016-01-19 11:17:52 +08:00
@est

function test_closure() {
var x = 1;
function closure1() {
x = 2;
}
function closure2() {
console.log(x);
}
return [closure1, closure2];
}
closures = test_closure();
(closures[0])();
(closures[1])();
ethego
2016-01-19 11:20:01 +08:00
```python
def test_closure():
test_closure.x = 1
def closure():
test_closure.x = 2
closure()
print x
test_closure()
```
你再试试
nooper
2016-01-19 11:20:18 +08:00
python3.
ethego
2016-01-19 11:21:19 +08:00
因为 python 的闭包确实不是完整的闭包,变量的查找规则不依赖于闭包。
xuboying
2016-01-19 11:26:22 +08:00
@ethego
这样写是错的,如下例子,结果是 2 4 6 6 6 6 ,希望的结果是 2 4 6 6 4 2
>def outer(s):
> if s == 4:
> return
> outer.OuterVar = s
> def inner():
> outer.OuterVar = outer.OuterVar * 2
> inner()
> print outer.OuterVar ,
> outer(s+1)
> print outer.OuterVar ,
>if __name__ == "__main__":
> outer(1)

正确的方法是用一个类来封装
>class Namespace: pass
>
>def outer(s):
> if s == 4:
> return
> ns = Namespace()
> ns.OuterVar = s
>
> def inner():
> ns.OuterVar = ns.OuterVar * 2
> inner()
>
> print ns.OuterVar,
>
> outer(s+1)
>
> print ns.OuterVar,
>if __name__ == "__main__":
> outer(1)
xuboying
2016-01-19 11:28:18 +08:00
重新排版
@ethego
这样写是错的,如下例子,结果是 2 4 6 6 6 6 ,希望的结果是 2 4 6 6 4 2

def outer(s):
   if s == 4:
     return
   outer.OuterVar = s
   def inner():
     outer.OuterVar = outer.OuterVar * 2
   inner()
   print outer.OuterVar ,
   outer(s+1)
   print outer.OuterVar ,
if __name__ == "__main__":
   outer(1)

正确的方法是用一个类来封装

class Namespace: pass

def outer(s):
   if s == 4:
     return
   ns = Namespace()
   ns.OuterVar = s

   def inner():
     ns.OuterVar = ns.OuterVar * 2
   inner()

   print ns.OuterVar,

   outer(s+1)

   print ns.OuterVar,
if __name__ == "__main__":
   outer(1)
ethego
2016-01-19 11:30:48 +08:00
@xuboying
```python
def test_closure():
----def closure():
--------closure.x = 2
----closure.x = 1
----closure()
----print x

test_closure()
```
你再试试

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/251731

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX