是否可以在python中修改外部但不是全局范围的变量?

时间:2011-12-09 15:45:07

标签: python python-2.7

给出以下代码:

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()阅读,但是如何修改呢?

9 个答案:

答案 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

自变量具有以下含义:

  • 第一个参数是执行范围。 0表示本地B(),1表示父级A(),而2表示祖父母<module>,也称为全局
  • 第二个参数是要在给定范围内执行的字符串或代码对象
  • 您也可以在程序内不带 interactive 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(),可以使用 通过更新 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!
相关问题