在Python中共享调用函数与被调用函数的作用域

13 投票
5 回答
5424 浏览
提问于 2025-04-17 14:43

有没有办法在Python中管理作用域,让调用函数中的变量在被调用的函数中也能被看到?我想要的效果像下面这样:

z = 1

def a():
    z = z * 2
    print z

def b():
    z = z + 1
    print z
    a()
    print z

b()

我希望能得到以下输出:

2
4
2

解决这个问题的真正方法就是把z作为一个变量传递过去。但我不想这么做。

我的代码库很大而且复杂,里面有不同水平的用户。他们现在已经习惯只传递一个变量,而我现在需要添加另一个变量。我不太相信他们能在所有函数调用中都正确地传递第二个变量,所以我在寻找一种保持接口不变的替代方案。很有可能这根本就做不到。

5 个回答

3

这个方法看起来有点复杂,但它避免了使用全局变量,我测试过了,确实有效。使用选定答案中的代码,函数 getSetZ() 有一个“静态”变量,可以用来存储传入的值,然后在函数被调用时,如果传入 None,就可以取出这个值。有一些限制,比如它假设 None 不是 z 的可能值,而且不适合在多线程环境中使用。你只需要记得在每次调用你想要使用调用函数的 z 的函数之前,先调用一次 getSetZ(),然后把从 getSetZ() 中取出的值放到那个函数的局部变量里。

def getSetZ(newZ):
    if newZ is not None:
        getSetZ.z = newZ
    else:
        return getSetZ.z

def a():
    z = getSetZ(None)
    z = z * 2
    print z

def b():
    z = getSetZ(None)
    z = z + 1
    print z
    getSetZ(z)
    a()
    print z

getSetZ.z = 0
getSetZ(1)
b()

希望这对你有帮助。

7

这里可能适合用一个类来解决问题:

class MyClass(object):
    def __init__(self, z):
        self.z = 1

    def a(self):
        self.z = self.z * 2
        print self.z

    def b():
        self.z = self.z + 1
        print self.z
        self.a()
        print self.z

你也可以用全局变量来达到相同的效果,但我建议你创建一个自己的独立命名空间(比如用一个类),这样可以更好地管理你的代码范围。

14

我觉得这就是你想要的。如果你能控制内部的函数,并且你想要的变量已经在调用链的某个地方可用了,但你又不希望用户在调用时必须包含这个变量。很明显,你不想用global关键字,因为你不希望内部的赋值影响到外部的范围。

使用inspect模块可以让你访问到调用的上下文。(不过,要注意这有点脆弱。你最好只在没有线程的CPython中使用。)

import inspect

def calling_scope_variable(name):
  frame = inspect.stack()[1][0]
  while name not in frame.f_locals:
    frame = frame.f_back
    if frame is None:
      return None
  return frame.f_locals[name]

z = 1

def a():
  z = calling_scope_variable('z')
  z = z * 2
  print z

def b():
  z = calling_scope_variable('z')
  z = z + 1
  print z
  a()
  print z

b()

这样确实能得到正确的答案:

2
4
2

撰写回答