函数的5种参数类型
POSITIONAL_OR_KEYWORD(位置参数或关键字参数)
VAR_POSITIONAL(可变参数)
KEYWORD_ONLY(关键字参数)
VAR_KEYWORD(可变关键字参数)
POSITIONAL_ONLY(位置参数)
POSITIONAL_OR_KEYWORD如其名所见,既可以用位置传参,也可以用关键字传参
def foo(name):
print(name)
foo('a') # a
foo(name='a') # a
VAR_POSITIONAL是可变参数,通过*来声明,它会把接收到的值存入一个元组
def foo(*args):
print(args)
foo(1, 2, 3) # (1,2,3)
KEYWORD_ONLY只能通过关键字传参,这种参数会在VAR_POSITIONAL参数类型的后面,只能通过指定关键字来传参,不可以用位置传参
def f(name, *, val):
print(name, val)
f('a', val=1) # a 1
VAR_KEYWORD是可变关键字参数,通过前缀**来声明,这种参数类型可以接收0个或多个参数,并存入一个字典
def foo(**kw):
for key, value in kw.items():
print('%s=%s' % (key, value), end=' ')
foo(a=1, b=2, c=3) # a=1 b=2 c=3
高版本的Python无法创建一个POSITIONAL_ONLY类型的参数,但是有些使用C语言实现且不接收关键字参数的函数(如divmod)支持
默认参数
- VAR系列的两个参数(可变参数、可变的关键词参数)不允许设置默认参数,而另外两个参数(位置和关键词参数、关键词参数)可以设置默认参数。 因为VAR_POSITIONAL的默认参数是tuple()空元祖,而VAR_KEYWORD的默认参数是dict()空字典。
- 默认参数的位置POSITIONAL_OR_KEYWORD类型的默认参数一定要放在后面,否则会报错。
- KEYWORD_ONLY虽然没有强制要求,因为都是用关键字传参,谁先谁后无所谓,但最好还是放在后面。
- 默认参数最好不要设置为可变类型(如dict、list、set)。 如果把默认参数设置为可变类型,并在函数中改变了该参数的值,下次再调用它就不再是默认值了。
return
return作用:
- 结束函数
- 返回对象
不写return语句会默认返回None
高阶函数
- python一切皆对象,函数也是对象。
- 函数本身可以赋值给变量,即变量可以指向函数对象。
- 函数名其实就是指向函数对象的变量。
- 因为变量可以指向函数对象,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数称之为高阶函数。
- 函数对象也可以作为函数的返回值。
- 内置的高阶函数有filter,map,reduce。
匿名函数
python 使用 lambda 来创建匿名函数。
- lambda只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法
lambda [arg1 [,arg2,.....argn]]:expression
变量作用域
L:local 函数内部作用域
E:enclosing 外部嵌套函数的作用域
G:global 全局作用域
B:build-in 内置作用域
要修改global作用域中变量的值,要用global关键字对变量进行声明
num = 1
def foo():
global num # 需要使用 global 关键字声明
print(num) # 1
num = 123
print(num) # 123
foo()
print(num) # 123
内部函数要修改外部函数的变量的值,用nonlocal关键字声明变量
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num) # 100
inner()
print(num) # 100
outer()
递归
递归条件:
- 需要调用自身函数
- 要有递归出口,也就是结束条件
递归调用实际上是函数自己在调用自己,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈予以记录。而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。同一个问题,如果递归解决方案的复杂度不明显优于其它解决方案的话,那么使用递归是不划算的。方式有些问题使用迭代算法是很难甚至无法解决的(比如汉诺塔问题)。这时递归的作用就显示出来了。