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
lichifeng
V2EX  ›  Python

python 的函数内部变量不能和函数重名?

  •  
  •   lichifeng · 2014-06-18 09:14:29 +08:00 · 8585 次点击
    这是一个创建于 3847 天前的主题,其中的信息可能已经有所发展或是发生改变。
    第一段错误的代码:
    def test(i):
    ....if i == 0:
    ........test = 'one'
    ........print test
    ....elif i == 1:
    ........test = 'two'
    ........print test
    ....elif i == 2:
    ........test(1)
    test(2)
    我以为结果是'two',
    实际运行结果:UnboundLocalError: local variable 'test' referenced before assignment

    ==============================

    第二段错误的代码:
    def test(i):
    ....if i == 0:
    ........test = 'one'
    ........print test
    ....elif i == 1:
    ........test_1 = 'two'
    ........print test_1
    ....elif i == 2:
    ........test(1)
    test(2)
    我以为结果是'two',
    实际运行结果:UnboundLocalError: local variable 'test' referenced before assignment

    ==============================

    第三段的代码:
    def test(i):
    ....if i == 0:
    ........test_0 = 'one'
    ........print test_0
    ....elif i == 1:
    ........test_1 = 'two'
    ........print test_1
    ....elif i == 2:
    ........test(1)
    test(2)
    运行结果正确:'two'


    谁能给我解释一下0.0
    菜鸟实在被搞晕了
    10 条回复    2014-06-18 11:49:44 +08:00
    mengzhuo
        1
    mengzhuo  
       2014-06-18 09:39:10 +08:00   ❤️ 1
    = =
    好基础啊
    不行,不要这样写代码

    test_0 != test
    clino
        2
    clino  
       2014-06-18 09:45:05 +08:00   ❤️ 2
    我的理解是: 在test()下的这个作用域里有test=这种赋值,那么python会认为 test 是一个局部变量,个人理解因为python里赋值有声明的作用,而函数名test对于函数内部的作用域来说不是局部变量,所以一旦python认定test是函数内的局部变量以后你就访问不到外面的函数名指代的这个变量了
    cakegg
        4
    cakegg  
       2014-06-18 10:33:09 +08:00   ❤️ 2
    你在这个test函数里定义了名称为test的变量, 那么外部的test函数名会被shadow. 所以在这个test函数内部使用test变量时都只会在**函数内部**寻找test的定义.

    因此, 在运行test(2)时, 函数test(i)在内部找不到test(1)的定义. 如果你一定要这么写, 那么需要在test(i)**函数内部**给出test函数的定义. 比如下面的代码就不会报错了...

    def test(i):
    ....def test(j):
    ........print "Inside test"
    ....if i == 0:
    ........test = 'one'
    ........print test
    ....elif i == 1:
    ........test = 'two'
    ........print test
    ....elif i == 2:
    ........test(1)
    test(2)

    唉, 语文从来都是班里倒数, 不知道有没有表达清楚...
    daniel7725
        5
    daniel7725  
       2014-06-18 10:39:27 +08:00   ❤️ 2
    楼主在第一段代码的 test="two"前面加上一句 global test,你的代码就不会报错了。
    原因在于Python的名字空间。当你定义了test()函数之后,在全局空间 globals()中,是"test":<function test at 0xxxxxxx>,这表示test是个函数,并且是全局空间的。但是你在函数里面,进行test="two"这种赋值操作的时候,python会把test变成局部变量,就如2楼所说。这时就会出现你遇到的那个错误。
    所以,你要想让程序跑起来(这里不推荐这种做法),就加上global test。这时就强制python不把test当成是局部变量,但是同时你也将丢掉了test函数(这时你在 print globals()时,你会发现test变成了 "test":"two"),这也就是Python的灵活支出,所有的变量都不是指针,而是名字空间字典里面的key,可以随意改变。
    楼主别这样使用就行了。
    一般情况遇到这个错误,是下面这样:
    tmp = 1
    def test():
    ....if tmp == 1:
    ........tmp = 2
    test()

    也就是 你定义了全局变量 tmp,但是在函数test里面使用tmp并且想要改变tmp的值,这时就会报你那样的错误了。这个时候就需要添加上 global tmp,表示是全局变量。
    fdgogogo
        6
    fdgogogo  
       2014-06-18 10:50:21 +08:00   ❤️ 1
    不是很明白你为什么要写这么晕的代码...
    按这么说的确是不能重名,为什么呢? 看下代码你就懂了

    >>> def foo():
    ... print 'hello'
    ...
    >>> def bar():
    ... print 'world'
    ...
    >>> foo()
    hello
    >>> bar()
    world
    >>> foo
    <function foo at 0x1004812a8>
    >>> bar
    <function bar at 0x100481320>
    >>> foo = bar
    >>> foo
    <function bar at 0x100481320>
    >>> foo()
    world
    >>> foo == bar
    True
    >>> foo() == bar()
    world
    world
    True

    你在写def test()的时候, 实际上你不是写了一个函数, 你是生成了一个叫test的变量, 装着一个函数. 你可以在运行时实时生成新的函数, 也可以给把一个函数赋值给另一个函数, 所以你这里的test指向的并不是你以为的局部变量,而是这个函数自身
    cakegg
        7
    cakegg  
       2014-06-18 11:14:59 +08:00
    @fdgogogo 我觉得您的理解有误. LZ 的意思是 函数内部变量 和 该函数 是否可以重名. 事实上是可以的, 只是不建议这么写程序.
    jianghu52
        8
    jianghu52  
       2014-06-18 11:23:45 +08:00   ❤️ 1
    说下我的简单理解。
    python 里面 def test()这个是函数
    test 表示这个以这个函数为变量,比如我可以写这样的代码
    def usemethod(test):
    pass
    这也就是所谓的函数式编程。将一个函数看成是int,string这样的基本类型传来传去。
    所以当你在函数内写一个跟函数重名的变量的时候,python自动就认为你是在用这个函数作为变量。
    fdgogogo
        9
    fdgogogo  
       2014-06-18 11:44:15 +08:00
    @cakegg 嗯没错,仔细跑了一下代码的确是shadow掉的问题,前面是想当然了
    lichifeng
        10
    lichifeng  
    OP
       2014-06-18 11:49:44 +08:00
    自认为大概明白什么意思,谢谢!
    V2EX比STACKOVERFLOW还好用啊,哈哈

    @楼上各位大
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2838 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 14:16 · PVG 22:16 · LAX 06:16 · JFK 09:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.