问题:是否可以在外部范围而不是全局范围内的python中修改变量?
给定以下代码:
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
Python 3.x具有nonlocal
关键字。我认为这可以满足您的要求,但是我不确定您是在运行python 2还是3。
非本地语句使列出的标识符引用最近的封闭范围中的先前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地命名空间。该语句允许封装的代码在全局(模块)范围之外的本地范围之外重新绑定变量。
对于python 2,我通常只使用可变对象(如列表或dict),然后更改值而不是重新分配。
例:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
输出:
[1, 1]
回答 1
您可以使用空类来保存临时范围。就像是易变但更漂亮。
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
我对Python有点陌生,但是我已经读了一些。我相信您将获得的最佳效果类似于Java解决方法,即将您的外部变量包装在列表中。
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
编辑:我想这可能在Python 3之前是正确的。看起来nonlocal
是您的答案。
回答 3
不,至少不能以这种方式。
因为“设置操作”将在当前范围内创建一个新名称,该名称将覆盖外部名称。
回答 4
对于以后在更安全但更重的解决方法上进行研究的人来说。无需将变量作为参数传递。
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
回答 5
简短的答案将自动起作用
我创建了一个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
这些参数具有以下含义:
- 第一个参数是执行范围。0表示本地
B()
,1表示父级A()
,2表示祖父母,<module>
也就是全局 - 第二个参数是您要在给定范围内执行的字符串或代码对象
- 您也可以在程序内部不带参数的交互式shell来调用它
长答案
这更复杂。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页面复制一些方法来规避这些问题
- 预先关联,导入和定义对象
- 事先将占位符关联到您的对象
- 在主程序中将对象重新分配给自身以更新符号表:x = locals()[“ x”]
- 在主程序中使用exec()而不是直接调用以避免优化。而不是调用x:exec(“ x”)
如果您觉得使用exec()
不是您想要的东西,则可以通过更新真正的本地字典(而不是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!
回答 6
我认为您不应该这样做。可以在其封闭的上下文中更改事物的功能非常危险,因为在不了解该功能的情况下可能会编写该上下文。
您可以通过在类中将B设置为公共方法,将C设置为私有方法(可能是最好的方法)来使其明确。或通过使用可变类型(例如列表)并将其显式传递给C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
回答 7
可以,但是您必须使用全局语句(使用全局变量时,并不是一如既往的很好的解决方案,但是它可以工作):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
回答 8
我不知道是否有一个功能的属性 __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
执行后的全局bA()
已被修改,因此它可能不适用
只有在全局命名空间中存在带有标识符b的对象时,情况才如此