ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

明明在赋值前读取,为什么还会报 UnboundLocalError?global 与 nonlocal 深挖

明明在赋值前读取,为什么还会报 UnboundLocalError?global 与 nonlocal 深挖 「Python 进阶之路」系列 Day32写在前面Day04 讲闭包与作用域时提过nonlocal今天作为面试高频题串讲的第二篇把global/nonlocal单独拎出来深挖——重点讲一个经常让人摸不着头脑的报错明明代码是先读取、后赋值为什么 Python 会说这个变量还没被赋值一、是什么两个改写外层变量的关键字global声明函数内某个名字指向模块级别的全局作用域赋值会直接修改全局变量而不是创建局部变量nonlocal声明函数内某个名字指向最近一层外层函数的作用域不是全局常用于嵌套函数里修改闭包变量counter0defincrement():globalcounter counter1increment()increment()print(counter)# 2二、为什么编译期就决定了变量归属Python 编译函数体时会静态扫描整个函数内部——只要函数体内任意位置对某个名字有赋值动作这个名字在整个函数体内都会被当成局部变量不管这行赋值实际会不会被执行到、也不管它出现在读取语句之前还是之后。global/nonlocal就是用来打破这条默认规则的显式声明这个名字不要当局部变量去外层/全局作用域找。三、怎么用1. UnboundLocalError经典陷阱不加global函数内对同名变量赋值只会创建一个新的局部变量完全不影响外部x100defmodify_x():x200print(函数内部x:,x)modify_x()print(函数外部x:,x)# 100没被修改更容易踩的坑是只要函数体内某处有赋值语句哪怕赋值语句写在读取之后Python 也会在编译期把这个名字整体标记为局部变量y100defbad_read():print(y)# 想读全局变量yy200# 但这行赋值让Python在编译期就把y标记成了局部变量bad_read()# UnboundLocalError: cannot access local variable y where it is not associated with a valueprint(y)这一行看起来应该能读到全局的y毕竟这行代码执行时下面那行赋值还没跑到但 Python 判断y 是不是局部变量是整个函数体一次性静态扫描的结果不是按代码执行顺序动态判断的——只要函数体内有y ...整个函数体从头到尾的y都算局部变量print(y)这行读取的是尚未被赋值的局部变量直接报错。2. 关键区别global能自动创建nonlocal不行存在不存在global声明直接绑定模块的全局作用域不存在就自动创建nonlocal声明最近一层外层函数是否已存在该变量绑定到那一层直接SyntaxError不会继续往外找global声明的变量即使全局作用域里一开始压根不存在也完全没问题调用函数时会自动创建defcreate_new_global():globalbrand_new_var brand_new_var我是运行时被global创建出来的print(brand_new_varinglobals())# Falsecreate_new_global()print(brand_new_varinglobals())# Truenonlocal则要求最近一层外层函数作用域必须已经存在这个变量否则在编译期直接报SyntaxError不会等到运行时才发现defouter2():definner2():nonlocalnot_exist# outer2根本没有not_exist这个变量not_exist1inner2()# SyntaxError: no binding for nonlocal not_exist foundnonlocal也不会跳过没有该变量的中间层继续往更外层或全局去找——它只认最近一层中间层没有就直接报错哪怕全局作用域里恰好有同名变量mid_missing全局的mid_missingdeflevel1():deflevel2():# level2这一层没有定义mid_missingdeflevel3():nonlocalmid_missing# level3声明nonlocal但最近一层(level2)没有这个变量mid_missinglevel3改的level3()level2()level1()# SyntaxError: no binding for nonlocal mid_missing found只有当变量确实存在于最近一层外层函数时nonlocal才能正常工作deflevel1_ok():mid_varlevel1定义的deflevel2_ok():deflevel3_ok():nonlocalmid_var# 一层层往外找在level1_ok里找到了mid_varlevel3改的level3_ok()level2_ok()print(mid_var)level1_ok()# level3改的四、面试追问Q1global和nonlocal分别解决什么问题global让函数内的赋值直接作用于模块级全局变量nonlocal让嵌套函数内的赋值作用于最近一层外层函数的变量两者都是为了打破函数内赋值默认创建局部变量这条默认规则。Q2为什么函数内明明在赋值语句之前读取变量还是会报UnboundLocalErrorPython 编译函数体时是整体静态扫描的只要函数体内任意位置对某个名字有赋值这个名字在整个函数体内都被当成局部变量与赋值语句实际执行到没到、出现在读取语句前后完全无关。这个判定发生在编译期而不是按代码实际执行顺序动态决定的。Q3global和nonlocal在变量不存在时的表现有什么关键区别global声明的变量哪怕全局作用域里原本不存在调用时也能正常运行并自动创建nonlocal要求最近一层外层函数作用域必须已经存在这个变量否则在编译期直接抛出SyntaxError不会等到运行时才暴露问题。Q4nonlocal会不会跳过没有该变量的中间层去更外层或全局找不会。nonlocal只认最近一层外层函数作用域这一层没有就直接报错哪怕更外层或全局恰好有同名变量也不会继续往外查找。Q5多个函数共享同一个global变量在实际项目里有什么隐患会造成隐式耦合——多个函数都能修改同一份全局状态谁改了、什么时候改的很难追踪尤其在多线程场景下还会引入竞态条件Day17 讲过。实际项目里更推荐把共享状态封装进类或者显式通过参数传递而不是依赖分散在各处的全局变量。下一篇预告Day33 讲装饰器连环追问——围绕带参数的装饰器、多个装饰器叠加的执行顺序这些高频细节展开。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表