先来说明下几个定义:
1,函数
在python中,函数通过def关键字、函数名和可选的参数列表定义。通过return关键字返回值。我们举例来说明如何定义和调用一个简单的函数:
1 2 3 4 5 6 7 | #coding:UTF8 def foo(): return 1 print foo() 1 |
方法体(当然多行也是一样的)是必须的,通过缩进来表示,在方法名的后面加上双括号()就能够调用函数
2,作用域
在Python中,函数会创建一个新的作用域. Python开发者可能会说函数有自己的命名空间.这就意味着在函数内部碰到一个变量的时候函数会优先在自己的命名空间里寻找.来简单举例说明本地作用域与全局作用域
1 2 3 4 5 6 7 8 9 10 11 12 | #coding:UTF8 a_string = "This is a global variable" def foo(): print locals () print globals () # doctest: +ELLIPSIS foo() #2 { 'foo' : <function foo at 0x00000000026ECF98 >, ...., 'a_string' : 'This is a global variable' ,...} {} |
内置的函数globals返回一个包含所有Python解释器知道的变量名称的字段(省略一部分)在#2调用了函数foo 把函数背部本地作用域里面的内容打印出来.可以看到,函数foo有自己的独立的命名空间,即使暂时命名空间啥也没有.
3,变量解析规则
当然并不是说在函数里就不能访问外面的全局变量.在Python的作用域规则里,创建变量一定会在当前作用域里创建一个变量,但访问或者修改变量是会现在当前作用域查找变量,没有找到匹配变量会依次向上在闭合的作用域里进行查找.so 如修改函数foo的是实现打印全局的作用域的变量也是可以
1 2 3 4 5 6 7 8 9 | #coding:UTF8 a_string = "This is a global variable" def foo(): print a_string #1 foo() This is a global variable |
在#1处,Python解释器会尝试查找变量a_string,当然在函数的本地作用域是找不到,so接着会在上层的作用域去查找
但在另外一方面,假如在函数的内部给全局变量赋值,结果会不一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #coding:UTF8 a_string = "This is a global variable" def foo(): a_string = 'Test' #1 print locals () foo() { 'a_string' : 'Test' } print a_string #2 This is a global variable |
全局变量能够被访问到(如果是可变数据类型(像list,dict这些),甚至能够被更改),但赋值就不行了,在函数内部的#1,实际上新创建了一个局部变量,隐藏全局作用域中的同名变量,可以通过打印出全局命名空间中的内容得出这个结论.也可以在#2处打印出来的a_string没有改变
4,变量生存周期
值得注意的是:变量不仅是生存在一个个的命名空间里,都有自己的生存周期,如下:
1 2 3 4 5 6 7 8 | #coding:UTF8 def foo(): x = 1 foo() print x # 1 NameError: name 'x' is not defined |
5,函数参数
Python允许想函数传递参数,参数会变成本地变量存在与函数内部
1 2 3 4 5 6 | #coding:UTF8 def foo(x): print locals () foo( 1 ) { 'x' : 1 } |
在Python中有很多的方式来定义和传递参数,简要说明下:函数的参数是必须的位置参数或是可选的命名,默认参数
1 2 3 4 5 6 7 8 9 10 11 12 | #coding:UTF8 def foo(x, y = 0 ): # 1 return x - y print foo( 3 , 1 ) # 2 2 print foo( 3 ) # 3 3 print foo() # 4 TypeError: foo() takes at least 1 argument ( 0 given) print foo(y = 1 , x = 3 ) # 5 2 |
在#1处定义了函数foo,有一个位置参数x和一个命名参数y 在#2通过常规的方式来调用函数,即使只有一个命名参数,但参数依然可以通过位置参数传递给函数.在调用函数的时候,对于命名参数y也可以完全不管就想#3所示一样.如命名参数没有接收到任何值的话,Python会自动使用声明的默认值.但不能省略第一个位置参数x,否则会像#4发生错误
python支持函数调用时的命名参数。看看#5处的函数调用,传递的是两个命名实参,这个时候因为有名称标识,参数传递的顺序也就不用在意了。
当然相反的情况也是正确的:函数的第二个形参是y,但通过位置的方式传递值给它。在#2处的函数调用foo(3,1),我们把3传递给了第一个参数,把1传递给了第二个参数,尽管第二个参数是一个命名参数。
6,嵌套函数
Python允许创建嵌套函数,就意味着可以在函数里定义函数而且现有的作用域和变量生存周期依旧适用
1 2 3 4 5 6 7 8 9 10 11 | #coding:UTF8 def outer(): x = 1 def inner(): print x # 1 inner() # 2 outer() 1 |
Python解释器需找一个叫x的本地变量,查找失败之后会继续向上层的作用域里查,这个上层的作用域定义在另外一个函数里,对于函数outer来说,变量x是一个本地变量
函数inner可以访问封闭的作用域.在#2处,可以调用函数inner,inner也仅仅是一个遵循Python变量解析规则的变量名,Python解释器会优先在outer的作用域里面对变量名inner查找匹配的变量