函数与lambda表达式
函数
Python中万物皆对象,函数也不例外,python中低阶函数既可以做高阶函数的参数也可以做高阶函数的返回值
函数定义
函数以关键词def开头,后接函数名、小括号()和冒号:
函数以 return [表达式] 结束;若无return语句,则默认返回None;若无表达式,则返回None
def functionname(parameters):
function_suite
return [expression]
函数参数
Python函数有多种参数形态,由简到繁为:
位置参数 (positional argument)
默认参数 (default argument)
可变参数 (variable argument)
关键字参数 (keyword argument)
命名关键字参数 (name keyword argument)
参数组合
- 位置参数
位置参数在调用函数 (call function) 时位置要固定
def functionname(arg1):
"函数_文档字符串"
function_suite
return [expression]
- 默认参数
默认参数在函数调用时已经赋值,调用函数时,默认参数的值如果没有传入,则被认为是默认值;
默认参数需要放在位置参数之后,不然会报错;
函数调用时,若指定给形参赋值,则参数顺序可以与函数命名时的顺序不一致,解释器能够用参数名匹配参数值;
def functionname(arg1, arg2=v):
"函数_文档字符串"
# arg1是位置参数,arg2是默认参数,v 为默认值
function_suite
return [expression]
- 可变参数
可变参数是指传入参数的个数不固定,既可以没有,也可以有多个,在函数调用时会自动组合成元组;
可变参数前要加 *,以和其他参数区分
可变参数会接收所有未命名的变量参数
def functionname(arg1, arg2=v, *args):
"函数_文档字符串"
function_suite
return [expression]
- 关键字参数
关键字参数与可变参数相似,都允许传入零到任意个参数,但命名关键字参数接收的是字典
关键字参数前要加 **,以区分其他参数
def functionname(arg1, arg2=v, *args, **kw):
"函数_文档字符串"
function_suite
return [expression]
- 命名关键词参数
命名关键字参数用于限制关键字参数的名字,定义格式是 *, nkw;调用的函数要使用命名关键字参数时,不能缺少参数名
def functionname(arg1, arg2=v, *args, *, nkw, **kw):
"函数_文档字符串"
function_suite
return [expression]
- 参数组合
函数参数定义的顺序只有两种:
a. 位置参数、默认参数、可变参数和关键字参数。
b. 位置参数、默认参数、命名关键字参数和关键字参数。
变量作用域
定义在函数内部的变量拥有局部作用域,该变量称为局部变量;定义在函数外部的变量拥有全局作用域,该变量称为全局变量;局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。
当内部作用域想修改外部作用域的变量时,需要global和nonlocal关键字;global关键字在函数或其他局部作用域中使用全局变量,nonlocal关键字在函数或其他作用域中使用外层(非全局)变量
-
闭包
闭包是一种特殊的内嵌函数,如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包。
通过闭包可以访问外层非全局作用域的变量,这个作用域称为闭包作用域;
闭包的返回值通常是函数;
修改闭包作用域中的变量需要 nonlocal 关键字; -
递归
一个函数在内部调用自身本身,这个函数就是递归函数
递归采用的是一种自上而下的思想,但计算开销大,容易导致栈溢出
lambda表达式
python中的函数可以分为两类:一类是关键字def定义的正规函数;另一类是关键字lambda定义的匿名函数,即没有函数名的函数
匿名函数的定义
语法结构:lambda argument_list: expression
argument_list 是函数参数,可以是位置参数、默认参数、关键字参数,和正规函数里的参数类型一样;
expression 是一个表达式,输入函数参数,输出函数值;不需要 return 语句。
note:匿名函数有自己的命名空间,不能访问自己参数列表之外或全局命名空间里的参数
lbd_sqr = lambda x: x ** 2
print(lbd_sqr)
# <function <lambda> at 0x000000BABB6AC1E0>
y = [lbd_sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5)) # 15
匿名函数的应用
函数式编程 是指代码中每一块都是不可变的,都由纯函数的形式组成。纯函数是指函数本身相互独立、互不影响,对于相同的输入,总会有相同的输出,没有任何副作用。
匿名函数 常常应用于函数式编程的高阶函数中,主要有两种形式:
参数是函数 (filter, map)
返回值是函数 (闭包,closure)
filter(function, iterable)
过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,可以使用 list() 转换为列表
odd = lambda x: x % 2 == 1
templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(templist)) # [1, 3, 5, 7, 9]
map(function, *iterables)
根据提供的函数对指定序列做映射
m = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m1))
# [3, 7, 11, 15, 19]
练习题
-
怎么给函数编写文档?
在函数开头添加的字符串即视为文档字符串,表示函数的注释,将作为函数的一部分存储起来,可通过函数的__doc__
属性访问 -
怎么给函数参数和返回值注解?
函数参数的注解可以在定义函数时,在参数的后面加上冒号和要求使用的类型: type
;但参数注解不会强制改变传入参数的类型,只能作为提示
返回值的注解在定义函数的冒号之前,小括号之后添加 ‘->’ 和返回值类型,即-> type
这些注解都会以字典的形式存储在函数的__annotations__
属性访问
def accumlate(x:int, y:int) -> int:
return x*y
print(accumlate.__annotations__)
# {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
-
闭包中,怎么对数字、字符串、元组等不可变元素更新。
可通过使用关键词nonlocal实现 -
分别根据每一行的首元素和尾元素大小对二维列表 a = [[6, 5], [3, 7], [2, 8]] 排序。(利用lambda表达式)
a = [[6, 5], [3, 7], [2, 8]]
b = sorted(a, key=lambda x: x[0])
c = sorted(a, key=lambda x: x[-1])
- 利用python解决汉诺塔问题?
汉诺塔
class Solution:
def hanota(self, A: List[int], B: List[int], C: List[int]) -> None:
n = len(A)
self.move(n, A, B, C)
# 定义 move 函数移动汉诺塔
def move(self,n, A, B, C):
if n == 1:
C.append(A[-1])
A.pop()
return
else:
self.move(n-1, A, C, B) # 将A上面n-1个通过C移到B
C.append(A[-1]) # 将A最后一个移到C
A.pop() # 这时,A空了
self.move(n-1,B, A, C) # 将B上面n-1个通过空的A移到C