DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Python 函数里递增外部计数器报 UnboundLocalError

函数里递增外部计数器触发 UnboundLocalError,需区分局部变量、模块状态和闭包状态。按状态归属选择返回值、global 或 nonlocal,并核对条件分支和调用方。

模块里已经写了 count = 0,函数中也能读取这个值,但加入 count += 1 后,调用函数却报 UnboundLocalError。旧版报错常见为 local variable 'count' referenced before assignment,较新的版本可能显示 cannot access local variable 'count' where it is not associated with a value。

问题通常出在同名变量的作用域。以下讨论 Python 3 的普通函数;如果出错变量只在某个条件分支中赋值,也要检查执行路径,不能看到这个异常就直接加 global。

赋值会改变整个函数中这个名字的含义

一个最小场景如下:

count = 0

def increment():
    count += 1

increment()

+= 包含读取和赋值。在没有 global 或 nonlocal 声明时,函数中给 count 赋值,会让这个名字在该函数中被当作局部变量。执行右侧计算时,局部的 count 还没有值,于是报错。模块中的同名变量不会自动成为它的初始值。

作用域按整个函数代码块确定,不取决于赋值语句这次有没有执行。因此,在赋值之前加一句 print(count),或把赋值藏进一个条件分支,也可能触发同样的异常。仅把 count += 1 换成 count = count + 1 不能修复作用域。

先从 traceback 定位具体函数,再搜索这个函数中对同名变量的赋值,包括 =、+=、循环目标和异常绑定等。要确认的是“准备更新哪个状态”,而不只是让异常消失。

普通计算可以把旧值传入,把新值返回

如果计数器由调用方管理,可以让依赖关系显式化:

def increment(value):
    return value + 1

count = 0
count = increment(count)
count = increment(count)
print(count)  # 预期为 2

这里函数读取自己的参数,返回结果,再由调用方更新变量。注意接住返回值:只调用 increment(count) 而没有赋值给 count,整数计数器不会因此改变。

在已有程序中替换时,要一起检查所有调用位置。有的函数还负责写文件、发消息或提交事务,不要为了验证计数器反复调用有副作用的完整业务流程;使用独立的输入样例检查返回值即可。

确实要更新模块变量时使用 global

小型脚本若明确依赖模块级状态,可在函数里声明:

count = 0

def increment():
    global count
    count += 1

increment()
print(count)  # 预期为 1

声明必须放在函数内对该名字的使用或赋值之前。它指向定义这个函数的模块命名空间,不是任意调用方的局部变量,也不会凭空初始化一个尚不存在的计数值。

这会让不同调用共享同一个状态。原先以为各次调用彼此独立的代码,可能因此互相影响;global 也不提供并发同步。多个线程、协程或进程涉及同一业务计数时,仍需按执行模型设计状态管理。

外层函数的局部变量要用 nonlocal

闭包中的计数器属于外层函数,不属于模块:

def make_counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment

first = make_counter()
second = make_counter()
print(first(), first(), second())  # 预期为 1 2 1

nonlocal 查找最近的、已经绑定该名字的外层函数作用域。若没有这样的绑定,代码会产生 SyntaxError。在这个场景改用 global,会选中另一处状态,不能作为等价替换。

修复后同时检查值和状态归属

验证应覆盖连续调用,以及原本应独立的两个调用上下文。只确认“不报错”不够:返回值方案要确认调用方接收了结果;模块变量方案要确认共享符合设计;闭包方案要确认不同实例不会共用计数器。

如果变量本来就是局部结果,只在部分分支中赋值,应让每条需要使用它的路径先产生有效值,或在缺少值时提前返回。不要用模块变量掩盖漏掉的分支。修改前保留函数及调用位置的版本;发现状态范围改变时,一并回退声明与调用方改动,避免留下半套状态管理逻辑。