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

2014-06-18 09:14:29 +08:00
 lichifeng
第一段错误的代码:
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
菜鸟实在被搞晕了
8413 次点击
所在节点    Python
10 条回复
mengzhuo
2014-06-18 09:39:10 +08:00
= =
好基础啊
不行,不要这样写代码

test_0 != test
clino
2014-06-18 09:45:05 +08:00
我的理解是: 在test()下的这个作用域里有test=这种赋值,那么python会认为 test 是一个局部变量,个人理解因为python里赋值有声明的作用,而函数名test对于函数内部的作用域来说不是局部变量,所以一旦python认定test是函数内的局部变量以后你就访问不到外面的函数名指代的这个变量了
clino
2014-06-18 09:50:06 +08:00
cakegg
2014-06-18 10:33:09 +08:00
你在这个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
2014-06-18 10:39:27 +08:00
楼主在第一段代码的 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
2014-06-18 10:50:21 +08:00
不是很明白你为什么要写这么晕的代码...
按这么说的确是不能重名,为什么呢? 看下代码你就懂了

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

@楼上各位大

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/118107

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX