Python基础复习函数篇
目录
1.猴子补丁
2. global和nonlocal关键字
3.迭代器和生成器
4.递归函数
5.高阶函数和lamdba函数
6.闭包
7.装饰器
1. 猴子补丁
猴子补丁主要用于在不修改已有代码情况下修改其功能或增加新功能的支持。
例如: 在使用第三方模块时,模块中的某些方法可能无法满足我们的开发需求。此时,我们可以在不修改这些方法代码的情况下,通过猴子补丁用一些
自己编写的新方法进行替代,从而实现一些新的功能。
如很多代码用到 import json,后来发现ujson性能更高,如果觉得把每个文件的import json 改成 import ujson as json成本较高,或者说想测试一下用ujson替换json是否符合预期,只需要在入口加上:
import json import ujson def monkey_patch_json(): json.__name__ = 'ujson' json.dump = ujson.dumps json.loads = ujson.loads monkey_patch_json()
2. global和nonlocal关键字
在一个函数中使用global关键字,可以声明在该函数中使用的是全局变量
而非局部变量。
在一个函数中要修改全局变量,必须使用global声明。
在python中,函数的定义可以嵌套,即在一个函数的函数体中可以包含另一个函数的定义。
通过nonlocal关键字,可以使内层的函数直接使用外层函数中定义的变量。
例如:
def outer1(): # 不加nonlocal x = 10 # 局部变量 def inner(): x = 20 print(x) print(x) outer1() 输出结果: 20 10 def outer2(): # 加nonlocal x = 10 # 局部变量 def inner(): nonlocal x = 20 print(x) print(x) outer2() 输出结果: 20 20
3.迭代器和生成器
迭代器 Iterator
迭代器是访问可迭代对象的工具。迭代器有两个基本的方法:iter() 和 next()。
迭代器是指用iter(obj) 返回的对象(实例)。迭代器可以用next(it) 获取可迭代对象的数据。
iter(iterable) 从可迭代对象中返回一个迭代器,其中iterable必须是一个能提供一个迭代器的对象,如字符串,列表或元组对象都可用于创建迭代器。
next(iterator) 从迭代器iterator中获取下一个数据,如果无法获取下一行数据,则触发StopIteration异常
说明:
代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
用iter函数可以返回一个可迭代对象的迭代器
L = [2, 3, 5, 7] it = iter(L) # it 绑定的是可迭代对象 L 提供的迭代器 print(next(it)) # 2 # 向迭代器要数据 print(next(it)) # 3 print(next(it)) # 5 print(next(it)) # 7 print(next(it)) # StopIteration异常 myit = iter(range(1, 10, 3)) # range也可以作为一个迭代器对象 1, 4, 7 print(next(myit)) # 1 print(next(myit)) # 4 print(next(myit)) # 7 print(next(myit)) # StopIteration异常 可用try捕获
迭代器对象也可以使用常规for语句进行遍历:
list=[1,2,3,4] it = iter(list) # 创建迭代器对象 for x in it: print (x, end=" ") 输出结果如下: 1 2 3 4
生成器 generator
含有yield语句的函数是生成器函数,此函数被调用将返回一个生成器对象,在生成器函数调用return会触发一个StopIteration异常(即生成结束)
生成器函数调用时返回的生成器是一次性的,当生成结束后将不再提供数据
yield 可翻译为产生或生成
yield 语句
语法:
yield 表达式
说明:
yield 用于
def 函数中,目的是将此函数作为生成器函数使用
yield 用来生成数据,提供迭代器 next(it) 函数使用
下面举例 自己用生成器实现python内建函数 filter
这个函数也可以加深对迭代器的理解:
def myfilter(fn, iterable): it = iter(iterable) # 先拿到迭代器 while True: try: x = next(it) # 取值 if fn(x): yield x except StopIteration: return # 生成结束 for x in myfilter(lambda y: y % 2 == 1, range(10)): print(x) # 1 3 5 7 9
iter(可迭代对象) 用可迭代对象生成迭代器
next(iter(可迭代对象)) 从迭代器中取值
yield x 包含yield的函数即生成器函数
4. 递归函数
递归函数是指在一个函数内部通过调用自己来完成一个问题的求解
当我们在进行问题的分解时,发现分解之后待解决的子问题与原问题有着相同
的特性和解法,只是在问题规模上与原问题相比有所减小,此时,就可以设计递归
函数进行求解
比如,对于计算n!的问题,可以将其分解为: n! = n * (n – 1) .可见,分解后的子问题
(n - 1)! 与原问题的 n! 的计算方法完全一样,只是规模有所减少。
同样,(n - 1)! 这个子问题有可以进一步分解为(n-1) * (n-2)! 可以进一步分解为
(n - 2)(n - 3)! ... , 直到要计算 1! 时,直接返回1 .
def fac(n): if n == 1: # 递归特性一:必须有一个明确的结束条件 return 1 else: return n * fac(n-1) # 递归特性二:每次递归都是为了让问题规模变小,即找出函数的等价关系式 print(fac(5)) # 120 #递归特性三:递归层次过多会导致栈溢出,且效率不高
注意:
当问题规模较大时,递归调用会涉及到很多层的函数调用,一方面会由于栈操作影响程序运行速度,另一方面在Python中有栈的限制,
太多层的函数调用会引起栈溢出问题,如将 fac(5) 改成 fac(1000)则会报错。
在定义递归函数时,要先明确函数的功能,这样才能完成递归特性二的写法。
5.高阶函数和lamdba函数
高阶函数是指把函数作为参数的一种函数
其中注意 函数不仅可以赋给形参 ,也可以赋给变量。赋值后,既可以用变量名替代函数名完成函数调用,
此时变量储存的是函数的内存地址,通过()调用这个地址的内容,即函数。
lambda函数也称为匿名函数, 是一种不使用def定义函数的形式,其作用是能快速定义一个简短的函数
lambda函数的函数体只是一个表达式, 所以lambda函数通常只能实现比较简单的功能。
形式 :
lamdba[参数1[,参数2,...,参数n]] : 表达式
冒号后面的表达式的计算结果即为该lamdba函数的返回值。实例如下:
1 def FunAdd(f,x,y): # 定义函数FunAdd 2 return f(x) + f(y) # 用传给f的函数先对x和y分别处理后,再求和并返回 3 print(FunAdd(lambda x:x**2, 3, -5)) # 计算32 + (-5)2 4 print(FunAdd(lambda x:x**3,3,-5)) # 计算33 + (-5)3
也可以将lamdba函数赋值给一个变量,然后通过该变量去调用响应的lamdba函数。如:
fun = lamdba x:x**2 print(fun(3)) # 输出9
其他高阶函数
filter函数
filter(function or None, iterable) --> filter object
''' Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.'''
>>>list(filter(None,[1,2,0,True,False]))
>>>Out: [1, 2, True]
zip函数
zip(iter1 [,iter2 [...]]) --> zip object
| Return a zip object whose .__next__() method returns a tuple where
| the i-th element comes from the i-th iterable argument. The .__next__()
| method continues until the shortest iterable in the argument sequence
| is exhausted and then it raises StopIteration.
参数iterable为可迭代的对象,并且可以有多个参数。该函数返回一个以元组为元素的列表,其中第 i 个元组包含每个参数序列的第 i 个元素。返回的列表长度被截断为最短的参数序列的长度。只有一个序列参数时,它返回一个1元组的列表。没有参数时,它返回一个空的列表。
a = ['张三','小花','老王'] b = ['男','女','男'] c = ['已婚','已婚','未婚'] z = zip(a,b,c) print(type(z)) # 注意:python3 不再返回列表,而是返回zip对象 print([x for x in z]) 输出: <class 'zip'> [('张三', '男', '已婚'), ('小花', '女', '已婚'), ('老王', '男', '未婚')]
enumerate函数
enumerate(iterable, start=0)
返回一个enumerate对象此对象生成的类型为(索引,值)的元组,默认索引从零开始,也可以用第二个参数start指定
- iterable -- 一个序列、迭代器或其他支持迭代对象。
- start -- 下标起始位置
例:
day = ['None','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'] for i in enumerate(day): print('星期{} :{}'.format(*i)) 输出: 星期0 :None 星期1 :Monday 星期2 :Tuesday 星期3 :Wednesday 星期4 :Thursday 星期5 :Friday 星期6 :Saturday 星期7 :Sunday
6.闭包
如果内层函数使用了外层函数中定义的局部变量,并且外层函数的返回值是内层函数的引用,就构成了闭包
其中定义在外层函数中但由内层函数使用的变量被称为自由变量。
一般情况下,如果一个函数结束,那么该函数中定义的局部变量就都会被释放。
然而闭包是一种特殊情况, 外层函数在结束时会发现其定义的局部变量将来会在内层函数中使用,此时
外层函数就会把这些自由变量绑定到内层函数。
因此所谓闭包, 实际上就是将内层函数的代码以及自由变量(外层函数定义,但会由内层函数使用)打包在一起。
def outer(x): y = 10 # 定义局部变量y并赋值为10 def inner(z): # 在out函数中定义嵌套函数inner nonlocal x,y return x + y + z return inner # 返回嵌套函数inner的引用 f = outer(5) # 将返回的inner函数赋给f g = outer(50) # 将返回的inner函数赋给g print('f(20)的值为:',f(20)) print('g(20)的值为:',g(20)) print('f(30)的值为:',f(30)) print('g(20)的值为:',g(30)) 输出结果: f(20)的值为: 35 g(20)的值为: 80 f(30)的值为: 45 g(20)的值为: 90
提示:
闭包的主要作用在于可以封存函数执行的上下文环境。
比如,上例通过两次调用outer函数形成了两个闭包 f 和 g 。这两个闭包具有相互独立的上下文
环境(一个闭包(f)中x = 5,y = 10,另一个闭包中x = 50,y = 10),且每个闭包可多次调用。
7.装饰器(Decorator)
利用装饰器,可以在不修改已有函数的情况下向已有函数中注入代码,使其具备新的功能。
比如利用装饰器可以将日志处理,执行事件计算,性能测试,事务处理等较为通用且与函数功能本身无关的代码注入到不同的函数中,从而使得代码更加简洁。
一个装饰器可以为多个函数注入代码, 一个函数也可以注入多个装饰器的代码
本质上,装饰器就是一个返回函数的高阶函数。
实例1:
def deco(func): # 定义装饰器函数deco def inner(*args, **kwargs): print('deco begin') func(*args, **kwargs) print('deco end') return inner # 返回函数inner的引用 @deco #语法糖,相当于在f函数执行前执行了f = deco(f) def f(a,b,c): print('a+b+c =',a+b+c) f(1,2,3)
输出结果: deco begin a+b+c = 6 deco end
7.2 带参数和有返回值的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,装饰器函数的参数就是业务函数 f。
装饰器的语法允许我们在调用时,提供其他参数,比如@deco(a) 。
这样就Wie装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务
函数可能需要的日志等级是不一样的。
另外有些业务函数有自己的return值,所以我们在装饰器函数中也要设定返回值,即将业务函数的返回值在装饰函数中再返还给业务函数。示例如下:
1 import logging 2 3 def use_deco(level): # 这个函数将装饰器参数传到内部环境中 4 def deco(func): # 真正的装饰器函数 5 def wrapper(*args, **kwargs): #内部用来实现装饰功能的函数 6 if level == 'warn': 7 logging.warning("%s is running" % func.__name__) # 打印一个警告信息; 每个函数对象都有一个__name__属性,可以拿到函数的名字 8 elif level == "info": 9 logging.info("%s is running" % func.__name__) # 默认日志级别是warning,所以info不会被打印 10 return func(*args) # 返回业务函数的返回 11 return wrapper # 返回函数wrapper的引用 12 return deco # 返回装饰器函数的引用 13 14 @use_deco(level='warn') #语法糖,还是相当于@deco(f = deco(f)),因为use_deco(level='warn') 返回的是deco 15 def f(a,b,c): 16 print('a+b+c =',a+b+c) 17 return a * b * c 18 19 a = f(1,2,3) 20 print('a*b*c =',a) 21 22 运行结果: 23 a+b+c = 6 24 WARNING:root:f is running 25 a*b*c = 6
装饰器顺序
一个函数还可以同时定义多个装饰器,比如:
@a @b @c def f(): pass
他的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于
f = a ( b ( c ( f ) ) )