V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
aragakiiyui
V2EX  ›  Python

怎样更好的解释 Python 中的 UnboundLocalError?

  •  
  •   aragakiiyui · 2017-01-23 11:20:49 +08:00 · 1946 次点击
    这是一个创建于 2650 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,现在只考虑一种情况, 其他情况以此类推:

    a = 1
    def func1():
       print(a)
       return a
        
    def func2():
       print(a)
       a = a + 1
       return a
    

    这两个函数只有 func1 可以正常执行, func2 会抛出 UnboundLocalError 异常。前者比较好解释,局部作用域没有 a ,那么会从全局作用域中去查找这个 a 。

    后者我只能这样解释,就是函数体在执行前会做预先检查。如果发现有赋值语句存在,就会认为该变量名处于局部作用域中。由于函数体中有 a = a + 1 ,所以函数只在局部作用域查找 a , 然后执行的时候,发现并没有 a 这个变量,故抛出异常。

    我觉得这种解释非常的绕,而且不是很直观,很容易忘记。不知道各位 v 友有什么通俗一点的方式来描述这种行为~

    10 条回复    2017-01-24 15:51:23 +08:00
    est
        1
    est  
       2017-01-23 11:28:45 +08:00
    global a
    aragakiiyui
        2
    aragakiiyui  
    OP
       2017-01-23 11:32:55 +08:00
    @est ......晕,这个解释不了这个问题。
    lightning1141
        3
    lightning1141  
       2017-01-23 12:13:35 +08:00
    因为赋值语句的存在, Python 尝试在局部作用域 (即 locals 名字空间) 中新建一个对象关联. 其实只是局部变量跟全局变量变量名冲突了. 冲突之后 Python 因为按 LEGB 顺序查找变量, 发现 a 还没有在局部作用域绑定, 所以导致了 UnboundLocalError.
    ```
    def test1():
    a = a
    print(a)

    def test2():
    x = a
    print(x)
    ```
    第一个没问题, 第二个报错.
    lightning1141
        4
    lightning1141  
       2017-01-23 12:19:14 +08:00
    简单来说就是:
    赋值导致了拥有这个变量名的变量只能在局部作用域查找
    bxb100
        5
    bxb100  
       2017-01-23 12:21:23 +08:00 via Android
    加个 a = a 还报错吗
    lightning1141
        6
    lightning1141  
       2017-01-23 12:26:28 +08:00   ❤️ 1
    @lightning1141
    @bxb100
    说反了, 第一个报错, 第二个没问题..........
    bxb100
        7
    bxb100  
       2017-01-23 12:32:58 +08:00 via Android
    @lightning1141 符合逻辑
    phrack
        9
    phrack  
       2017-01-24 09:41:51 +08:00   ❤️ 1
    以前写 c 程序的时候,偶尔也会有出乎意料的结果,但是我一直有个办法,就是反汇编直接跟踪执行流程。

    python 也一样, import dis, dis.dis(func1), dis.dis(func2), 瞬间就明了了。
    IanPeverell
        10
    IanPeverell  
       2017-01-24 15:51:23 +08:00
    func2() 里面加一个 global a 就不会抱错了,因为这样是为了让你在修改变量的时候提醒你,你有个全局变量和你要修改的变量名称相同。
    ( python 3.5.2)
    a = 1
    def func1():
    print(a)
    return a

    print(func1())

    def func2():
    global a
    a += 1
    print(a)
    return a

    print(func3())

    这种情况下是不会报错的,如果仅仅是引用全局变量而不修改全局变量是不会报错的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2809 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 15:17 · PVG 23:17 · LAX 08:17 · JFK 11:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.