在Python中通过global关键字从外部作用域访问变量

7
根据这个问题的已接受答案,我理解我不能创建纯全局变量。好的,很酷。
然而,他接着说:
引用: [..]你所能做的就是在特定范围内创建一个变量。(如果你在Python解释器中创建一个变量,然后导入其他模块,你的变量就在最外层的范围内,因此在你的Python会话中是全局的。[..]
好的,所以我们不能按原始意义分配全局变量,但似乎可以通过global关键字从包内部访问最外层作用域中的变量,对吗?
然后,显然,在我的尝试中访问通过命令行参数传递给我的Python程序的变量时,我错过了一些至关重要的东西。
我的程序有通常的__main__.py,它处理参数解析并执行来自我的Python模块backend的代码。 backend的代码依赖于通过命令行参数输入的内容。然而,我似乎无法使这些参数对backend可用。
我的包的布局如下:
mypackage
- __main__.py
- backend/
    -__init__.py
    -direction.py

这是__main__.py文件的内容:
import argparse

# Setup ArgParser
parser = argparse.ArgumentParser(description="Fancy Description of program.")
parser.add_argument('--tar', nargs=1, dest='target',
                help='Specify output folder.',
                default=['/path/to/output'])

target_dir = args.target[0]

# Import backend now that our variable is set.
from backend.direction import direction

我的backend/direction.py文件:

global target_dir
print(target_dir)

运行此代码会引发一个 NameError: 'target_dir' is not defined 错误。那么问题出在哪里呢? 我是假设了一种不可能的情况,还是在声明上出了错?


当我们说“global”时,意味着变量在外部作用域中定义。它对其他模块不可见(在Python中,每个文件都是一个模块)。因此,您需要显式地import该模块才能访问该变量。 - thefourtheye
那么,在我的情况下,我会在direction.py中导入__main__吗?这听起来不太好,也不像应该做的事情(?)。使用文件作为变量的解决方法可接受吗? - deepbrook
@j4ck 说实话,你可能根本不应该使用全局变量。如果你确实需要使用它们,通常最好将它们限制在单个模块中(参见PEP8)。 - Brendan Abel
1
说实话,我不确定你为什么想在这里使用全局变量。你应该将 target_dir 传递给实际需要它的任何函数。如果有很多函数需要用到它,考虑使用一个类。 - Daniel Roseman
我猜我只是在某种程度上被global关键字困扰住了。target_dir被传递给几乎所有的函数,所以我想绕过为每个调用输入它的麻烦 - 但是我看到这里的建议和答案清楚地说明了为什么对于这个任务来说global是无意义的。 - deepbrook
2个回答

17
global 只能在其所在的模块内部使用。在全局作用域(即模块作用域)中,你也不需要使用 global声明 全局变量。在函数作用域中使用 global 关键字可以表示你想要使用全局作用域中的变量,而非当前作用域中的同名变量。
在 Python 3 中,还有一个 nonlocal 关键字,它允许你指示修改外层作用域中非全局/模块作用域的变量。 global
A = 1

def global_func():
    global A
    A = 2


def func():
    A = 3


print(A)
# 1

global_func()
print(A)
# 2

func()
print(A)
# 2

nonlocal

def outside():
    a = 1

    def inside_nonlocal():
        nonlocal a
        a = 2

    def inside():
        a = 3

    print(a)
    # 1

    inside_nonlocal()
    print(a)
    # 2

    inside()
    print(a)
    # 2

我需要将这个分成两行吗?全局变量A = 2不也可以完成任务吗? - Schnaps
@Schnaps 不,那不是一个有效的语句。这两个语句不能合并。 - Brendan Abel

0

我不清楚你的问题实际上是什么。以下操作是否不可行?

import backend

if __name__ == "__main__":
    # parse arguments
    backend.argreceive(arguments)

正如Daniel Roseman所建议的那样,如果有许多后端函数需要访问这些变量,则应考虑使用类和类属性来存储变量。
class argreceive():
    def __init__(self,args):
        self.args = args

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接