Python/函数
函数定义
[编辑]Python函数定义的通常格式为:
def 函数名(参数列表):
#函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明
函数体
#return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None
参数的传值与传引用
[编辑]Python中,对象有类型;变量仅仅是对象的一个引用,变量没有类型。 string、tuple和 numbers 是不可更改的类型(immutable type),给它们的变量赋值其实是指向了新的对象;而 list,dict 等则是可以修改的类型(mutable type)。Python函数的参数传递,在函数内部最初都是访问实参那个对象而不是复制一份:
- 不可变类型:在函数内部“修改”参数的值,只是引用到另一个对象,不影响实参本身。
- 可变类型:在函数内部修改参数的值后,函数外部的实参也会受影响
命名实参与不定长参数
[编辑]Python的函数实参可分为两类:
- 无命名的非关键字实参(non-keyword argument)或位置实参(positional argument)
- 关键字实参(keyword argument)或译作命名实参。关键字实参之间的顺序可以任意。
关键字实参必须出现在非关键字实参右侧,否则报错“positional argument follows keyword argument”。
函数形参可分为
- 无缺省值的形参
- 有缺省值的形参
*args
**kwargs
,称为“var-keyword argument”。*
(单个星号)/
(单个斜杠)
def foo(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): ------------- ------------- ------------- | | | | Positional or keyword | | - Keyword only -- Positional only
def bar(param1, param2=default_value,*args, kwd1, **kwargs):
函数的形参表必须遵守下属规则:
- 出现在
*args
或者*
之后的形参都只能对应命名实参 - 带缺省值的形参,其右侧一直到
*
的所有参数都必须是有缺省值形参。参数的缺省值在函数定义时从左至右求值。 - 形参中没有名字的单独一个星号
*
,只是起到分隔符作用,并不对应于实参,表示实参表此处向右都必须是命名实参。这适用于当名称有意义并且函数定义通过显式名称更容易理解时,只用关键字实参;或者您希望防止用户依赖所传递参数的位置。 - 从Python 3.8开始,形参中没有名字的单独一个斜杠
/
,只是起到分隔符作用,并不对应于实参,表示实参表此处向左都必须是位置形参。这适用于调用函数时强制实参的求值顺序;或者以后修改形参名字但不影响调用者使用。“仅是位置形参”(Positional only)的名字将不再尝试与关键字实参的名字匹配,这意味着同名的关键字实参可以被**kwargs
收集,这称为Recap。 - 形参
**kwargs
必须出现在*args
的右侧,否则报错“invalid syntax” - 形参
**kwargs
必须是最后一个形参,否则报错“arguments cannot follow var-keyword argument” - 通常,不在一个函数同时使用
*args
与**kwargs
,以免混淆。
调用函数时,实参可分为四类:
- 位置实参
- 关键字实参,形如 name=value
- 单星号解包实参,形如*args
- 双星号解包实参,形如**kwargs
实参在实参表中出现的规则:
- 实参表可分为左、中、右三部分,任何部分都可以为空
- 实参表左部分,任意数量的位置实参可以和任意数量的单星号解包实参混杂出现;
- 实参表中部分,任意数量的关键字实参可以和任意数量的单星号解包实参混杂出现;
- 实参表右部分,任意数量的关键字实参可以和任意数量的双星号解包实参混杂出现。
函数调用时,形实参数结合的规则:
- 首先形参表的“仅位置形参”与位置实参从左向右结合。
- 其次形参表的“仅关键字形参”与关键字实参实参结合。
- 再次对于“可位置可关键字形参”:
- 首先与关键字实参匹配。
- 其次对于不能匹配关键字实参的“可位置可关键字形参”,再与位置实参匹配。
- 最后还剩下未匹配的位置实参组成一个元组收集到*args形参,还剩下未匹配的关键字实参组成一个字典收集到**kwargs形参。
形参实参结合时一些语法报错情形:
- 已经匹配的非关键字形参,其对应的实参的名字不能再作为关键字实参出现,否则报错“duplicate value for the same argument”或“got multiple values for argument”。
- 特例:“仅是位置形参”(Positional only)的名字将不再尝试与关键字实参的名字匹配,这意味着同名的关键字实参可以被
**kwargs
收集,这称为Recap。
- 特例:“仅是位置形参”(Positional only)的名字将不再尝试与关键字实参的名字匹配,这意味着同名的关键字实参可以被
- 如果没有
**kwargs
形参,关键字实参的名字不出现在形参表中则报错“an unexpected keyword argument” - 不能被匹配的形参,将报错“missing 1 required positional argument”或“missing 1 required keyword-only argument”
def test1(a, b, c=0, *args, **kwargs):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kwargs)
def test2(a, b, c=0, *args, d, **kwargs):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'args=', args, 'kw =', kwargs)
# 定义一个元组和字典用作参数传入
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
test1(*args, **kw)
# a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
test2(*args, **kw)
#a = 1 b = 2 c = 3 d = 99 args= (4,) kw = {'x': '#'}
def test3(a, *args,d, c=0):
print('a =', a, 'c =', c, 'd =', d, 'args=', args)
args=(1,3,5)
test3(-1, d=101, *args) # 这是关键字实参与单星号解包实参混杂情形
#输出为:a = -1 c = 0 d = 101 args= (1, 3, 5)
函数返回多个值
[编辑]返回多个值时自动作为一个tuple。函数调用者可以使用多个变量来接受多个返回值(即tuple中的各个成分)。
嵌套函数
[编辑]嵌套函数(nested function),需要注意:
- 内部函数可以读写访问外部函数的可变(mutable)变量(如 list、dict 等)
- 内部函数可以读访问外部函数的不可变(immutable)变量(如 int、str、tuple 等)
- 内部函数使用nonlocal关键字,可以读写访问任意层次的外部函数的不可变(immutable)变量
- 内部函数访问的外部函数的变量,可以定义在内部函数之后。
函数闭包
[编辑]闭包是指嵌套函数作为外部函数的返回值。因为函数是头等对象,所以函数对象可以作为实参、返回值。嵌套函数可以使用外部函数的变量,即使外部函数已经运行结束了。例如
def adder(outer_argument): # outer function
def adder_inner(inner_argument): # inner function, nested function
return outer_argument + inner_argument # Notice outer_argument
return adder_inner
add5 = adder(5) # a function that adds 5 to its argument
add7 = adder(7) # a function that adds 7 to its argument
print add5(3) # prints 8
print add7(3) # prints 10
匿名函数
[编辑]匿名函数的语法如下:
lambda 可选的形参表:expression
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。lambda 函数拥有自己的命名空间,可以访问包含它的作用域中的变量。
def make_incrementor(n): return lambda x: x + n
偏函数
[编辑]对一个函数的部分参数绑定值后,得到的函数为偏函数:
偏函数名 = partial(func, *args, **kwargs)
其中,func 指的是要封装的原函数,*args 和 **kwargs 分别用于接收非命名实参和命名实参。
from functools import partial
#定义个原函数
def display(name,age):
print("name:",name,"age:",age)
#定义偏函数,其封装了 display() 函数,并为 name 参数设置了默认参数
GaryFun = partial(display,name = 'Gary')
#由于 name 参数已经有默认值,因此调用偏函数时,可以不指定
GaryFun(age = 13) #必须以命名实参方式传入,否则会报错:TypeError: display() got multiple values for argument 'name'
def mod( n, m ):
return n % m
#定义偏函数,并设置参数 n 对应的实参值为 100
mod_by_100 = partial( mod, 100 )
print(mod( 100, 7 ))
print(mod_by_100( 7 ))
eval()与exec()
[编辑]eval(source, globals=None, locals=None, /) exec(source, globals=None, locals=None, /)
expression参数是一个字符串,代表要执行的语句 。该语句受后面两个字典类型参数 globals 和 locals 的限制,只有在 globals 字典和 locals 字典作用域内的函数和变量才能被执行。
globals参数管控的是一个全局的命名空间,即 expression 可以使用全局命名空间中的函数。如果只是提供了 globals 参数,而没有提供自定义的 __builtins__,则系统会将当前环境中的 __builtins__ 复制到自己提供的 globals 中,然后才会进行计算;如果连 globals 这个参数都没有被提供,则使用 Python 的全局命名空间。
locals参数管控的是一个局部的命名空间,和 globals 类似,当它和 globals 中有重复或冲突时,以 locals 的为准。如果 locals 没有被提供,则默认为 globals。
eval() 执行完会返回结果,而 exec() 执行完不返回结果
map()、filter()、reduce()
[编辑]由于 map() 函数是直接由用 C 语言写的,运行时不需要通过 Python 解释器间接调用,并且内部做了诸多优化,所以相比其他方法,此方法的运行效率最高。
reduce() 函数在 Python 3.x 中已经被移除,放入了 functools 模块,因此在使用该函数之前,需先导入 functools 模块。
函数注解
[编辑]“函数注解是关于用户自定义函数使用类型的可选的元信息”。也就是说,函数注解的用途为“函数中的形参和返回值提供类型提示信息”。注意,函数注解没有任何语法上的意义,只是为函数参数和返回值做注解,并在运行获取这些注解,仅此而已。换句话说,为函数做的注解,Python不做检查,不做强制,不做验证,什么操作都不做,函数注解对Python解释器没任何意义。
def f(ham:str,egg:str='eggs')->str:
pass
print(f.__annotations__)
#输出结果为:{'ham': <class 'str'>, 'egg': <class 'str'>, 'return': <class 'str'>}