有时候,函数调用的参数可能是以元组、列表或字典的形式存在。可以通过使用“*”或“**”操作符将这些参数解包到函数内部以供调用。以下面的函数为例,该函数接受两个位置参数,并打印出两个参数的值。
如果提供给函数的参数值是以列表形式存在,那么我们可以直接将这些值解包到函数中,如下所示:
类似的,当我们有关键词时,可以使用字典来存储kwarg到值的映射关系,并利用“**”操作符将关键字参数解包到函数,如下所示:
有时候,当定义一个函数时,我们之前可能不知道参数的数量。这就导致了下面签名的函数定义:
“*args”参数表示未知的位置参数序列长度,而“**kwargs”代表包含关键字和值映射关系的字典,它可以包含任意数量的关键字和值映射,并且在函数定义中“*args”必须位于“**kwargs”前面。下面的代码演示了这种情况:
必须向函数提供正常的参数,但“*args”和“**kwargs”却是可选的,如下所示:
在函数调用中,普通参数以正常方式提供,而可选参数则可以通过解包的形式到达函数调用中。
Python也支持匿名函数,这些函数使用lambda关键字创建。Python中Lambda表达式的形式如下所示:
ambda表达式返回评估后的函数对象,并且具有与命名函数相同的属性。在Python中,Lambda表达式通常只用于非常简单的函数,如下所示:
上面的lambda表达式的功能与下面命名函数的功能相同:
嵌套函数和闭包
在一个函数内部定义函数就创建了嵌套函数,如下所示:
在这种类型的函数定义中,函数inner只在函数outer内部有效,所以当内部函数需要被返回(移动到外部作用范围)或被传递给另一个函数时,使用嵌套函数通常比较方便。在如在上面的嵌套函数中,每次调用外部函数时都会创建一个新的嵌套函数实例,这是因为,在每次执行外部函数时,都会执行一次内部函数定义,而其函数体则不会被执行。
嵌套函数可以访问创建它的环境,这是python函数定义语义的直接结果。一个结果是,外部函数中定义的变量可以在内部函数中引用,即使外部函数已经执行结束。
当内部嵌套的函数引用外部函数中的变量时,我们说嵌套函数相对于引用变量是封闭的。我们可以使用函数对象的一个特殊属性“__closure__”来访问这个封闭的变量,如下所示:
Python中的闭包有一个古怪的行为。在Python 2.x及更低版本中,指向不可变类型(例如字符串和数字)的变量不能在闭包内反弹。下面的例子说明了这一点:
一个相当不可靠的解决方案是,使用一个可变类型来捕获闭包,如下所示:
Python 3引入了“nonlocal”关键字用来解决下面所示的闭包范围问题。在本教程命名空间一节中,我们更加详细地描述了这些古怪用法。
闭包可以用来维持状态(与类作用不同),在一些简单的情况下,还可以提供一种简洁性与可读性比类更强的解决方案,我们使用中的一个日志例子来说明这一点。假设一个非常简单的日志API,它使用基于类的面向对象思想,并可以在不同级别上打印日志:
相同的功能也可以使用闭包来实现,如下所示: