首页 > 代码库 > Python 五、Python函数
Python 五、Python函数
一、函数概述
1、函数的基础概念
函数是python为了代码最大程度地重用和最小化代码冗余而提供的基础程序结构。
函数是一种设计工具,它能让程序员将复杂的系统分解为可管理的部件
函数用于将相关功能打包并参数化
在python中可以创建4种函数:
全局函数:定义在模块中
局部函数:嵌套于其它函数中
lambda(匿名)函数:仅是一个表达式
方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用
函数和过程的联系:每个Python函数都有一个返回值,默认为None,也可以使用“return value”明确定定义返回值
python提供了很多内置函数
二、创建函数
1、语法
def functionName(parameter1,parameter2):
suite
2、一些相关的概念
def是一个可执行语句
因此可以出现在任何能够使用语句的地方,甚至可以嵌套于其它语句中,例如if或while中
def创建了一个对象并将其赋值给一个变量名(即函数名上面语法中的functionName)
return用于返回结果对象,其为可选,无return语句的函数,自动返回None对象
返回多个值时,彼此间使用逗号分隔,且组合为元祖形式返回一个对象
def语句运行之后,可以在程序中通过函数名后附加括号进行调用
3、parameters(参数)传递形式
默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数
但也可以通过关键字参数、默认参数或参数容器等改变这种机制
位置参数:从左向右
In [1]: a=1 In [2]: b=2 In [3]: def f1(x,y): ...: print x,y ...: In [4]: f1(3,4) 3 4 In [5]: f1(a,b) 1 2 In [6]: f1(b,a) 2 1
关键字参数:按关键名称匹配
In [7]: f1(x=1,y=2) 1 2 In [8]: f1(x=a,y=b) 1 2 In [9]: f1(y=b,x=a) 1 2
混用上面两种方式时:必须先写所有位置参数,关键字参数放后面
In [11]: f1(a,y=a) 1 1 In [12]: f1(a,y=b) 1 2 In [14]: f1(a,y=3) 1 3 In [15]: f1(x=a,b) File "<ipython-input-15-9a92e2a15371>", line 1 f1(x=a,b) SyntaxError: non-keyword arg after keyword arg
定义函数时使用默认参数:有默认值的参数
混用有默认值和无默认值的参数时,无默认值放前面,有默认值的放后面
In [23]: c=3 In [24]: def f2(x,y,z=10):print x,y,z In [25]: f2(a,b) 1 2 10 In [26]: f2(a,b,c) 1 2 3 In [27]: f2(a,5,c,) 1 5 3 In [28]: f2(a,) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-27-9538a6a12c98> in <module>() ----> 1 f2(a,) TypeError: f2() takes at least 2 arguments (1 given) In [29]: f2(a,b,c,5) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-28-934459b06043> in <module>() ----> 1 f2(a,b,c,5) TypeError: f2() takes at most 3 arguments (4 given) In [30]: def f3(x,y=10,z):print x,y,z File "<ipython-input-30-9aff61efd310>", line 1 def f3(x,y=10,z):print x,y,z SyntaxError: non-default argument follows default argument
调用函数时使用可变参数要求:
定义函数时使用*开头的参数,可用于收集任意多基于位置参数,返回元祖
定义函数时使用**开头的参数: 收集关键字参数,返回字典
使用可变参数和其它参数混合时,可变参数必须写在后面
In [38]: def f3(*x):print x In [39]: f3(1,2,3,4,5) (1, 2, 3, 4, 5) In [40]: f3(a,b,c) (1, 2, 3) In [41]: f3(a,b,c=5) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-41-98bdba155a09> in <module>() ----> 1 f3(a,b,c=5) TypeError: f3() got an unexpected keyword argument ‘c‘ In [42]: def f4(**x):print x In [43]: f4(a,b,c=5) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-43-f755ca76d443> in <module>() ----> 1 f4(a,b,c=5) TypeError: f4() takes exactly 0 arguments (3 given) In [44]: f4(a=1,b=2,c=5) {‘a‘: 1, ‘c‘: 5, ‘b‘: 2} In [45]: def f5(x,*y):print x,y In [46]: f5(a,b) 1 (2,) In [47]: f5(a,b,c) 1 (2, 3) In [48]: f5(a,b,c=3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-48-3b74f29f2a11> in <module>() ----> 1 f5(a,b,c=3) TypeError: f5() got an unexpected keyword argument ‘c‘ In [49]: def f6(x,**y):print x,y In [53]: f6(a,b,c=3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-53-c2c798b5c0d9> in <module>() ----> 1 f6(a,b,c=3) TypeError: f6() takes exactly 1 argument (3 given) In [54]: f6(a,b=2,c=3) 1 {‘c‘: 3, ‘b‘: 2} In [55]: def f7(*x,**y):print x,y In [56]: f7(a,b,c) (1, 2, 3) {} In [57]: def f8(*x,y=3):print x,y File "<ipython-input-57-9024bc5a5148>", line 1 def f8(*x,y=3):print x,y ^ SyntaxError: invalid syntax In [58]: def f8(y=3,*x):print x,y In [59]: f8(1,2,3) (2, 3) 1
可变参数解包:调用参数时,使用*开头的参数,用于将参数集合打散,从而传递任意多个
位置或关键字参数
In [64]: l1=["Sun","Mon","Tus"] In [65]: x,y,z=l1 #变量分解赋值 In [66]: x,y,z Out[66]: (‘Sun‘, ‘Mon‘, ‘Tus‘) In [67]: x Out[67]: ‘Sun‘ In [68]: print x Sun In [76]: def f9(x,y,z):print x,y,z In [80]: l1=[1,2,3] In [81]: f9(*l1) 1 2 3 In [82]: l2=[1,2,3,"a","b"] In [83]: f9(*l2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-83-be9366550ea7> in <module>() ----> 1 f9(*l2) TypeError: f9() takes exactly 3 arguments (5 given) In [84]: l3=[1,2] In [85]: f9(*l3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-85-eae6110eaf91> in <module>() ----> 1 f9(*l3) TypeError: f9() takes exactly 3 arguments (2 given) In [89]: f9(*l1) 1 2 3 In [90]: f9(**l1) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-90-13c8f289cb2c> in <module>() ----> 1 f9(**l1) TypeError: f9() argument after ** must be a mapping, not list In [93]: l3 Out[93]: [1, 2] In [95]: a Out[95]: 1 In [96]: f9(a,*l3) 1 1 2 In [97]: f9(x=5,*l3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-97-f946fbd99d70> in <module>() ----> 1 f9(x=5,*l3) TypeError: f9() got multiple values for keyword argument ‘x‘
4、匿名函数lambda
lambda是一个表达式而非语句
lambda是一个单个表达式,而不是一个代码块
lambda运算符(语法格式):
lambda args:expression
args:以逗号分隔的参数列表
expression:用到args中各参数的表达式
lambda定义的代码必须是合法的表达式,不能出现多条件语句(可使用if的三元表达式)和非表达式,如for和while等
lambda的首要用途是指定短小的回调函数
lambda将返回一个函数而不是将函数赋值给某变量名
三、命名空间与LEGB
1、命名空间
理解Python的LEGB原则是理解Python命名空间的关键,而理解Python的命名空间又是理解Python中许多语法规定的关键。所以,Python的LEGB原则就成为Python中一个非常核心的内容
白话一点讲:命名空间是对变量名的分组划分。
不同组的相同名称的变量视为两个独立的变量,因此隶属于不同分组(即命名空间)的变量名可以重复。
命名空间可以存在多个,使用命名空间,表示在该命名空间中查找当前名称。
命名空间表示变量的可见范围,一个变量名可以定义在多个不同的命名空间,相互之间并不冲突,但同一个命名空间中不能有两个相同的变量名。
比如:两个叫“张三”的学生可以同时存在于班级A和班级B中,如果两个张三都是一个班级,那么带来的麻烦复杂很多了,在Python中你不能这么干。
在Python中用字典来表示一个命名空间,命名空间中保存了变量(名字)和对象的映射关系,在Python中命名空间出现在哪些地方呢?有函数范围内的命名空间(local),有模块范围内的命名空间(global),有python内建的命名空间(built-in),还有类对象的所有属性组成的命名空间
Python一切皆对象,所以在Python中变量名是字符串对象
例如:
In [25]: a=10
表示建立字符串对象a
与Number对象10
之间的对应关系。由于这是一种映射关系,所以,可以使用键-值的形式来表示,即{name : object}。
前面已经说过,命名空间是对变量名的分组划分,所以,Python的命名空间就是对许多键-值对的分组划分,即,键值对的集合,因此:
Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系
2、命名空间的生命周期
所有的命名空间都是有生命周期的,对于python内建的命名空间,python解析器启动时创建,一直保留直至直python解析器退出时才消亡。而对于函数的local命名空间是在函数每次被调用的时候创建,调用完成函数返回时消亡,而对于模块的global命名空间是在该模块被import的时候创建,解析器退出时消亡。
3、作用域
一个作用域是指一段程序的正文区域,可以是一个函数或一段代码。
一个变量的作用域是指该变量的有效范围。Python的作用域是静态作用域,因为它是由代码中得位置决定的,而命名空间就是作用域的动态表现。
函数定义了本地作用域,而模块定义了全局作用域:
每个模块都是一个全局作用域,因此,全局作用域的范围仅限于单个程序文件
每次对函数的调用都会创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量
所有的变量名都可以归纳为本地,全局或内置的(由__builtin__模块提供)
4、LEGB原则
LEGB含义解释:
L-Local(function);函数内的名字空间
E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G-Global(module);函数定义所在模块(文件)的名字空间
B-Builtin(Python);Python内置模块的名字空间,builtin作用域,对应builtin命名空间,python内部定义的最顶层的作用域,在这个作用域里面定义了各种内建函数:open、range、xrange、list等等
前面讲到,Python的命名空间是一个字典,字典内保存了变量名与对象之间的映射关系,
因此,查找变量名就是在命名空间字典中查找键-值对。
Python有多个命名空间,因此,需要有规则来规定,按照怎样的顺序来查找命名空间,LEGB就是用来规定命名空间查找顺序的规则。
LEGB规定了查找一个名称的顺序为:local-->enclosing function locals-->global-->builtin
举例来说明:
[root@Node3 src]# vi test1.py [root@Node3 src]# cat test1.py #!/usr/local/bin/python2.7 x=1 def foo(): x=2 def innerfoo(): x=3 print "locals",x #函数内x,同时也有外部嵌套函数foo()的x,全局x innerfoo() print "enclosing function locals",x #函数内x ,全局x foo() print ‘global‘,x #全局x [root@Node3 src]# chmod +x test1.py [root@Node3 src]# ./test1.py locals 3 enclosing function locals 2 global 1
对上例稍加改动:
[root@Node3 src]# cat test1.py #!/usr/local/bin/python2.7 x=1 def foo(): x=2 def innerfoo(): # x=3 #注释掉 print "locals",x innerfoo() print "enclosing function locals",x foo() print ‘global‘,x
运行结果:
[root@Node3 src]# ./test1.py locals 2 enclosing function locals 2 #使用了外部嵌套的foo函数中的x global 1
x = 3
属于函数内部命名空间,当被注释掉之后,函数innerfoo
内部通过print x
使用x
这个名称时,触发了名称查找动作。
首先在Local命名空间查找,没有找到,然后到Enclosing function locals命名空间查找,查找成功,然后调用。
对上例再修改一下:
[root@Node3 src]# cat test1.py #!/usr/local/bin/python2.7 x=1 def foo(): x=2 def innerfoo(): global x #声明为全局变量 x=3 print "locals",x innerfoo() print "enclosing function locals",x foo() print ‘global‘,x [root@Node3 src]# ./test1.py locals 3 enclosing function locals 2 global 3 #修改了全局变量 [root@Node3 src]# cat test1.py #!/usr/local/bin/python2.7 x=1 def foo(): x=2 def innerfoo(): global x #声明全局变量 # x=3 print "locals",x innerfoo() print "enclosing function locals",x foo() print ‘global‘,x [root@Node3 src]# ./test1.py locals 1 #直接使用了全局变量,而没有按照LEGB顺序查找 enclosing function locals 2 global 1 [root@Node3 src]# cat ./test1.py #!/usr/local/bin/python2.7 x=1 def foo(): y=2 def innerfoo(): x=3 print "locals",x,y innerfoo() print "enclosing function locals",x,y foo() print ‘global‘,x,y [root@Node3 src]# ./test1.py locals 3 2 enclosing function locals 1 2 global 1 Traceback (most recent call last): #这里为什么会报错?内置模块和内置函数并不相等 File "./test1.py", line 13, in <module> print ‘global‘,x,y NameError: name ‘y‘ is not defined
[root@Node3 src]# cat test3.py #!/usr/local/bin/python2.7 l1=[1,2,3] a=3 def f1(x,y): x.pop() print x,y f1(l1,a) print l1,a l1,a [root@Node3 src]# ./test3.py [1, 2] 3 #可变对象会发生改变 [1, 2] 3
Python的闭包(工厂函数):
定义在外层函数内,却由内层函数引用的变量,在外层函数返回时,如果外层函数返回的值是内层函数,再次调用内层函数时,会记忆下外层函数的变量。
本文出自 “xiexiaojun” 博客,请务必保留此出处http://xiexiaojun.blog.51cto.com/2305291/1857859
Python 五、Python函数