本系列文章配套代码获取有以下三种途径:
-
可以在以下网站查看,该网站是使用JupyterLite搭建的web端Jupyter环境,因此无需在本地安装运行环境即可使用,首次运行浏览器需要下载一些配置文件(大约20M):
https://returu.github.io/Python_Basic/lab/index.html
-
也可以通过百度网盘获取,需要在本地配置代码运行环境,环境配置可以查看【Python基础】2.搭建Python开发环境:
链接:https://pan.baidu.com/s/1x2Ynh_VibPY2u-HWTaJo8w?pwd=mnsj
提取码:mnsj
-
前往GitHub详情页面,单击 code 按钮,选择Download ZIP选项:
https://github.com/returu/Python_Basic
——————————————————
1.生成器:
生成器是一个Python对象序列生成对象。通过它可以对可能会很长的序列进行迭代,而无须一次性在内存中创建并保存整个序列。生成器通常是迭代器的数据源。
1.1 生成器与迭代器:
在for循环中介绍了迭代器的相关内容,生成器与迭代器的使用方法较为相似,但是两者的内部原理却是完全不同的。
迭代器是将所有的内容都放在内存中,然后依次往下遍历;而生成器则不会把内容放在内存中,每次遍历返回的都是本次计算出来的元素,用完之后立即销毁。
因此,当整个序列占用内存特别大时,使用生成器对象会节约内存;当系统运算资源不足时,使用迭代器对象会节约运算资源。
1.2 生成器函数(generator):
生成器函数也是一种普通函数,但是是通过yield
而非return
语句返回函数值,返回值是一个生成器对象。
1# 定义一个函数实现与内置range()函数一样的功能
2>>> def my_range(start=0 , end=10 , step=1):
3... num = start
4... while num < end:
5... yield num
6... num += step
7
8# 这是一个普通函数
9>>> my_range
10<function __main__.my_range(start=0, end=10, step=1)>
11
12# 函数会返回一个迭代器你对象
13>>> my_range = my_range(1,5)
14>>> my_range
15<generator object my_range at 0x0000015BE8C5ADD0>
16
17# 迭代返回的生成器对象
18>>> for i in my_range:
19... print(i)
20
211
222
233
244
上述代码中,自定义了一个函数,其功能与内置range()函数一样,并返回生成器对象。在调用过程中,使用for循环对生成器对象进行遍历并输出。
在for循环的内部实现里,每次循环生成器都会返回一个元素,等全部循环结束后,生成器也随之结束,不会在内存中有存留。
1# 尝试再次迭代该生成器,可以看到什么都没有输出
2for j in my_range:
3 print(j)
2.递归:
如果一个函数调用其自身,就叫做递归。
1>>> def flatten(lol):
2... for item in lol:
3... if isinstance(item , list):
4... for subitem in flatten(item):
5... yield subitem
6... else:
7... yield item
8
9>>> lol = [1,[2,3],4,[[5]],[[6,[7,8]]],9]
10>>> flatten(lol)
11<generator object flatten at 0x000002952CB970B0>
12>>> list(flatten(lol))
13[1, 2, 3, 4, 5, 6, 7, 8, 9]
Python3.3 加入了yield from
表达式,该表达式可以让一个生成器把部分工作交给另一个生成器,用于简化函数。
1# 使用yield from表达式简化代码
2>>> def flatten_2(lol):
3... for item in lol:
4... if isinstance(item , list):
5... yield from flatten(item)
6... else:
7... yield item
3.装饰器:
有时候需要在不改变源代码的情况下修改已有的函数(例如增加调试语句,查看传入的参数等),此时就需要使用到装饰器。
3.1 装饰器的实现
装饰器是一种函数,接受一个函数作为输入并返回另一个函数。装饰器的主要作用就是在不改变已有代码的基础上,扩展原有功能。
装饰器就是在原有的函数外面再包装一层函数,使新函数在返回原有函数之前实现一些新功能。
1# 定义一个装饰器,用于检查输入参数是否是int类型
2>>> def check_int(func): # 装饰器函数,参数为要被装饰的函数
3... def new_function(*args): # 定义一个检查参数类型的函数
4... print("运行的函数为:" , func.__name__)
5... print("参数为:" , args)
6...
7... if isinstance(args[0] , int) and isinstance(args[1] , int):
8... return func(*args)
9... print("输入参数不为int类型!")
10...
11... return new_function # 将装饰后的函数返回
12
13>>> def add_ints(a , b):
14... return a + b
15
16
17# 使用装饰器——手动应用
18>>> F = check_int(add_ints) # 对add_ints()函数进行装饰
19>>> F(2,3)
20运行的函数为:add_ints
21参数为: (2, 3)
225
23>>> F(2,'w')
24运行的函数为:add_ints
25参数为: (2, 'w')
26输入参数不为int类型!
3.2 @修饰符:
还可以在需要装饰的函数前添加@decoratoe_name来代替手动应用装饰器。
使用@修饰符会使得装饰器与被装饰函数之间的关系更加明显,降低了编码出错的可能性。
使用@修饰符是最常用的写法。
1>>> def check_int(func):
2... def new_function(*args):
3... print("运行的函数为:" , func.__name__)
4... print("参数为:" , args)
5...
6... if isinstance(args[0] , int) and isinstance(args[1] , int):
7... return func(*args)
8... print("输入参数不为int类型!")
9...
10... return new_function
11
12# 使用装饰器——@修饰符
13>>> @check_int
14... def add_ints(a , b):
15... return a + b
16
17>>> add_ints(2 , 3)
18运行的函数为:add_ints
19参数为: (2, 3)
205
21>>> add_ints(2,'w')
22运行的函数为:add_ints
23参数为: (2, 'w')
24输入参数不为int类型!
3.3 多个装饰器联合使用:
一个函数可以有多个装饰器。当有多个装饰器时,最接近函数的装饰器(就在def之上)先运行,然后再运行其上的装饰器。
下面先定义两个装饰器,然后应用。
1# 定义第一个装饰器
2def outter_1(func):
3 print("第一个装饰器")
4 def inner_1(*args):
5 print("执行inner_1")
6 result = func(*args)*2
7 return result
8
9 return inner_1
10
11# 定义第二个装饰器
12def outter_2(func):
13 print("第二个装饰器")
14 def inner_2(*args):
15 print("执行inner_2")
16 result = func(*args)+'-'
17 return result
18
19 return inner_2
20
21# 使用装饰器
22@outter_2
23@outter_1
24def demo(a):
25 print("执行demo函数")
26 return a+'+'
此时系统会输出如下结果:
由此可以看出,装饰器在代码载入时就开始执行,会按照从下到上的顺序加载装饰器函数。
1第一个装饰器
2第二个装饰器
然后执行调用语句:
1demo('XXX')
输出如下结果:
1执行inner_2
2执行inner_1
3执行demo函数
4'XXX+XXX+-'
outter_2(outter_1(demo()))
,然后执行调用语句,首先执行outter_2()
,其参数为outter_1(demo())
,因此在执行outter_1()
,其参数为demo()
,执行demo()
函数,其返回值为XXX+
,然后执行inner_1()
,得到XXX+XXX+
,再执行inner_2()
得到XXX+XXX+-
。下面将装饰器函数位置进行互换:
1# 将装饰器位置互换
2
3@outter_1
4@outter_2
5def demo(a):
6 print("执行demo函数")
7 return a+'+'
8
9demo('XXX')
输出结果如下:
先运行最接近函数的装饰器(将结果加-),然后再运行其上的装饰器(将结果*2)。
1第二个装饰器
2第一个装饰器
3执行inner_1
4执行inner_2
5执行demo函数
6'XXX+-XXX+-'
本篇文章来源于微信公众号: 码农设计师