Python函数中的作用域规则及参数传递
全局作用域与局部作用域的关系
Python中只有模块、类以及函数才会引入新的作用域。
在其他语言中:
#include<stdio.h>
int main() {
if(2 > 0) {
int i = 0;
}
printf("i = %d", i);
return 0;
}
上述代码中,if子句引入了一个局部作用域,printf函数中对变量i的引用会引发编译错误。
在Python中:
if True:
i = 0
print i
if子句并没有引入一个局部作用域,变量i仍然处在全局作用域中,因此,变量i对于接下来的print语句是可见的。
模块是一个全局作用域
每一个模块是一个全局作用域——一个名字空间,变量名在一个模块文件顶层生成(赋值)。
对函数的每个调用是一个新的局部作用域
每次你调用一个函数,生成一个新的局部作用域——是在函数的内部生成的名字所在的一个名字空间。
赋值的名字是局部的,除非声明是全局
默认情况下,在函数中赋值的名字都放到局部作用域中(函数调用相关的名字空间)。如果你需要赋值一个名字,而该名字又在包括在函数的模块顶层的话,你可以通过在一个函数中用一个global语句声明是全局的。(由于该名字已经存在于包括该函数的模块顶层(即全局变量),若想在函数中对其进行赋值,默认情况下放弃了全局变量,在局部作用域中又产生了一个局部变量,若想保留该变量的全局性,可以通过在函数中用一个global语句声明是全局的)
i = 0
def f():
i = 8
print i
f()
print i
运行结果:8和0。i = 8是一个名字绑定操作,它在函数f的局部作用域中引入了新的变量i,屏蔽了全局变量i,因此f内部的print语句看到的是局部变量i,f外部的print语句看到的是全局变量i。
i = 0
def f():
print i
i = 0
f()
运行结果显示:UnboundLocalError: local variable ‘i’ referenced before assignment。在这个例子当中,函数f中的变量i是局部变量,但是在print语句使用它的时候,它还未被绑定到任何对象之上,所以抛出异常。
所有其他的名字都是全局的或内置的
函数定义中没赋值的名字均被认为是全局的(在包含它的模块的名字空间中)或内置的(在Python提供的预定义名字空间中)。
参数传递
参数通过局部名字传递
函数参数时候对共享对象的引用,被调用者所引用。
在一个函数中对参数名赋值不影响调用者
函数首部中的参数名当函数运行时在作用域中成为新的局部名字。函数的参数名字不是调用者使用的名字的别称。
在一个函数中改变一个可变的对象参数可能影响调用者
在另一方面,既然参数只是简单的赋值给对象,函数能改变被传递的可变对象,结果可能影响调用者。见下面例子:
>>>def changer(x,y)
x = 2
y[0] = 'spam'
>>>X = 1
>>>L = [1,2]
>>>changer(X,L)
>>>X,L
(1,[''spam,2])