给一个已经跑得好好的脚本加上"在函数里修改模块级常量"的能力,是再常见不过的需求。可有时候你只是往函数里插了一行 global,脚本就直接编译不过了。
现象
你给一个函数加上模块级变量的修改逻辑,运行时得到这样的报错:
File "demo.py", line 5
global COUNT
^^^^^^^^^^^^
SyntaxError: name 'COUNT' is used prior to global declaration
注意这是一个 SyntaxError——它发生在代码被编译的阶段,而不是运行到那一行才报错。也就是说,哪怕这个函数一次都没被调用,只要文件里有这段代码,整个模块就导入不了。
根因
关键在于理解 global 这个声明语句在 Python 里的位置规则。
global X 不是一条"运行时执行"的语句,而是一条作用于整个函数作用域的编译期指令。它告诉编译器:"在这个函数里,名字 X 一律按模块级变量解析,不要建局部变量。"正因为它是编译期生效、影响整个函数的,Python 就要求它必须出现在该名字被任何形式的读取或写入之前。
下面这段就会报上面的错:
COUNT = 0
def bump():
print(COUNT) # ① 先“使用”了 COUNT
global COUNT # ② 才声明 global —— 太晚了
COUNT += 1
第 ① 行一读 COUNT,编译器就为这个名字定下了"局部/全局"的解析方式;等到第 ② 行再看到 global COUNT,它发现这个名字已经被"用过"了,前后矛盾,于是报 used prior to global declaration。
容易踩坑的地方在于:这个"使用"不一定是显式的 print。函数参数默认值、装饰器里引用、甚至同名参数,只要在 global 之前出现了这个名字,都可能触发。改代码时把新逻辑插到旧逻辑前面,就很容易踩中。
解决
把 global 提到函数的第一行,让声明先于一切使用:
COUNT = 0
def bump():
global COUNT # 声明放最前面
print(COUNT)
COUNT += 1
这样编译器在处理函数体时,先看到 global,就提前确定了 COUNT 的解析规则,后面的读取和写入都按模块级变量处理,不再冲突。
改名规避也是一种办法:如果模块级变量和函数内的别名不一样,就不会有这个先后矛盾。但更推荐前者——把 global 放最上面,语义最清晰。
延伸与预防
Python 里带有位置要求的声明语句不止 global 一条,记住这一族规则能省下不少排查时间:
nonlocal有完全一样的限制——必须出现在该名字被使用之前,否则同样报used prior to nonlocal declaration。- 遇到
global和del混用时也要小心:del X也算"使用 X",如果global X写在它后面,照样报错。 - 函数参数默认值在
def语句执行时就被求值,位置在函数体之外,所以默认值里引用的名字不受函数体内global的影响——这是另一个容易混淆的点。
预防上,最实用的一条是:改代码时,凡是往函数里新增 global / nonlocal,先把它挪到函数体最顶端,再写其余逻辑。另外,把这类语法错误挡在提交之前的技术手段也很直接——很多编辑器/IDE 会在保存时就标红,python -m py_compile your_script.py 也能在不运行的情况下快速验证语法。养成改完先编译一眼的习惯,就不会把 SyntaxError 带到运行阶段了。