给出以下代码:
def A() :
b = 1
def B() :
# I can access 'b' from here.
print( b )
# But can i modify 'b' here? 'global' and assignment will not work.
B()
A()
对于B()
中的代码,函数变量b
位于外部范围内,但不在全局范围内。是否可以从b
函数中修改B()
变量?当然我可以从这里和print()
阅读,但是如何修改呢?
答案 0 :(得分:69)
Python 3.x有nonlocal
keyword。我认为这样做你想要的,但我不确定你是否正在运行python 2或3。
非本地语句会导致列出的标识符引用 先前在最近的封闭范围内绑定变量。这是 很重要,因为绑定的默认行为是搜索 本地名称空间首先该语句允许封装代码 除了全局之外,重新定义局部范围之外的变量 (模块)范围。
对于python 2,我通常只使用一个可变对象(如列表或dict),并改变值而不是重新分配。
示例:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
输出:
[1, 1]
答案 1 :(得分:16)
您可以使用空类来保存临时范围。它就像可变但有点漂亮。
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
这会产生以下交互式输出:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
答案 2 :(得分:8)
我对Python有点新鲜,但我已经读过一些关于这一点的内容了。我相信你得到的最好的东西类似于Java解决方法,即将你的外部变量包装在一个列表中。
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
//output is '2'
编辑:我想在Python 3之前这可能是真的。看起来“非本地”是你的答案。
答案 3 :(得分:4)
不,你不能,至少以这种方式。
因为“设置操作”将在当前范围中创建一个新名称,该名称将覆盖外部范围。
答案 4 :(得分:2)
对于那些稍后看一个更安全但更重的解决方法的人来说。无需将变量作为参数传递。
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
答案 5 :(得分:1)
我不认为你应该想要这样做。可以在其封闭的上下文中改变事物的函数是危险的,因为可以在不知道函数的情况下编写该上下文。
你可以通过使B成为公共方法而C成为类中的私有方法(可能是最好的方法)来使其显式化;或者使用诸如列表之类的可变类型并将其显式传递给C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
答案 6 :(得分:0)
你可以,但是你必须使用global statment(在使用全局变量时,并不是一个非常好的解决方案,但是它有效):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
答案 7 :(得分:0)
我不知道当这个外部空间不是全局空间==模块时,是否存在函数的属性给出函数外部空间的__dict__
,就是这种情况当函数是嵌套函数时,在Python 3中。
但是在Python 2中,据我所知,没有这样的属性。
所以做你想做的事的唯一可能性是:
1)使用可变对象,如其他人所说
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
结果
b before B() == 1
b == 10
b after B() == 10
CédricJulien的解决方案有一个缺点:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
结果
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
执行A()
后的全局 b 已被修改,可能没有被提及
只有在全局命名空间中存在标识符 b 的对象时才会出现这种情况
答案 8 :(得分:0)
我创建了一个python库来解决此特定问题。它是在松散状态下发布的,因此可以根据需要使用它。您可以使用pip install seapie
安装它,也可以在https://github.com/hirsimaki-markus/SEAPIE
user@pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
输出
2
自变量具有以下含义:
B()
,1表示父级A()
,而2表示祖父母<module>
,也称为全局这更复杂。 Seapie通过使用CPython API编辑调用堆栈中的帧来工作。 CPython是事实上的标准,因此大多数人不必担心它。
如果您正在阅读以下内容,很可能会引起您的兴趣:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
后者将强制更新传递到本地范围。但是,局部范围的优化与全局范围的优化不同,因此,如果您未以任何方式对其进行初始化,则在尝试直接调用它们时,引入新对象会遇到一些问题。我将从github页面复制一些方法来规避这些问题
如果您不想使用exec()
,可以使用
通过更新 true 本地词典(不是locals()返回的词典)来模拟行为。我将从https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
输出:
hack!