目录
简单函数
匿名函数(lambda表达式)
嵌套函数
返回函数和闭包
高阶函数
偏函数
函数的装饰器
简单函数
所谓函数,就是一种可重复使用的,用来实现单一或相关联功能的代码段。在程序中,我们将一些常用的功能写成一个函数,不仅提高了程序的模块性,还能大大提高代码的重复利用率,这样我们就可以少敲很多代码了~话不多说,入正题
定义一个函数需要满足以下要求:
1. 函数代码块以 def 关键字开头,后接函数名圆括号和冒号。
2. 函数名需要满足变量命名规则,参数需要在圆括号内定义,可以定义任意多个。
3. 函数的内容必须缩进,并以 return 语句或缩进结束作为函数内容的结束
4. 以下不是要求,而是说明
5. return 语句可以在函数结束时返回给调用方一个或多个对象,该对象可以是整型,可以是字符串,也可以是
函数。总之只要是对象就可以返回。刚才忘了说Python中一切皆为对象!
6. Python 中的函数总会有返回值。当函数没有指定返回对象,如没有 return 语句或只有一个 return ,则函
数会返回 None
7. 函数的调用方法是函数名加圆括号,如果函数有参数,则圆括号内需要有实参
来个实例:
def func1(arg1, arg2):
# 定义一个名为 func1 的函数,并定义此函数有两个参数 arg1, agr2
mysum = arg1 + arg2
# 函数功能实现 。计算 arg1, agr2 的和,并将结果赋值给 mysum
return mysum
# 函数返回语句。将 mysum 的值返回
mysum = func1(2, 3)
# 函数调用。调用 func1 函数,并将函数的执行结果赋值给 mysum 。实参(2, 3)依次赋值给形参 arg1, arg2
print(mysum)
# 输出 mysum
# 5
来个返回多个对象实例:
def func2(long, wide):
# 本函数的功能是计算并返回周长和面积
myperimeter = (long + wide) * 2
myarea = long * wide
return myperimeter,myarea
# 返回多个对象
# 在接收返回值时,可以用一个变量或与返回个数相同的变量数来接受
myperimeter,myarea = func2(2, 3)
# 用两个变量来接受
print(myperimeter,myarea)
panda = func2(2, 3)
# 用一个变量接收多个返回值,则返回值会自动打包成一个元组赋值给变量
print(panda)
# 10 6
# (10,6)
匿名函数(lambda表达式)
Python中的匿名函数其实就是 lambda 表达式(变量 = lambda 参数 :函数体),当你不想给函数起名字时,就可以定义一个匿名函数。但匿名函数有一个限制,就是只能有一个表达式,所以它只能实现一些简单的功能。它不用写return 语句,表达式的结果就是返回值(返回值是一个函数对象)。虽然只有一个表达式,但匿名函数也可以有多个参数(也可以有默认参数、关键字参数等参数类型)。下面就来看一下示例
func3 = lambda arg1, arg2 : arg1 * arg2
# 定义匿名函数,并将其返回值赋值给func3
print(func3(3, 4))
# 匿名函数的调用方法与普通函数一样
# 12
匿名函数常用的一点是作为高阶函数的参数来使用
# 例如下 求字典的最大值
dic = {'arg1': 19, 'arg2': 12, 'arg3': 20}
ret = max(dic, key = lambda i: dic[i])
print(ret)
# arg3
嵌套函数
顾名思义,嵌套函数即在函数内部定义了一个或多个函数。我们可以把嵌入其内的函数称为内嵌函数,把被嵌套的函数称为外层函数。需要注意的是内嵌函数的作用于只在外层函数的内部,即只有在外层函数内才能调用内嵌函数。
# 例如下
def fun4(arg1):
print("我是外层函数fun4")
def fun4_1(arg2):
print("我是内嵌函数fun4_1,我只能在外层函数fun4内部被调用")
return arg1 + arg2
return fun4_1(2)
# 返回内嵌函数的调用结果
print(fun4(1))
# 输出fun4的调用结果
# fun4_1() #如果在这调用会出现 "NameError" 的异常
# 参数arg1的作用域是整个fun4范围内,所以在fun4_1内可以使用arg1
# 参数arg2的作用域是整个fun4_1范围内,所以并不是整个fun4内都可以使用arg2
返回函数和闭包
在之前变量类型的笔记里我们说过函数名也可以当做变量使用。既然可以当做变量,那么自然也可以当做另一个函数的返回值。我们把返回值是一个函数的函数叫做返回值函数。因为被返回的函数需要在函数内定义,所以返回函数往往也是一种嵌套函数,区别之处就在于函数的返回值不同。
# 例如下
def fun5(arg1):
def fun5_1(arg2):
return arg1 + arg2
return fun5_1
# 把内嵌函数fun5_1返回
mysum = fun5(4)
# 此时的 mysum 就相当于 fun5_1 函数
print(mysum(5))
# 9
# 第一次调用fun5时并没有执行fun5_1函数,而是把fun5_1作为返回值赋值给mysum
# 若想执行fun5_1函数就必须调用 mysum 。事实上这也是提供了一种在外部函数之外调用内嵌函数的方法
闭包:定义一个闭包要满足一下几点:
1. 必须是嵌套函数
2. 内嵌函数必须引用了定义在闭合范围内(外部函数里)的变量
3. 外部函数必须返回内嵌函数——必须返回那个内部函数
事实上我上面的那个例子就是一个典型的闭包,闭包在第一次调用时不会执行内嵌函数,而是把内嵌函数最返回值返回给调用方。在这个过程中,内嵌函数会保存外部函数中所定义的变量,如上例的arg1(其实是保存内嵌函数之前所有的变量)。不过也仅仅是保存起来,并不能直接在内嵌函数内对外部函数的变量进行重新赋值,当需要进行对外部变量的重新赋值时,需要加关键字 nonlocal 进行声明。下面来个例子
def fun5(arg1):
arg1_1 = 10
def fun5_1(arg2):
# arg1_1 += 10 这种直接对其修改,会报错
nonlocal arg1_1
arg1_1 += 10
print(arg1_1)
return arg1 + arg2 + arg1_1
return fun5_1
mysum = fun5(4) # 每一次调用 fun5 都会返回不同的函数
# 此时,fun5_1 函数已经保存了fun5 中相关的变量了
print(mysum(5))
# 20
# 29
需要注意的是返回闭包时,返回函数不能引用任何循环变量,否则就会出错
# 例如下
def fun6():
L = []
for i in range(1,4):
def fun6_1():
return i*i
L.append(fun6_1) #分别把保存了i= 1,2,3时的fun6_1函数插入到列表里
return L
fun6_2, fun6_3, fun6_4, = fun6() # 取出分别保存了i=1,i=2,i=3 的fun6_1函数
print(fun6_2(), fun6_3(),fun6_4())
# 我们想要输出的是: 1 4 9
# 但现实是输出的是: 9 9 9
############################改进方法############################
def fun7():
def fun7_1(j):
def fun7_1_1():
return j*j
return fun7_1_1
L = []
for i in range(1,4):
L.append(fun7_1(i))
return L
fun7_2, fun7_3, fun7_4, = fun7()
print(fun7_2(), fun7_3(),fun7_4())
高阶函数
Python中把一些以函数为实参传入的特殊函数称为高阶函数。它的标准库里提供了很多的高阶函数,下面我们来介绍几个常用的高阶函数
map函数
map函数的原型是map(func, *iterables)
,其中第一个参数 func 是一个函数,第二个参数是一个可迭代对
象。map函数的功能是把函数 func 依次作用到迭代对象 iterables 的每一个元素上,然后返回新的可迭代对象(事
实上是一个 map类型的可迭代对象)。简单来说,如果你想对序列的每一个元素都进行相同的操作,那么就可以
用到 map 函数(听起来用循环也可以实现相同的效果是吧)。
# 例如下
def fun8(arg):
return arg*arg
L = [1, 2, 3, 4, 5]
L2 = map(fun8, L)
L3 = map(lambda n : n * n, L)
# 第一个参数也可以是lambda表达式
print(list(L2))
print(list(L3))
# [1, 4, 9, 16, 25]
# [1, 4, 9, 16, 25]
reduce函数
reduce函数的原型是reduce(function, sequence[, initial])
,其中第一个参数是一个函数,第二个参数是一个
可迭代对象。需要注意的是reduce函数对其第一个参数有一定要求,即function必须是一个有两个参数且有返回值
的函数。reduce函数的功能是对一个可迭代对象按照 function 函数的规则计算,合并成一个结果并返回。
# 例如下
from functools import reduce
# 要用这个函数必须导入相应的模块
def fun9(arg1, arg2):
return arg1 + arg2
L = [1, 2, 3, 4, 5]
mysum = reduce(fun9, L)
print(mysum)
# 15
filter函数
filter函数的原型是filter(function or None, iterable)
,其中第一个参数是一个函数或者是 None,第二个参
数是一个可迭代对象。filter函数的功能是对一个可迭代对象按照 function 函数定义的规则进行过滤,将符合条件
的元素生成一个新的可迭代对象并返回,(返回值是一个filter类型的可迭代对象)。如果第一个参数是函数,则要求该
函数的返回值必须是布尔值。如果是None,则返回原序列。
# 例如下
def fun10(arg1):
return (arg1 % 2) != 0
# 把偶数过滤掉
L = [1, 2, 3, 4, 5]
newL1 = filter(fun10, L)
newL2 = filter(None, L)
print(list(newL1))
print(list(newL2))
# [1, 3, 5]xcd
# [1, 2, 3, 4, 5]
sorted函数
sorted函数的原型是sorted(__iterable, key, reverse)
。它的第一个参数表示一个可迭代对象,第二个参数可
以是一个函数也可以是 None(默认值),第三个参数是一个布尔值(默认为False)。sorted的功能是对一个序列以key
函数为逻辑 (规则) 进行升序或降序排序(可以这么理解:先是把key函数作用于序列L1的每一个元素,这样就产生了
一个新的序列L2,可以把这两个序列看成一个以L1元素为键,对应L2元素为键值的一个字典。然后对字典以键值为
关键字进行排序,最后返回字典的值组成的序列)当参数reverse为默认值False时,进行升序排;为True时进行降
序排。
# 例如下
L = [-4, 1, -2, 5, 3, -6]
newL1 = sorted(L, key = abs)
# 对列表L的元素按绝对值进行升序排序
newL2 = sorted(L, key = abs, reverse = True)
# 对列表L的元素按绝对值进行降序排序
print(newL1)
print(newL2)
# [1, -2, 3, -4, 5, -6]
# [-6, 5, -4, 3, -2, 1]
偏函数
我们都知道int()函数的用法,它的功能是把表示某种进制数的字符串转化为十进制数,但默认字符串表示的是十进制数。也就是说我要把表示二进制的字符串转化为十进制数就要这样写a = int("101", 2)
。那如果我的工程里面要多次把表示二进制的字符串转化为十进制数,而我又很懒,不想一直传进两个参数,那我该怎么办?我可以用偏函数来搞定。
偏函数是一种某些参数值被固定了的函数,相当于一个由特定参数的函数。定义偏函数需要用到高阶函数 partial。partial 函数的作用就是固定其他函数的某些参数值,并返回一个新的函数。
# 例如下
from functools import partial
# 导入高阶函数 partial
int2 = partial(int, base = 2)
# 利用partial定义偏函数,此时的int2就是一个偏函数
print(int("101",2))
print(int2("101"))
# 可以发现,定义了偏函数之后,我就可以少传进一个参数了
# 5
# 5
函数的装饰器
装饰器分为类装饰器和函数装饰器等,装饰器的作用是在不改动源代码的基础上进行功能扩展。
函数的装饰器本质上就是一种返回函数的高阶函数,它可以在不改动原函数代码的情况下无限制的扩展函数的功能,它的定义需要用到返回函数和闭包结构。
要创建装饰器需要知道以下几点:
1. 装饰器是一个函数。
2. 装饰器取被装饰函数为参数。 即,需要把被修饰函数作为参数传入装饰器
3. 装饰器返回一个新函数。这个新函数的功能包括被装饰函数的功能和装饰器的功能
4.装饰器维护被装饰函数的签名。(函数参数的个数和类型称为其签名)即装饰器要确保它返回的函数与被装
饰器函数有同样的参数(指个数和类型都相同)
函数的装饰器分为不带参和带参两种,下面将分别介绍。
不带参装饰器
def fun11(func):
def fun11_1(*args, **kwargs):
# 为什么写*args和**kwargs这两个参数?因为要满足上面第4点
print("这是装饰器,在执行被装饰的函数前都要先执行我")
return func(*args, **kwargs)
return fun11_1
# 上面的结构实际上就是一个闭包结构
@fun11 # 装饰器,即 @ + 装饰器的名字
def fun12(*args, **kwargs):
print("这是功能被扩展的函数")
# 紧接着装饰器定义新的函数,这个函数就是功能被扩展的函数,它将拥有装饰器的所有功能
fun12()
# 输出如下:
# 这是装饰器,在执行被装饰的函数前都要先执行我
# 这是功能被扩展的函数
#########################另外一种装饰方法#########################
'''
def fun11(func):
def fun11_1(*args, **kwargs):
print("这是装饰器,在执行被装饰的函数前都要先执行我")
return func(*args, **kwargs)
return fun11_1
def fun12(*args, **kwargs):
print("这是功能被扩展的函数")
fun13 = fun11(fun12)
fun13()
# fun13也将同时拥有fun11和fun12的所有功能
'''
带参的装饰器
def fun14(name, hobby):
# 带参数的装饰器需要多写一层函数,不可以直接在fun14_1里传进其他参数
def fun14_1(func):
def fun14_1_1(*args, **kwargs):
print("这是{0}的装饰器{1}".format(name, hobby))
return func(*args, **kwargs)
return fun14_1_1
# 返回内嵌函数
return fun14_1
# 返回内嵌函数
@fun14("阿珍", "阿强")
def fun15(*args, **kwargs):
print("这是阿珍")
fun15()
# 问:装饰器是怎么工作的(以上代码为例)?
# 答:1、执行 fun14(name, hobby),并返回 fun14_1。如果装饰器无参数,此步骤省略
# 2、执行 fun14_1(fun15),并返回 fun14_1_1。
# 3、执行 fun15 = fun14_1_1
为了避免出现不必要的麻烦(书上说什么函数有可能会忘记自己的身份,很复杂,涉及到Python内部的知识),我们写自己的装饰器时一定要导入 functools 模块,并调用wraps函数(这也是一个装饰器)。具体如下
from functools import wraps
def fun11(func):
@wraps(func)
# 调用wraps函数
def fun11_1(*args, **kwargs):
print("这是装饰器,在执行被装饰的函数前都要先执行我")
return func(*args, **kwargs)
return fun11_1
@fun11
def fun12(*args, **kwargs):
print("这是被扩展的功能函数")
fun12()