函数,参数及参数解构, 返回值,作用域
函数,参数及参数解构
函数
数学定义: y=f(x),y是x的函数,x是自变量.
python函数: 完成一定的功能,由若干语句组成的语句块,函数名称,参数列表构成,它是组织代码的最小单位.
函数的作用:
结构化变成对代码的最基本的封装,一般按照功能组织一段代码.
封装的目的为了复用,减少冗余代码.
代码更加简洁美观,可读易懂.
函数的分类:
内建函数: 如max(),reversed()等.
库函数: 如math.ceil()等.
函数定义,调用
def语句定义函数.
语法:
def 函数名(参数列表):
函数体(代码块)
[return 返回值]
函数名就是标识符,命名要求一样.
语句块必须缩进,约定4个空格.
python的函数没有return语句,隐式会返回一个None值.
定义中的参数列表成为形式参数,只是一种符号表达,简称形参.
调用:
函数定义,只是声明了一个函数,它不会被执行,需要调用.
调用的方式,就是函数名加上小括号,括号内写上参数.
调用时写的参数是实际参数,是实实在在传入的值,简称实参.
函数举例:
def add(x, y): result = x + y return resultout = add(4, 5)print(out) # 9
上面只是一个函数的定义,有一个函数add,接收2个参数.
计算的结果,通过返回值返回.
调用通过函数名add加2个参数,返回值可使用变量接收.
定义需要在调用前,也就是说调用时,已经被定义过了,否则抛异常NameError.
函数是可调用的对象,callable().
函数参数
参数调用时传入的参数要和定义的个数相匹配(可变参数例外).
位置参数:
def f(x,y,z) 调用使用f(1,3,5).
按照参数定义顺序传入实参.
关键字参数:
def f(x,y,z)调用使用f(x=1,y=3,z=5).
使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参顺序就可以和定义顺序不同.
传参:
f(z=None,y=10,x=[1])
f((1,),z=6,y=4.1)
f(y=5,z=6,2)
要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的.
参数规则: 参数列表参数一般顺序是,普通参数,缺省参数,可变位置参数,keyword-only参数(可带缺省值),可变关键字参数.
def fn(x,y,z=3,*arg,m=4,n,**kwargs): print(x,y,z,m,n) print(args) print(kwargs)
注:
代码应该易读易懂,而不是为难别人.
按照书写习惯定义函数参数.
函数参数默认值
参数默认值:
定义时,在形参后跟上一个值.
def add(x=4,y=5): result = x + y print(result) add(6,10)add(6,y=7)add(x=5)add()add(y=7)# add(x=5,6) # error.# add(y=8,4) # error.add(x=5,y=6)add(y=5,x=6)
注: def add(x=4, y)是错误的.
作用:
参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值为默认值.
参数非常多时,并不需要用户每次都输入所有的参数,简化函数调用.
举例:
定义一个函数login,参数名称为host,port,username,password.
def login(host='127.0.0.1', port='8080', username='ames', password='ames'): print('{}:{}@{}/{}'.format(host, port, username, password))login()login('127.0.0.1', 80, 'tom', 'tom')login('127.0.0.1', username='root')login('localhost', port=80, password=1234)login(port=80,password='python', host='www') # 运行结果: 127.0.0.1:8080@ames/ames127.0.0.1:80@tom/tom127.0.0.1:8080@root/ameslocalhost:80@ames/1234www:80@ames/python
可变参数
一个形参可以匹配任意个参数.
def add(nums): sum=0 for x in nums: sum += x return sumadd([1, 3, 5])9add([2, 4, 6])12
位置参数的可变参数: 有多个数,需要累加求和.
def add(*nums): sum = 0 print(type(nums)) for x in nums: sum += x print(sum)add(3, 6, 9)<class 'tuple'>18
在形参前使用*表示该形参是可变参数,可以接收多个实参.
收集多个实参为一个tuple.
关键字参数的可变参数:
def showconfig(**kwargs): for k,v in kwargs.items(): print('{} = {}'.format(k,v)) Ishowconfig(host='127.0.0.1', port=8080, username='ames', password=123456)username = ameshost = 127.0.0.1port = 8080password = 123456 # 运行结果:password = 123456port = 8080username = ameshost = 127.0.0.1
形参前使用**符号,表示可以接收多个关键字参数.
收集的实参名称和值组成一个字典.
可变参数混合使用:
def showconfig(username,password,**kwargs)
def showconfig(username,*args,**kwargs)
def showconfig(username,password,**kwargs,*args)
总结:
可变参数分为位置可变参数和关键字可变参数.
位置可变参数在形参前使用一个星号*.
关键字可变参数在形参前使用两个星号**.
位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict.
混合使用参数时,可变参数要放在参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要放在关键字可变参数之前.
可变参数举例:
def fn(x,y,*args,**kwargs): print(x) print(y) print(args) print(kwargs) fn(3,5,7,9,10,a=1,b='ames')35(7, 9, 10){'a': 1, 'b': 'ames'} In [10]: fn(13,15)1315(){} In [11]: fn(23,25,27)2325(27,){} fn(33,35,a=1,b='ames')3335(){'a': 1, 'b': 'ames'} fn(7,9,y=5,x=3,a=1,b='ames')---------------------------------------------------------------------------TypeError Traceback (most recent call last)13-a0c84e79923c> ----> 1 fn(7,9,y=5,x=3,a=1,b='ames') TypeError: fn() got multiple values for argument 'y' # 错误,7和9分别赋给了x,y,又y=5 x=3,x和y的值重复. In [14]:
def fn(*args, x, y, **kwargs): print(x) print(y) print(args) print(kwargs) fn(3, 5)---------------------------------------------------------------------------TypeError Traceback (most recent call last)-15----> 1 fn(3, 5) TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y' fn(13, 15, 17)---------------------------------------------------------------------------TypeError Traceback (most recent call last)-16----> 1 fn(13, 15, 17) TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y' fn(23, 25, a=1, b='ames')---------------------------------------------------------------------------TypeError Traceback (most recent call last)-17----> 1 fn(23, 25, a=1, b='ames') TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y' fn(37, 39, y=35, x=33, a=1, b='ames')3335(37, 39){'a': 1, 'b': 'ames'} In [19]:
keyword-only参数
keyword-only参数 (python3加入).
如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数,而是keyword-only参数.
In [19]: def fn(*args, x): ...: print(x) ...: print(args) ...: In [20]: fn(3, 5)---------------------------------------------------------------------------TypeError Traceback (most recent call last)20-----> 1 fn(3, 5) TypeError: fn() missing 1 required keyword-only argument: 'x' In [21]: fn(13, 15, 17)---------------------------------------------------------------------------TypeError Traceback (most recent call last)21-----> 1 fn(13, 15, 17) TypeError: fn() missing 1 required keyword-only argument: 'x' In [22]: fn(23, 25, x = 27)27(23, 25) In [23]:
注: 如上错误,args可看做已经截获了所有位置参数,x不使用关键字参数就不可能拿到实参.
以上函数如果换成def fn(**kwargs,x),会直接报语法错误,可以理解为kwargs会截获所有的关键字,就算写了x=5,x也永远得不到这个值,所以语法错误.
keyword-only参数的另一种形式:
def fn(*,x,y): print(x,y) fn(x=5,y=6)5 6
*号之后,普通形参都变成了必须给出的keyword-only参数.
可变参数和参数默认值
def fn(*args,x=5): print(x) print(args) fn() # 等价于fn(x=5)5()fn(5)5(5,)fn(x=6)6()fn(1,2,3,x=10)10(1, 2, 3)
def fn(y,*args,x=5): print('x={},y={}'.format(x,y)) print(args) fn()---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 fn() TypeError: fn() missing 1 required positional argument: 'y' fn(5)x=5,y=5() fn(x=6)---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 fn(x=6) TypeError: fn() missing 1 required positional argument: 'y' fn(1,2,3,x=10)x=10,y=1(2, 3) fn(y=17,2,3,x=10) File "", line 1 fn(y=17,2,3,x=10) ^SyntaxError: positional argument follows keyword argument fn(17,2,3,x=10)x=10,y=17(2, 3) fn(1,2,y=3,x=10)---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 fn(1,2,y=3,x=10) TypeError: fn() got multiple values for argument 'y'
def fn(x=5, **kwargs): print('x={}'.format(x)) print(kwargs) fn()x=5{} fn(5)x=5{} fn(x=6)x=6{} fn(y=3,x=10)x=10{'y': 3} fn(3,y=10)x=3{'y': 10}
参数解构
参数解构:
给函数提供实参时,可在集群类型前使用*或**,把集群类型的解构解开,提取出所有元素作为函数的实参.
非字典类型使用*解构成位置参数.
字典类型使用**解构成关键字参数.
提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配.
def add(x,y): return x+y add(*(4,5))9 add(*[4,5])Out[47]: 9 add(*{4,6})10 d = {'x':5, 'y':6} add(**d)11 add(**{'x':5, 'y':6})11def add(*iterable): result = 0for x in iterable: result += x return result add(1,2,3)6 add(*[1,2,3])6 add(*range(10))45
练习:
1.编写一个函数,能够接受至少2个参数,返回最小值和最大值.
import random def double_values(*nums): print(nums) return max(nums), min(nums)print(*double_values(*[random.randint(10, 20) for _ in range(10)])) # 两次解构# 运行结果:(18, 16, 16, 12, 13, 13, 14, 20, 18, 16)20 12
编写一个函数,接受一个参数n,n为正整数,左右两种打印方式.要求数字必须对齐.
def show(n): tail = ' '.join([str(i) for i in range(n, 0, -1)]) width = len(tail) for i in range(1, n): print("{:>{}}".format(" ".join([str(j) for j in range(i, 0, -1)]),width)) print(tail)show(12)
def showtail(n): tail = ' '.join([str(i) for i in range(n, 0, -1)]) print(tail) for i in range(len(tail)): if tail[i] == ' ': print(' '*i, tail[i+1:])showtail(12)
插入排序
函数返回值,作用域
函数的返回值
举例:
def showplus(x): print(x) return x+1 print(x+1) # 该行不会执行.print(showplus(5)) # 运行结果:56
多条return语句:
def guess(x): if x > 3: return '>3' else: return '<=3'print(guess(10))-------------------------------------------def showplus(x): print(x) return x+1 return x+2 # 该行不会执行.print(showplus(5)) # 运行结果:56
def fn(x): for i in range(x): if i > 3: return i else: print('{} is not greater than 3'.format(x))print(fn(5)) # 运行结果: 4print(fn(3)) # 运行结果:3 is not greater than 3None # 所有函数都有函数值,如果没有return语句,隐式调用return None.
总结:
python函数使用return语句返回'返回值'.
所有函数都有函数值,如果没有return语句,隐式调用return None.
return语句并不一定是函数的语句块的最后一条语句.
一个函数可以存在多个return语句,但是只有一条可以被执行.如果没有一条return语句被执行到,隐式调用return None.
如果有必要,可以显示调用return None,可以简写为return.
如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其他语句就不会被执行.
作用: 结束函数调用和返回值.
返回多个值:
def showlist(): return [1, 3, 5]print(showlist()) # 运行结果: [1, 3, 5] def showlist(): return 1, 3, 5print(showlist()) # 运行结果: (1, 3, 5)
注:
函数不能同时返回多个值.
return [1,3,5] 即指明返回一个列表,是一个list对象.
return 1,3,5 看似返回多个值,隐式的被python封装成了一个元组.
def showlist(): return 1,3,5x, y, z = showlist()print(x, y, z) # 运行结果: 1 3 5
函数嵌套
函数嵌套:
在一个函数中定义了另一个函数.
def outer(): def inner(): print('inner') print('outer') inner() outer()outerinner inner()---------------------------------------------------------------------------NameError Traceback (most recent call last)6-----> 1 inner() NameError: name 'inner' is not defined
注:
函数有可见范围,这就是作用域的概念.
内部函数不能被外部直接使用,会抛异常NameError,如上代码.
def outer(): def inner(): print('inner') return 'xx' # return None print('outer') inner() return inner(),'p'print(outer()) # 执行结果outerinnerinner('xx', 'p') # 两次return的结果组成了一个元组.
作用域
作用域: 一个标识符的可见范围即标识符的作用域.一般常说的是变量的作用域.
x = 5 def foo(): print(x) foo()5x = 5def foo(): x += 1 # x = x + 1print(x)foo()# 运行报错:UnboundLocalError: local variable 'x' referenced before assignment
全局作用域:
在整个程序运行环境中都可见.
局部作用域:
在函数,类等内部可见.
局部变量使用范围不能超过其所在的局部作用域.
In [31]: def fn1(): ...: x = 1 # 局部作用域,在fn1内. ...: def fn2(): ...: print(x) ...: In [32]: print(x)---------------------------------------------------------------------------NameError Traceback (most recent call last)32-----> 1 print(x) NameError: name 'x' is not defined In [33]:
嵌套结构:
def outer1(): o = 65 def inner(): print('inner {}'.format(o)) print(chr(o)) print('outer {}'.format(o)) inner() outer1() # 运行结果:outer 65inner 65A def outer2(): o = 65 def inner(): o = 97 print('inner {}'.format(o)) print(chr(o)) print('outer {}'.format(o)) inner() outer2() # 运行结果:outer 65inner 97a
从嵌套结构例子看出:
外层变量作用域在内层作用域可见.
内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但是这个o并没有覆盖外层作用域outer中的o.
x = 5 def foo(): y = x + 1 x += 1 print(x) foo()---------------------------------------------------------------------------UnboundLocalError Traceback (most recent call last)input----> 1 foo() input 1 def foo():---> 2 y = x + 1 3 x += 1 4 print(x) 5 UnboundLocalError: local variable 'x' referenced before assignment def foo(): y = x + 1 # x += 1 print(x) foo()5x = 5 def foo(): x += 1 return x foo()---------------------------------------------------------------------------UnboundLocalError Traceback (most recent call last)input----> 1 foo() input 1 def foo():----> 2 x += 1 3 return x UnboundLocalError: local variable 'x' referenced before assignment In [17]: 注意:x += 1 其实是 x = x + 1相当于在foo内部定义一个局部变量x,那么foo内部所有x都是这个局部变量x了.但是这个x还没完成复制,就被右边拿来做加1操作了,故报错.
全局变量: global
x = 5 def foo(): global x x += 1 return xfoo()6
使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x.
全局作用域中必须有x的定义.
如果全局作用域中没有x定义:
def foo(): global x x = 10 x += 1 print(x) foo()11 print(x)11
使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x.
但是,x = 10赋值即定义,x在内部作用域为一个外部作用域的变量赋值,所以x += 1不会报错.注意这里的x的作用域还是全局的.
global总结:
x+=1这种是特殊形式产生的错误的原因,先引用后赋值,而python动态语言是赋值才算定义,才能被引用.解决办法是在这条语句前增加x=0之类的赋值语句,或者使用global告诉内部作用域,去全局作用域查找变量定义.
内部作用域使用x=5之类的赋值语句会重新定义局部作用域使用的变量x,但是一旦这个作用域中使用的global声明为全局的,那么x=5相当于在为全局作用域的变量x赋值.
global使用原则:
外部作用域变量在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离.
如果函数需要使用外部全局变量,使用函数的形参传参解决.
函数值传递和引用传递(包括形式参数和实际参数的区别)
通常情况下,定义函数时都会选择有参数的函数形式,函数参数的作用是传递数据给函数,令其对接收的数据做具体的操作处理。在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是:
· 形式参数:在定义函数时,函数名后面括号中的参数就是形式参数,例如:
#定义函数时,这里的函数参数 obj 就是形式参数
def demo(obj)
print(obj)
·
· 实际参数:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数。例如:
a = "C语言中文网"
#调用已经定义好的 demo 函数,此时传入的函数参数 a 就是实际参数
demo(a)
·
实参和形参的区别,就如同剧本选主角,剧本中的角色相当于形参,而演角色的演员就相当于实参。
明白了什么是形参和实参后,再来想一个问题,那就是实参是如何传递给形参的呢?Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:
1. 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
2. 引用(地址)传递:适用于实参类型为可变类型(列表,字典);
值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。例如,定义一个名为 demo 的函数,分别为传入一个字符串类型的变量(代表值传递)和列表类型的变量(代表引用传递):
def demo(obj) : obj += obj print("形参值为:",obj)print("-------值传递-----")a = "C语言中文网"print("a的值为:",a)demo(a)print("实参值为:",a)print("-----引用传递-----")a = [1,2,3]print("a的值为:",a)demo(a)print("实参值为:",a)
运行结果为:
-------值传递-----a的值为:C语言中文网形参值为: C语言中文网C语言中文网实参值为: C语言中文网-----引用传递-----a的值为:[1, 2, 3]形参值为: [1, 2, 3, 1, 2, 3]实参值为: [1, 2, 3, 1, 2, 3]
分析运行结果不难看出,在执行值传递时,改变形式参数的值,实际参数并不会发生改变;而在进行引用传递时,改变形式参数的值,实际参数也会发生同样的改变。
Python匿名函数详解
在编程语言中,函数的应用:1. 代码块重复,这时候必须考虑用到函数,降低程序的冗余度2. 代码块复杂,这时候可以考虑用到函数,降低程序的可读性在Python,有两种函数,一种是def定义,一种是lambda函数#假如要求两个数之和,用普通函数或匿名函数如下:1. def func(x,y):return x+y2. lambda x,y: x+y在编程语言中,C/C++/Java属于过程式编程,而匿名函数(lambda)一般应用于函数式编程中,举个简单例子也许比较好理解,对于一个列表,要求大于3的元素.过程式编程实现: 也就是常规的方法>>> L1 = [1,2,3,4,5]>>> L2 = []>>> for i in L1:if i>3:L2.append(i)>>> L2[4, 5]函数式编程实现: 运用filter,给其一个判断条件即可>>> def func(x): return x>3 #在函数中>>> filter(func,[1,2,3,4,5])[4, 5]如果运用匿名函数,则更加精简,一行就可以了:>>> filter(lambda x:x>3,[1,2,3,4,5])[4, 5]总结: 从中可以看出,lambda一般应用于函数式编程,代码简洁,常和reduce,filter等函数结合使用。格式如下:解构上面的例子x 为lambda函数的一个参数:分割符x>3 则是返回值,在lambda函数中不能有return,其实:后面就是返回值为什么要用匿名函数?1. 程序一次行使用,所以不需要定义函数名,节省内存中变量定义空间2. 如果想让程序更加简洁时。匿名函数几个规则:1. 一般也就一行表达式,必须有返回值2. 不能有return3. 可以没有参数,可以有一个或多个参数>>> def func(x): x+y>>> func>>> lambda x: x+y at 0x0000000002F48D68>无参匿名函数:------>>> t = lambda : True #分号前无任何参数>>> t()True等价于下面的def定义的函数>>> def func(): return True>>> func()True------>>> s = "this is\na\ttest" #建此字符串按照正常情形输出>>> s'this is\na\ttest'>>> print s.split() #split函数默认分割:空格,换行符,TAB['this', 'is', 'a', 'test']>>> ' '.join(s.split()) #用join函数转一个列表为字符串'this is a test'等价于>>> (lambda s:' '.join(s.split()))("this is\na\ttest")带参数匿名函数>>> lambda x: x**3 #一个参数>>> lambda x,y,z:x+y+z #多个参数>>> lambda x,y=3: x*y #允许参数存在默认值匿名函数调用#直接赋值给一个变量,然后再像一般函数调用------>>> c = lambda x,y,z: x*y*z>>> c(2,3,4)24------>>> c = lambda x,y=2: x+y #使用了默认值>>> c(10) #不输的话,使用默认值212------>>> a = lambda *z:z #*z返回的是一个元祖>>> a('Testing1','Testing2')('Testing1', 'Testing2')------>>> c = lambda **Arg: Arg #arg返回的是一个字典>>> c(){}#直接后面传递实参------>>> (lambda x,y: x if x> y else y)(101,102)102------>>> (lambda x:x**2)(3)9#lambda返回的值,结合map,filter,reduce使用>>> filter(lambda x:x%3==0,[1,2,3,4,5,6])[3, 6]等价于下面的列表推导式>>> l = [x for x in [1,2,3,4,5,6] if x%3==0]>>> l[3, 6]嵌套使用#lambda嵌套到普通函数中,lambda函数本身做为return的值------>>> def increment(n):... return lambda x: x+n...>>> f=increment(4)>>> f(2)6------>>> def say():... title = 'Sir,'... action= lambda x: title + x... return action...>>> act = say()>>> act('Smith!')'Sir,Smith!'大量例子:例01: 字符串联合,有默认值,也可以x=(lambda...)这种格式>>> x = (lambda x="Boo",y="Too",z="Zoo": x+y+z)>>> x("Foo")'FooTooZoo'例02: 和列表联合使用>>> L = [lambda x:x**2,\lambda x:x**3,\lambda x:x**4]>>> for f in L:... print f(2)...4816也可以如下面这样调用>>> print L[0](3)9例03: 和字典结合使用>>> key = 'B'>>> dic = { 'A': lambda: 2*2,\... 'B': lambda: 2*4,\... 'C': lambda: 2*8}>>> dic[key]()8例04: 求最小值>>> lower = lambda x,y: x if x>>> lower('aa','ab')'aa'例05: 和map及list联合使用>>> import sys>>> showall = lambda x:list(map(sys.stdout.write,x))>>> showall(['Jerry\n','Sherry\n','Alice\n'])JerrySherryAlice>>> showall(['Jerry','Sherry','Alice'])JerrySherryAlice等价于下面>>> showall = lambda x: [sys.stdout.write(line) for line in x]>>> showall(('I\t','Love\t','You!'))I Love You![None, None, None]例06: 在Tkinter中定义内联的callback函数import sysfrom Tkinter import Button,mainloopx = Button(text='Press me',command=(lambda:sys.stdout.write('Hello,World\n')))x.pack()x.mainloop()>>>Hello,World!Hello,World!例07: lambda和map联合使用,>>> out = lambda *x: sys.stdout.write(' '.join(map(str,x)))>>> out('This','is','a','book!\n')This is a book!例08: 判断字符串是否以某个字母开头>>> print (lambda x: x.startswith('B'))('Bob')True----->>> Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']>>> B_Name= filter(lambda x: x.startswith('B'),Names)>>> B_Name['Bob', 'Barbara']例09: lambda和map联合使用:>>> squares = map(lambda x:x**2,range(5))>>> squares[0, 1, 4, 9, 16]例10. lambda和map,filter联合使用:>>> squares = map(lambda x:x**2,range(10))>>> filters = filter(lambda x:x>5 and x<50,squares)>>> filters[9, 16, 25, 36, 49]例11. lambda和sorted联合使用#按death名单里面,按年龄来排序#匿名函数的值返回给key,进来排序>>> death = [ ('James',32),('Alies',20),('Wendy',25)]>>> sorted(death,key=lambda age:age[1]) #按照第二个元素,索引为1排序[('Alies', 20), ('Wendy', 25), ('James', 32)]例12. lambda和reduce联合使用>>> L = [1,2,3,4]>>> sum = reduce(lambda x,y:x+y,L)>>> sum10例13. 求2-50之间的素数#素数:只能被1或被自己整除的数>>> nums = range(2,50)>>> for i in nums:nums = filter(lambda x:x==i or x % i,nums)>>> nums[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]例14. 求两个列表元素的和>>> a = [1,2,3,4]>>> b = [5,6,7,8]>>> map(lambda x,y:x+y, a,b)[6, 8, 10, 12]例15. 求字符串每个单词的长度>>> sentence = "Welcome To Beijing!">>> words = sentence.split()>>> lengths = map(lambda x:len(x),words)>>> lengths[7, 2, 8]写成一行:>>> print map(lambda x:len(x),'Welcome To Beijing!'.split())
一. 递归函数
递归函数就是函数在内部调用自身。
必须有一个明确的递归结束条件,称为递归出口。
注意: 切勿忘记递归出口,避免函数无限调用。
函数调用自身的实现:
其实函数每次被调用时都会创建一个新命名空间,也就是当函数调用“自身”时,实际上运行的是两个不同的函数(也可以说一个函数具有两个不同的命名空间)。
【例】定义阶乘函数
递归实现:
>>> def fact(n):
... if n == 1:
... return 1
... else:
... return n * fact( n-1 )
...
>>>
>>> fact(1)
1
>>> fact(2)
2
>>> fact(4)
24
>>> fact(5)
120
fact(5) 计算过程:
factorial(5) # 第 1 次调用使用 5
5 * factorial(4) # 第 2 次调用使用 4
5 * (4 * factorial(3)) # 第 3 次调用使用 3
5 * (4 * (3 * factorial(2))) # 第 4 次调用使用 2
5 * (4 * (3 * (2 * factorial(1)))) # 第 5 次调用使用 1
5 * (4 * (3 * (2 * 1))) # 从第 5 次调用返回
5 * (4 * (3 * 2)) # 从第 4 次调用返回
5 * (4 * 6) # 从第 3次调用返回
5 * 24 # 从第 2 次调用返回
120 # 从第 1 次调用返回
迭代实现:
>>> def fact(n):
... result = 1
... for i in range(2,n+1):
... result *= i
... return result
...
>>>
>>> fact(1)
1
>>> fact(2)
2
>>> fact(5)
120
递归的优缺点:
优点:
递归使代码看起来更加整洁、优雅
可以用递归将复杂任务分解成更简单的子问题
使用递归比使用一些嵌套迭代更容易
缺点:
递归的逻辑很难调试、跟进
递归调用的代价高昂(效率低),因为占用了大量的内存和时间。
二. 尾递归
使用递归函数需要注意防止栈溢出。在计算机中函数调用是通过栈这种数据结构实现的。每当进入一个函数调用,栈就会加一层栈帧;每当函数返回就会减一层栈帧。由于栈的大小不是无限的,因此递归调用次数过多会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化。
尾递归是指函数返回时调用函数本身,并且 return 语句不能包含表达式。这样,编译器和解释器就可以对尾递归进行优化。使递归本身无论调用多少次都只占用一个栈帧,从而避免栈溢出的情况。
>>> def fact(n):
... return fact_iter(n,1)
...
>>>
>>> def fact_iter(num , result):
... if num == 1:
... return result
... return fact_iter(num-1 , num * result)
...
>>>
>>>
>>> fact(1)
1
>>> fact(2)
2
>>> fact(5)
120
python 递归、递推算法实例(小明爬楼梯)
递推算法递归算法例题:假设-段楼梯共15个台阶,小明-步最多能上3个台阶。编写程序计算小明上:这段楼梯一共有多少种方法。要求给出递推法和递归法两种代码。
递归法:
#递归法
defdemo(n):
d={1:1,2:2,3:4}
if n in d.keys():
return d[n]
else:
return demo(n-1)+demo(n-2)+demo(n-3)
print(demo(15))
递推法
#递推法
defdemo(n): # n最少为3,否则输出4错误
a=1
b=2
c=4
for i inrange(n-3):
c,b,a=a+b+c,c,b
return c
print(demo(4))
”分治法“
基本思想:
将一个问题分解为多个规模比较小的子问题,这些子问题互相独立并与原问题解决方案相同,递归求解这些子问题,然后将这些子 问题的解合并得到原问题的解。
分治法求解的基本步骤:
1、将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
2、明确最小子问题,递归最终的结束条件;
3、划分子问题,调用递归方法;
4、合并回溯后的结果
分治经典问题
(1)二分查找
(2)归并排序
(3)快速排序