一、生成式
1.什么是生成式?
答:是指快速生成对象的公式。一般包括(列表生成式,集合生成式,字典生成式)。
- 列表生成式:用来生成列表的特定语法形式的表达式。是Python提供的一种生成列表的简洁形式, 可快速生成一个新的list。
- 集合生成式:用来快速生成集合(set)。
- 字典生成式:用来快速生成字典(dir)。
1.列表生成式:
例如:要生成100个1-50之间的随机值
1)最原始的办法:(定义列表)
import random
nums = [] #定义一个空列表,用来装随机产生的随机数
for count in range(100):
num = random.randint(1,50)
nums.append(num)
print(nums)
print(len(nums)) #可写可不写
#运行结果:
[39, 30, 30, 40, 48, 34, 39, 14, 11, 6, 47, 27, 35, 26, 6, 7, 37, 37, 18, 14, 40, 48, 4, 20, 18, 14, 4, 26, 12, 25, 12, 34, 22, 35, 45, 7, 14, 32, 47, 20, 17, 14, 14, 2, 3, 16, 40, 23, 17, 30, 44, 32, 38, 3, 28, 39, 29, 27, 34, 32, 1, 41, 39, 31, 15, 37, 33, 5, 5, 34, 50, 20, 42, 26, 2, 15, 50, 21, 26, 50, 13, 48, 13, 25, 22, 5, 19, 50, 49, 13, 17, 10, 38, 17, 9, 13, 19, 5, 31, 20]
100
2)用列表生成式:
import random
nums = [random.randint(1,50) for count in range(100)] #列表生成式
print(nums)
print(len(nums)) #可写可不写
#运行结果:
[49, 37, 39, 9, 22, 22, 41, 40, 49, 15, 16, 38, 33, 11, 18, 14, 43, 43, 27, 50, 22, 40, 48, 5, 19, 48, 13, 25, 48, 32, 42, 39, 4, 35, 18, 48, 49, 27, 31, 10, 12, 19, 13, 42, 8, 35, 17, 17, 16, 30, 22, 46, 14, 16, 42, 25, 12, 44, 5, 45, 5, 23, 22, 45, 27, 20, 3, 49, 43, 12, 50, 40, 3, 5, 7, 32, 7, 42, 49, 45, 14, 4, 36, 47, 38, 11, 18, 29, 25, 3, 21, 33, 10, 1, 45, 37, 14, 35, 1, 21]
100
基于以上 两种方法,可以进一步生成其函数模块,可以直接调用函数的方法,如下:
1)循环方法的函数模块(def use_loop())
import random
def use_loop(): #定义use_loop()函数
nums = []
for count in range(100):
num = random.randint(1,50)
nums.append(num)
return nums
print(use_loop()) #调用函数
#运行结果:
[6, 19, 3, 48, 5, 39, 4, 20, 48, 40, 36, 32, 31, 10, 17, 16, 40, 12, 6, 18, 4, 40, 9, 36, 1, 35, 14, 33, 6, 12, 45, 2, 40, 16, 10, 34, 21, 30, 12, 29, 40, 34, 38, 44, 32, 16, 37, 31, 15, 38, 8, 9, 31, 17, 47, 11, 37, 39, 6, 27, 11, 1, 8, 33, 32, 10, 29, 26, 25, 44, 47, 23, 36, 8, 33, 15, 36, 2, 4, 9, 41, 10, 46, 28, 13, 31, 29, 25, 40, 2, 21, 26, 27, 35, 33, 40, 10, 26, 13, 2]
2)列表生成式的函数模块(use_list_expression())
import random
def use_list_expression(): #定义函数模块
return [random.randint(1,50) for count in range (100)]
print(use_list_expression()) #调用函数
#运行结果:
[2, 24, 31, 21, 45, 19, 20, 38, 46, 9, 38, 14, 46, 40, 21, 31, 35, 37, 48, 5, 30, 35, 26, 35, 8, 24, 26, 5, 50, 15, 22, 49, 6, 2, 50, 28, 10, 41, 35, 25, 22, 5, 48, 38, 2, 14, 30, 35, 48, 3, 3, 48, 37, 4, 49, 20, 28, 39, 41, 28, 22, 6, 3, 29, 34, 38, 33, 50, 4, 38, 1, 4, 1, 12, 34, 41, 41, 28, 23, 20, 33, 26, 2, 40, 2, 49, 14, 17, 9, 15, 13, 34, 31, 17, 3, 36, 24, 37, 30, 46]
可见,使用生成式更加简洁,代码量也少了很多,对于以上两个函数模块,还可以进一步优化,可以使生成的数字量更加的灵活,如下:
1)循环方法的函数模块:
import random
def use_loop(count=100,start=0,end=50):
nums = []
for count in range(count):
num = random.randint(start,end)
nums.append(num)
return nums
print(use_loop(count=10)) #调用函数,指定生成个数为10
print(use_loop(count=15)) #指定生成个数为15
#运行结果:
[45, 49, 9, 21, 10, 32, 5, 0, 36, 20]
[13, 11, 39, 34, 11, 21, 39, 10, 45, 20, 9, 4, 15, 25, 48]
2)列表生成式的函数模块:
import random
def use_list_expression(count=100,start=0,end=50):
return [random.randint(1,50) for count in range(count)]
print(use_list_expression(count=8))
print(use_list_expression(count=14))
#运行结果:
[41, 1, 4, 5, 4, 27, 28, 23]
[28, 42, 14, 45, 35, 18, 2, 30, 14, 29, 42, 15, 20, 11]
可见,使用生成式快速又简洁,节省很多代码量。
2)集合生成式:
例如:生成100个1-200之间随机且不重复数值
import random
nums = {random.randint(1,200) for count in range(100)} #其实就是把列表的方括号变成了花括号
print(nums)
#运行结果:
{1, 3, 5, 6, 7, 13, 14, 16, 19, 20, 22, 23, 25, 26, 27, 28, 29, 34, 38, 41, 44, 45, 46, 49, 52, 55, 57, 59, 64, 65, 67, 69, 72, 74, 75, 78, 82, 83, 87, 88, 89, 94, 95, 96, 99, 103, 110, 118, 124, 125, 128, 129, 132, 133, 138, 139, 140, 143, 144, 148, 150, 152, 156, 158, 159, 160, 162, 165, 168, 172, 173, 174, 176, 178, 184, 185, 190, 192, 194, 196, 199}
3)字典生成式:
例如:生成10个用户字典, key是用户名userx(x=1, 2, 3, ....), value是密码passwordx(x=1, 2, 3...)
import pprint #pprint模块只是为了打印结果更加的友好性
users_info = {'user' + str(x+1) : 'password'+ str(x+1) for x in range(10)}
pprint.pprint(users_info)
#运行结果:
{'user1': 'password1',
'user10': 'password10',
'user2': 'password2',
'user3': 'password3',
'user4': 'password4',
'user5': 'password5',
'user6': 'password6',
'user7': 'password7',
'user8': 'password8',
'user9': 'password9'}
2、生成式语法格式
- 普通的语法格式:[exp for iter_var in iterable]
- 带过滤功能语法格式: [exp for iter_var in iterable if_exp]
- 循环嵌套语法格式: [exp for iter_var_A in iterable_A for iter_var_B in iterable_B
例如:这里统一用列表生成式举例说明
1)普通的语法格式:([random.randint(start,end) for count in range (count)])
如:生成任意个数<=100个,1~50之间的随机数值(如20个)
import random
def use_list_expression(count=100,start=0,end=50):
return [random.randint(start,end) for count in range (count)]
print(use_list_expression(count=20))
#运行结果:
[13, 38, 4, 12, 43, 35, 32, 24, 47, 34, 5, 49, 30, 49, 13, 3, 17, 12, 18, 10]
2)带过滤功能的语法格式:([num for num in range(start, end + 1) if num % div_num == 0])
如:找出1-100之间能被3整除的数值
def use_list_expression(start=1, end=100, div_num=3):
"""使用列表生成式实现需求"""
return [num for num in range(start, end + 1) if num % div_num == 0]
print(use_list_expression())
#或者:
print(use_loop(div_num=50)) (1到100之间能被50整除的数)
#运行结果:
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
[50]
3)循环嵌套的语法格式:([item1+ item2 for item1 in 'abc' for item2 in '123'])
如:快速生成['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
nums = [item1+ item2 for item1 in 'abc' for item2 in '123']
print(nums)
#运行结果:
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
3、生成式练习:
import math
def circle_example():
S = lambda r: math.pi * pow(r,2)
C = lambda r : 2 * math.pi * r
return [(S(r), C(r)) for r in range(1, 11)]
print(circle_example())
#运行结果:
[(3.141592653589793, 6.283185307179586), (12.566370614359172, 12.566370614359172), (28.274333882308138, 18.84955592153876), (50.26548245743669, 25.132741228718345), (78.53981633974483, 31.41592653589793), (113.09733552923255, 37.69911184307752), (153.93804002589985, 43.982297150257104), (201.06192982974676, 50.26548245743669), (254.46900494077323, 56.548667764616276), (314.1592653589793, 62.83185307179586)]
2). 找出1~100之间所有的质数。
质数:指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数。
def is_prime(num):
"""判断是否为质数,如果是质数,返回True,如果不是,返回False"""
for i in range(2, num):
if num % i == 0:
return False
else:
return True
def find_prime():
"""找出1~100之间所有的质数"""
return [num for num in range(1, 101) if is_prime(num)]
if __name__ == '__main__':
result = find_prime()
print(result)
#运行结果:
[1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
3). 将字典的key值和value值调换。
def swap_key_value(dictObj):
return {value: key for key, value in dictObj.items()}
if __name__ == '__main__':
d = {
'user1': 'passwd1',
'user2': 'passwd2',
}
result = swap_key_value(d)
print(result)
#运行结果:
{'passwd1': 'user1', 'passwd2': 'user2'}
4). 字典key值大小写计数合并 : 已知字典{'A':10, 'b':5, 'a':2}, 合并后为{'a':12, 'b':5} 注意: key值最终全部为小写.
dict = {'A':10, 'b':5, 'a':2}
d = {key.lower(): (dict.get(key.lower(), 0) + dict.get(key.upper(), 0)) for key, value in dict.items()}
print(d)
#运行结果:
{'a': 12, 'b': 5}
二、生成器
1、什么是生成器?
答:在Python中,一边循环一边计算的机制,称为生成器:Generator。
2、生成器应用场景?
3、生成器的特点:
- 解耦. 爬虫与数据存储解耦;
- 减少内存占用. 随时生产, 即时消费, 不用堆积在内存当中;
- 可不终止调用. 写上循环, 即可循环接收数据, 对在循环之前定义的变量,
- 可重复使用; 生成器的循环, 在 yield 处中断, 没那么占 cpu.
4、如何创建生成器?
- 列表生成式的改写。将 [ ]改成()。
- 使用 yield关键字。
举例如下:
# 列表生成式
nums = [num for num in range(1, 10001) if num % 8 == 0]
print(nums)
print(type(nums)) #<class 'list'>
# 生成器创建方法一(将列表生成式的[]改成()就可以了)
nums_gen = (num for num in range(1, 10001) if num % 8 == 0)
print(nums_gen) # <generator object <genexpr> at 0x000002375E0987C8>
print(type(nums_gen)) # <class 'generator'>
#运行结果:
[8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248, 256, 264, 272, 280, 288, 296, 304, 312, 320, 328, 336, 344, 352, 360, 368, 376, 384, 392, 400, 408, 416, 424, 432, 440, 448, 456, 464, 472, 480, 488, 496, 504, 512, 520, 528, 536, 544, 552, 560, 568, 576, 584, 592, 600, 608, 616, 624, 632, 640, 648, 656, 664, 672, 680, 688, 696, 704, 712, 720, 728, 736, 744, 752, 760, 768, 776, 784, 792, 800, 808, 816, 824, 832, 840, 848, 856, 864, 872, 880, 888, 896, 904, 912, 920, 928, 936, 944, 952, 960, 968, 976, 984, 992, 1000, 1008, 1016, 1024, 1032, 1040, 1048, 1056, 1064, 1072, 1080, 1088, 1096, 1104, 1112, 1120, 1128, 1136, 1144, 1152, 1160, 1168, 1176, 1184, 1192, 1200, 1208, 1216, 1224, 1232, 1240, 1248, 1256, 1264, 1272, 1280, 1288, 1296, 1304, 1312, 1320, 1328, 1336, 1344, 1352, 1360, 1368, 1376, 1384, 1392, 1400, 1408, 1416, 1424, 1432, 1440, 1448, 1456, 1464, 1472, 1480, 1488, 1496, 1504, 1512, 1520, 1528, 1536, 1544, 1552, 1560, 1568, 1576, 1584, 1592, 1600, 1608, 1616, 1624, 1632, 1640, 1648, 1656, 1664, 1672, 1680, 1688, 1696, 1704, 1712, 1720, 1728, 1736, 1744, 1752, 1760, 1768, 1776, 1784, 1792, 1800, 1808, 1816, 1824, 1832, 1840, 1848, 1856, 1864, 1872, 1880, 1888, 1896, 1904, 1912, 1920, 1928, 1936, 1944, 1952, 1960, 1968, 1976, 1984, 1992, 2000, 2008, 2016, 2024, 2032, 2040, 2048, 2056, 2064, 2072, 2080, 2088, 2096, 2104, 2112, 2120, 2128, 2136, 2144, 2152, 2160, 2168, 2176, 2184, 2192, 2200, 2208, 2216, 2224, 2232, 2240, 2248, 2256, 2264, 2272, 2280, 2288, 2296, 2304, 2312, 2320, 2328, 2336, 2344, 2352, 2360, 2368, 2376, 2384, 2392, 2400, 2408, 2416, 2424, 2432, 2440, 2448, 2456, 2464, 2472, 2480, 2488, 2496, 2504, 2512, 2520, 2528, 2536, 2544, 2552, 2560, 2568, 2576, 2584, 2592, 2600, 2608, 2616, 2624, 2632, 2640, 2648, 2656, 2664, 2672, 2680, 2688, 2696, 2704, 2712, 2720, 2728, 2736, 2744, 2752, 2760, 2768, 2776, 2784, 2792, 2800, 2808, 2816, 2824, 2832, 2840, 2848, 2856, 2864, 2872, 2880, 2888, 2896, 2904, 2912, 2920, 2928, 2936, 2944, 2952, 2960, 2968, 2976, 2984, 2992, 3000, 3008, 3016, 3024, 3032, 3040, 3048, 3056, 3064, 3072, 3080, 3088, 3096, 3104, 3112, 3120, 3128, 3136, 3144, 3152, 3160, 3168, 3176, 3184, 3192, 3200, 3208, 3216, 3224, 3232, 3240, 3248, 3256, 3264, 3272, 3280, 3288, 3296, 3304, 3312, 3320, 3328, 3336, 3344, 3352, 3360, 3368, 3376, 3384, 3392, 3400, 3408, 3416, 3424, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488, 3496, 3504, 3512, 3520, 3528, 3536, 3544, 3552, 3560, 3568, 3576, 3584, 3592, 3600, 3608, 3616, 3624, 3632, 3640, 3648, 3656, 3664, 3672, 3680, 3688, 3696, 3704, 3712, 3720, 3728, 3736, 3744, 3752, 3760, 3768, 3776, 3784, 3792, 3800, 3808, 3816, 3824, 3832, 3840, 3848, 3856, 3864, 3872, 3880, 3888, 3896, 3904, 3912, 3920, 3928, 3936, 3944, 3952, 3960, 3968, 3976, 3984, 3992, 4000, 4008, 4016, 4024, 4032, 4040, 4048, 4056, 4064, 4072, 4080, 4088, 4096, 4104, 4112, 4120, 4128, 4136, 4144, 4152, 4160, 4168, 4176, 4184, 4192, 4200, 4208, 4216, 4224, 4232, 4240, 4248, 4256, 4264, 4272, 4280, 4288, 4296, 4304, 4312, 4320, 4328, 4336, 4344, 4352, 4360, 4368, 4376, 4384, 4392, 4400, 4408, 4416, 4424, 4432, 4440, 4448, 4456, 4464, 4472, 4480, 4488, 4496, 4504, 4512, 4520, 4528, 4536, 4544, 4552, 4560, 4568, 4576, 4584, 4592, 4600, 4608, 4616, 4624, 4632, 4640, 4648, 4656, 4664, 4672, 4680, 4688, 4696, 4704, 4712, 4720, 4728, 4736, 4744, 4752, 4760, 4768, 4776, 4784, 4792, 4800, 4808, 4816, 4824, 4832, 4840, 4848, 4856, 4864, 4872, 4880, 4888, 4896, 4904, 4912, 4920, 4928, 4936, 4944, 4952, 4960, 4968, 4976, 4984, 4992, 5000, 5008, 5016, 5024, 5032, 5040, 5048, 5056, 5064, 5072, 5080, 5088, 5096, 5104, 5112, 5120, 5128, 5136, 5144, 5152, 5160, 5168, 5176, 5184, 5192, 5200, 5208, 5216, 5224, 5232, 5240, 5248, 5256, 5264, 5272, 5280, 5288, 5296, 5304, 5312, 5320, 5328, 5336, 5344, 5352, 5360, 5368, 5376, 5384, 5392, 5400, 5408, 5416, 5424, 5432, 5440, 5448, 5456, 5464, 5472, 5480, 5488, 5496, 5504, 5512, 5520, 5528, 5536, 5544, 5552, 5560, 5568, 5576, 5584, 5592, 5600, 5608, 5616, 5624, 5632, 5640, 5648, 5656, 5664, 5672, 5680, 5688, 5696, 5704, 5712, 5720, 5728, 5736, 5744, 5752, 5760, 5768, 5776, 5784, 5792, 5800, 5808, 5816, 5824, 5832, 5840, 5848, 5856, 5864, 5872, 5880, 5888, 5896, 5904, 5912, 5920, 5928, 5936, 5944, 5952, 5960, 5968, 5976, 5984, 5992, 6000, 6008, 6016, 6024, 6032, 6040, 6048, 6056, 6064, 6072, 6080, 6088, 6096, 6104, 6112, 6120, 6128, 6136, 6144, 6152, 6160, 6168, 6176, 6184, 6192, 6200, 6208, 6216, 6224, 6232, 6240, 6248, 6256, 6264, 6272, 6280, 6288, 6296, 6304, 6312, 6320, 6328, 6336, 6344, 6352, 6360, 6368, 6376, 6384, 6392, 6400, 6408, 6416, 6424, 6432, 6440, 6448, 6456, 6464, 6472, 6480, 6488, 6496, 6504, 6512, 6520, 6528, 6536, 6544, 6552, 6560, 6568, 6576, 6584, 6592, 6600, 6608, 6616, 6624, 6632, 6640, 6648, 6656, 6664, 6672, 6680, 6688, 6696, 6704, 6712, 6720, 6728, 6736, 6744, 6752, 6760, 6768, 6776, 6784, 6792, 6800, 6808, 6816, 6824, 6832, 6840, 6848, 6856, 6864, 6872, 6880, 6888, 6896, 6904, 6912, 6920, 6928, 6936, 6944, 6952, 6960, 6968, 6976, 6984, 6992, 7000, 7008, 7016, 7024, 7032, 7040, 7048, 7056, 7064, 7072, 7080, 7088, 7096, 7104, 7112, 7120, 7128, 7136, 7144, 7152, 7160, 7168, 7176, 7184, 7192, 7200, 7208, 7216, 7224, 7232, 7240, 7248, 7256, 7264, 7272, 7280, 7288, 7296, 7304, 7312, 7320, 7328, 7336, 7344, 7352, 7360, 7368, 7376, 7384, 7392, 7400, 7408, 7416, 7424, 7432, 7440, 7448, 7456, 7464, 7472, 7480, 7488, 7496, 7504, 7512, 7520, 7528, 7536, 7544, 7552, 7560, 7568, 7576, 7584, 7592, 7600, 7608, 7616, 7624, 7632, 7640, 7648, 7656, 7664, 7672, 7680, 7688, 7696, 7704, 7712, 7720, 7728, 7736, 7744, 7752, 7760, 7768, 7776, 7784, 7792, 7800, 7808, 7816, 7824, 7832, 7840, 7848, 7856, 7864, 7872, 7880, 7888, 7896, 7904, 7912, 7920, 7928, 7936, 7944, 7952, 7960, 7968, 7976, 7984, 7992, 8000, 8008, 8016, 8024, 8032, 8040, 8048, 8056, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8120, 8128, 8136, 8144, 8152, 8160, 8168, 8176, 8184, 8192, 8200, 8208, 8216, 8224, 8232, 8240, 8248, 8256, 8264, 8272, 8280, 8288, 8296, 8304, 8312, 8320, 8328, 8336, 8344, 8352, 8360, 8368, 8376, 8384, 8392, 8400, 8408, 8416, 8424, 8432, 8440, 8448, 8456, 8464, 8472, 8480, 8488, 8496, 8504, 8512, 8520, 8528, 8536, 8544, 8552, 8560, 8568, 8576, 8584, 8592, 8600, 8608, 8616, 8624, 8632, 8640, 8648, 8656, 8664, 8672, 8680, 8688, 8696, 8704, 8712, 8720, 8728, 8736, 8744, 8752, 8760, 8768, 8776, 8784, 8792, 8800, 8808, 8816, 8824, 8832, 8840, 8848, 8856, 8864, 8872, 8880, 8888, 8896, 8904, 8912, 8920, 8928, 8936, 8944, 8952, 8960, 8968, 8976, 8984, 8992, 9000, 9008, 9016, 9024, 9032, 9040, 9048, 9056, 9064, 9072, 9080, 9088, 9096, 9104, 9112, 9120, 9128, 9136, 9144, 9152, 9160, 9168, 9176, 9184, 9192, 9200, 9208, 9216, 9224, 9232, 9240, 9248, 9256, 9264, 9272, 9280, 9288, 9296, 9304, 9312, 9320, 9328, 9336, 9344, 9352, 9360, 9368, 9376, 9384, 9392, 9400, 9408, 9416, 9424, 9432, 9440, 9448, 9456, 9464, 9472, 9480, 9488, 9496, 9504, 9512, 9520, 9528, 9536, 9544, 9552, 9560, 9568, 9576, 9584, 9592, 9600, 9608, 9616, 9624, 9632, 9640, 9648, 9656, 9664, 9672, 9680, 9688, 9696, 9704, 9712, 9720, 9728, 9736, 9744, 9752, 9760, 9768, 9776, 9784, 9792, 9800, 9808, 9816, 9824, 9832, 9840, 9848, 9856, 9864, 9872, 9880, 9888, 9896, 9904, 9912, 9920, 9928, 9936, 9944, 9952, 9960, 9968, 9976, 9984, 9992, 10000]
<class 'list'>
<generator object <genexpr> at 0x000002375E0987C8>
<class 'generator'>
从这里就可以很直观的看出生成器的优势了, 减少了大量的内存占用。
2)生成器创建方法2:使用 yield关键字。
#Fib数列的案例理解生成器的创建
def fib1(num):
"""递归实现Fib数列"""
if num in (1, 2):
return 1
else:
return fib1(num - 1) + fib1(num - 2)
print(fib1(15))
# 第二种方法: 函数中包含yield关键字
def fib2(num):
"""不使用递归方式实现Fib数列"""
count = 0
a = b = 1
while True:
if count < num:
count += 1
yield a
a, b = b, a + b
else:
break
# 如果函数中有yield关键字, 那么函数的返回值是生成器对象.
result = fib2(15)
print(result)
#运行结果:
610
<generator object fib2 at 0x000002AE978E88B8>
5、如何访问(打印)生成器的每一个元素呢?
- 通过for循环, 依次计算并生成每一个元素。
- 如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值。
例如: 1)通过for循环:
def fib2(num):
"""不使用递归方式实现Fib数列"""
count = 0
a = b = 1
while True:
if count < num:
count += 1
yield a
a, b = b, a + b
else:
break
# 如果函数中有yield关键字, 那么函数的返回值是生成器对象.
result = fib2(15)
print(result)
# 访问生成器对象元素的方法一: 通过for循环, 依次计算并生成每一个元素。
for num in result:
if num > 50:
break
print(num)
#运行结果:
<generator object fib2 at 0x0000020B96DD88B8>
55
89
144
233
2)通过next()函数获得生成器的下一个返回值:
def fib2(num):
"""不使用递归方式实现Fib数列"""
count = 0
a = b = 1
while True:
if count < num:
count += 1
yield a
a, b = b, a + b
else:
break
# 如果函数中有yield关键字, 那么函数的返回值是生成器对象.
result = fib2(100)
print(result)
# 访问生成器对象元素的方法二: 如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值。
print(next(result)) # 执行一次next生成一个值
print(next(result))
print(next(result)) #三次next,所以只生成前三个值
#运行结果:
<generator object fib2 at 0x0000019C63A888B8>
1
1
2
6、 比较python中return关键字和yield关键字的区别?
return: 遇到return, 在程序函数中返回某个值,函数到此就执行结束。后面的代码不会执行。
如:
def return_example():
print('step 1') #打印 'step 1'
return True
print('step 2') # 不执行
def yield_example():
for count in range(100): # 3 # 7
yield 'step' + str(count + 1) # 4 停止 # 8 停止
print("success") # 6 从上次停止的地方开始执行
if __name__ == '__main__':
result = yield_example() # 1
print(next(result)) # 2 执行函数yield_example
print(next(result)) # 5
#运行结果:
step1
success
step2
7、生成器的send 方法:
- send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。
- 但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错。
例如:
def grep(kw):
"""搜索关键字"""
while True:
response = ''
request = yield response
if kw in request:
print(request)
if __name__ == '__main__':
grep_gen = grep('python')
next(grep_gen)
# send方法可以给生成器传递数据, 并一直执行, 遇到yield停止。
grep_gen.send('I love python')
grep_gen.send('I like Java')
运行结果:
I love python
8、生成器的应用案例: 聊天机器人
1)简易版的(傻瓜式机器人)
def chatRobot():
response = ''
while True:
request = yield response
if '姓名' in request: # 判断关键字是否在用户的输入中
response = '姓名暂时保密' # 如果在,将字符串赋给response,并用response来接收,打印Robot: response
elif '你好' in request:
response = 'hello'
else:
response = '我不知道你在说些什么, 请换种说法'
if __name__ == '__main__':
Robot = chatRobot() # 生成器对象
next(Robot) # 调用next方法
while True:
request = input("Me: ")
if request == '再见':
print("欢迎下次聊天.....")
break
response = Robot.send(request) #将用户输入的request传给生成器,用request来接收
print("Robot: ", response)
运行结果:
Me: hello
Robot: 我不知道你在说些什么, 请换种说法
Me: 你好
Robot: hello
Me: 年龄
Robot: 我不知道你在说些什么, 请换种说法
Me: 姓名
Robot: 姓名暂时保密
Me: 再见
欢迎下次聊天.....
2)升级版聊天机器人
#注意:代码需要联网运行
# requests库是python实现的最简单易用的HTTP库,多用于网络爬虫。
import requests
# json库是python中实现json的序列化与反序列化的模块。
import json
def robot_api(word):
# 青云提供的聊天机器人API地址
url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg=%s' %(word)
try:
# 访问URL, 获取网页响应的内容。
response_text = requests.get(url).text
# 将json字符串转成字典, 并获取字典的‘content’key对应的value值
# eg: {'result': 0, 'content': '有做广告的嫌疑,清静点别打广告行么'}
return json.loads(response_text).get('content', "无响应")
except Exception as e:
# 如果访问失败, 响应的内容为''
return ''
def chatRobot():
response = ''
while True:
# yield response: response就是生成器执行next方法或者send方法的返回值。
# request = yield
request = yield response
if '姓名' in request:
response = '姓名暂时保密'
elif '你好' in request:
response = '你好!Hello'
else:
response = robot_api(request)
if __name__ == '__main__':
# 生成器对象
Robot = chatRobot()
# 调用next方法
next(Robot)
while True:
request = input("Me: >> ")
if request == '再见':
print("欢迎下次聊天.....")
break
response = Robot.send(request)
print("Robot: >> ", response)
运行结果:
Me: >> 你好
Robot: >> 你好!Hello
Me: >> 天气
Robot: >> 先告诉菲菲您在哪个城市,让我帮您查天气吧
Me: >> 西安天气
Robot: >> [12月24日] 西安天气:多云转阴,白天 4℃,夜晚 0℃,东北风,<3级
Me: >> 你真漂亮
Robot: >> {face:74}我就是人称超级可爱美少女菲菲也!
Me: >> 性别
Robot: >> 女
Me: >> 再见
欢迎下次聊天.....
三、生成器、迭代器、可迭代对象总结 (重要)
1)通俗理解:
生成式:快速生成列表,集合,字典的公式。
生成器( generator):一边循环一边计算的机制。
迭代器(iterator): 可以调用next方法访问元素的对象.
可迭代对象( Iterable): 可以实现for循环的对象
生成器都是迭代器,生成器内部实现了迭代器的协议。
生成器都是可迭代对象。
可迭代对象并不都是迭代器, 比如: str,list,tuple,set,dict.
如何将可迭代对象转换成迭代器? 用iter()内置函数

四、闭包
1、定义
闭包就是指有权访问另一个函数作用域中的变量的函数。
2、如何实现闭包:
3.闭包的常见形式:
(1)函数嵌套
(2)内部函数使用了外部函数的临时变量并且外部函数的返回值是内部函数的名称
闭包主要应用场景:装饰器
闭包优点:提高代码可复用性
例如1):内部函数使用了外部函数的临时变量,外部函数的返回值是内部函数的名称
"""
line_conf:是外部函数
line: 是内部函数
line是一个闭包, line调用了line_conf的局部变量a和b
"""
def line_conf(a, b):
"""y = ax + b """
def line(x):
return a * x + b
return line #返回的line 是一个函数
# line1是一个函数名
line1 = line_conf(2, 3) # 调用函数, a=2, b=3
line2 = line_conf(3, 3) # 调用函数, a=3, b=3
line3 = line_conf(4, 3) # 调用函数, a=4, b=3
# x = [1, 3, 5, 7 ,9]
x = list(range(1, 10, 2))
y1 = [line1(item) for item in x]
y2 = [line2(item) for item in x]
y3 = [line3(item) for item in x]
print(y1)
print(y2)
print(y3)
打印结果:
[5, 9, 13, 17, 21]
[6, 12, 18, 24, 30]
[7, 15, 23, 31, 39]
扩展:用来画图:
"""
line_conf:是外部函数
line: 是内部函数
line是否为闭包? Yes, line调用了line_conf的局部变量a和b
"""
import matplotlib.pyplot as plt
def line_conf(a, b): # a=2, b=3
"""y = ax + b """
def line(x):
return a * x + b
return line
# line1是一个函数名
line1 = line_conf(2, 3) # 调用函数, a=2, b=3
line2 = line_conf(3, 3) # 调用函数, a=3, b=3
line3 = line_conf(4, 3) # 调用函数, a=4, b=3
# x = [1, 3, 5, 7 .....99]
x = list(range(1, 100, 2))
y1 = [line1(item) for item in x]
y2 = [line2(item) for item in x]
y3 = [line3(item) for item in x]
#1) plot:折线图
plt.plot(x, y1, label='y=2x+3')
plt.plot(x, y2, label='y=3x+3')
plt.plot(x, y3, label='y=4x+3')
# 2)scatter: 散点图
# plt.scatter(x, y1, label='y=2x+3')
# plt.scatter(x, y2, label='y=3x+3')
# plt.scatter(x, y3, label='y=4x+3')
# 显示绘制的图形
plt.title('line display') # 添加标题
plt.legend() # 添加图例
plt.show()
1)折线图:
2)散点图:
用来画图只用注意导入画图模块即可:import matplotlib.pyplot as plt
五、装饰器
1、定义:
(1)首先:器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰对象添加额外功能的工具/函数。
2、使用装饰器的意义(原因):
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时 候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
3、装饰器的实现必须遵循两大原则:
- 封闭: 对已经实现的功能代码块封闭。 不修改被装饰对象的源代码
- 开放: 对扩展开发 装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
4、如何实现装饰器?
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
*******饰器的通用模板*******(很重要)
from functools import wraps
def function(func): #func===被装饰的函数
@wraps(func) #保留被装饰函数的名字和帮助文档
def wrapper(*args, **kwargs): #形参,可变参数(元组),关键字参数(字典)
"""被装饰函数前需要添加的内容"""
result = func(*args, **kwargs) # 实参,解包
"""被装饰函数后需要添加的内容"""
return result
return wrapper
5、装饰器的应用场景:
#logging模块:专门做日志记录和处理
import logging
from functools import wraps
#日志的基本配置
logging.basicConfig(
level= logging.DEBUG, # 控制台打印的日志级别
filename='message.log', # 日志文件位置
filemode='a', # 写入文件的模式,a是追加模式,默认是追加模式
# 日志的格式
format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
)
#定义装饰器
def logger(func):
"""插入日志的装饰器"""
def wrapper(*args, **kwargs):
logging.debug("函数%s开始执行" %(func.__name__))
result = func(*args, **kwargs)
logging.debug("函数%s执行结束" %(func.__name__))
return result
return wrapper
#使用装饰器
@logger #语法糖 执行: login = logger(login) 将login通过logger函数进行装饰,返回wrapper
def login(username, password):
if username == 'root' and password == '123':
print('login ok')
logging.debug('%s login ok' %(username))
else:
print('login failed')
logging.error('%s login failed' %(username))
if __name__ == '__main__':
login('root','123') # 实质执行的是wrapper函数
运行结果:
LOGIN FAILED
我们查看日志文件messag.log可以看到每次运行的日志情况:如:
(2)性能测试
比如:计算代码运行时间
from functools import wraps
import time
def timeit(func): # 2 func=download_music
"""打印被装饰函数运行总时间的装饰器"""
# @wraps保留被装饰函数的函数名和帮助文档, 否则是wrapper的信息.
@wraps(func)
def wrapper(*args, **kwargs): # 5 args=('Music', ), kwargs={}
start_time = time.time()
result = func(*args, **kwargs) # 6 func('Music')=download_music('Music')
end_time = time.time()
print("%s函数运行总时间为%fs" %(func.__name__, end_time-start_time))
return result # 7
return wrapper # 3
@timeit # 1 @timeit实质上执行的内容: download_music = timeit(download_music) = wrapper
def download_music(name): # 7
time.sleep(0.4)
print('[Download]: ', name)
return True
# 调用download_music函数时实质上调用的是wrapper函数。
download_music('Music') # 4
运行结果:
[Download]: Music
download_music函数运行总时间为0.420078s
(3)事务处理
import pprint
import json
from functools import wraps
import string
def json_result(func):
"""被装饰函数的返回值序列化成json格式字符串"""
# 保留被装饰函数的函数名和帮助文档。
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result) #python to json
return wrapper
@json_result
def get_users():
return {'user' + item: 'passwd' + item for item in string.digits}
result = get_users()
pprint.pprint(result)
运行结果:
('{"user0": "passwd0", "user1": "passwd1", "user2": "passwd2", "user3": '
'"passwd3", "user4": "passwd4", "user5": "passwd5", "user6": "passwd6", '
'"user7": "passwd7", "user8": "passwd8", "user9": "passwd9"}')
(4)缓存
functools.lru_cache的作用主要是用来做缓存,把相对耗时的函数结果进行保存,避免传入相同的 参数重复计算。同时,缓存并不会无限增长,不用的缓存会被释放。
from functools import lru_cache
from functools import wraps
import time
def timeit(func):
"""计算代码运行时间的装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print("%s函数运行的时间是%fs" %(func.__name__,end_time-start_time))
return result
return wrapper
def fib_cache(func):
"""实现缓存的装饰器"""
chaches = {}
@wraps(func)
def wrapper(num):
if num in chaches: #判断是否在缓存字典中
return chaches.get(num) #在,直接返回
else:
result = func(num) #不在,计算斐波那契数列
chaches[num]= result #并将结果存入字典
return result
return wrapper
@fib_cache # 使用了缓存装饰器的fib函数
def fib1(num):
if num in (1,2):
return 1
else:
return fib1(num-1) + fib1(num-2)
def fib2(num): #没有使用缓存装饰器的fib函数
if num in (1,2):
return 1
else:
return fib2(num-1) + fib2(num-2)
@timeit #计算使用了缓存装饰器的函数运行的时间
def use_cache():
result = fib1(30)
print(result)
@timeit #计算没有使用缓存装饰器的函数运行的时间
def no_cache():
result = fib2(30)
print(result)
if __name__ == '__main__':
use_cache()
no_cache()
运行结果:
832040
use_cache函数运行的时间是0.000000s
832040
no_cache函数运行的时间是0.210037s
3、多装饰器
一般情况下,在函数中可以使用一个装饰器,但是有时也会有两个或两个以上的装饰器。 多个装饰器装饰的顺序是自下而上(就近原则),而调用的顺序是自上而下(顺序执行)
from functools import wraps
#定义装饰器
def is_login(func):
@wraps(func)
def wrapper1(*args, **kwargs):
print("判断用户是否登录")
result = func(*args, **kwargs)
return result
return wrapper1
#定义装饰器
def is_permission(func):
@wraps(func)
def wrapper2(*args, **kwargs):
print("判断用户是否有权限")
result = func(*args, **kwargs)
return result
return wrapper2
"""
被装饰的过程:自下而上
(1)@is_permission: buy() = is_permission(buy()) buy()==wrapper2
(2)@is-login: buy() = is_login(buy()) 但是现在的buy()是wrapper2 ,所以 wrapper2 = is_login(wrapper2) buy==wrapper2 = wrapper1
buy == wrapper2 == wrapper1
"""
#使用装饰器
@is_login
@is_permission
def buy():
print("购买商品")
"""
调用的过程: 自上而下
(1)调用函数buy(),目前buy() = wrapper1,所以调用wrapper1,print("判断用户是否登录"),再执行函数func,此时传给is_login的参数func是wrapper2
(2)所以去执行wrapper2,print(判断用户是否有权限"),再执行函数func,此时传给is_permission的参数func是buy()
(3)所以执行buy()
"""
if __name__ == '__main__':
buy()
运行结果:
判断用户是否登录
判断用户是否有权限
购买商品
典型案例:实现 权限校验+多装饰器
from functools import wraps
#系统中的用户信息数据库
db = {
'root':{
'name':'root',
'password':'123',
'is_super': 0 # 0表示没有权限
},
'admin':{
'name':'admin',
'password':'123',
'is_super': 1 # 1表示有权限
}
}
#存储当前登录用户的信息
login_user_info = {}
#定义装饰器
def is_login(func):
"""判断用户是否登录的装饰器"""
@wraps(func)
def wrapper1(*args, **kwargs):
if login_user_info: #判断当前登录的用户信息是否存在
result = func(*args, **kwargs) # 存在,执行函数buy()
return result
else: # 不存在,登录用户
print("未登录,请先登录")
user = input("User:")
password = input("Passwoed:")
if user in db:
if db[user]['password'] == password:
print("登录成功") # 用户在用户数据库中,且密码正确,登录成功
login_user_info['username'] = user # 并将用户添加到用户数据库中
result = func(*args, **kwargs) # 执行被装饰函数
return result
else:
print("密码错误")
else:
print("用户不存在")
return wrapper1
def is_permission(func):
"""判断用户是否有权限的装饰器"""
@wraps(func)
def wrapper2(*args, **kwargs):
print("判断用户是否有权限")
current_user = login_user_info.get('username') # 获取当前登录的用户名
permission = db[current_user]['is_super'] # 获取当前用户的权限信息
if permission == 1: # 如果有权限 执行函数
result = func(*args, **kwargs)
return result
else:
print("用户%s没有权限" %(current_user))
return wrapper2
@is_login
@is_permission
def buy():
print("购买商品....")
if __name__ == '__main__':
buy()
运行结果:
#1)未登录,请先登录
User:浅浅
Password:20191224
登录成功
判断用户是否有权限
用户浅浅没有权限
#2)未登录,请先登录
User:cat
Password:61616
登录成功
判断用户是否有权限
购买商品....
4、有参装饰器
无参装饰器只套了两层,有参装饰器: 套三层的装饰器,实现一个用户登录的装饰器。
例如:
from functools import wraps
def auth(type):
print("认证类型:",type)
def desc(func):
def wrapper(*args, **kwargs):
if type == 'local':
user = input("User:")
password = input("Password:")
if user == 'root' and password == '123':
result = func(*args, **kwargs)
return result
else:
print("用户名/密码错误")
else:
print("不支持远程用户登录")
return wrapper
return desc
@auth('local')
def home():
print("网站主页")
if __name__ == '__main__':
home()
运行结果:
认证类型: local
User:浅浅
Password:1234
网站主页
需要 注意的是:@后面跟的是装饰器函数的名称, 如果不是名称, 先执行,再和@结合。
- 1). @auth(type='local')
- 2). desc = auth(type='local')
- 3). @desc 4). login = desc(login)
- 4). login = wrapper
六、内置高阶函数
1.什么是高阶函数?
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
2、高阶函数主要有哪些?
1)map() 函数(map(function, *iterable))
**map() 函数会根据提供的函数对指定序列做映射。即:
- map的传递函数名可以是内置函数
- map的传递函数名可以是匿名函数
- map的传递函数名可以是非匿名函数
- map的传递的可迭代对象可以是多个
例如:
# 1). map的传递函数名可以是内置函数
map_object = map(int, ['1', '2', '3'])
for item in map_object:
print(item, type(item))
# 2). map的传递函数名可以是匿名函数
iterator_object = (int(item) for item in ['1', '2', '3'])
for item in iterator_object:
print(item, type(item))
map_object = map(lambda x: x ** 2, [1, 2, 3, 4])
print(list(map_object))
def data_process(x):
return x + 4
# 3). map的传递函数名可以是非匿名函数
map_object = map(data_process, [1, 2, 3, 4])
print(list(map_object))
# 4). map的传递的可迭代对象可以是多个
map_object = map(lambda x, y: x ** 2 + y, [1, 2, 3], [1, 2, 3])
print(list(map_object))
运行结果:
1 <class 'int'>
2 <class 'int'>
3 <class 'int'>
1 <class 'int'>
2 <class 'int'>
3 <class 'int'>
[1, 4, 9, 16]
[5, 6, 7, 8]
[2, 6, 12]
2)reduce() 函数(reduce(function, sequence[, initial]) -> value)
reduce() 函数可以对参数序列中元素进行累积。
- 在python2中,reduce是内置高阶函数
- 在python3中,reduce函数需要导入:from functools import reduce
例如:
1)redue()实现累加:
from functools import reduce
nums_add = reduce(lambda x, y: x + y, list(range(1, 101)))
print(nums_add)
运行结果:
5050
2)redue()实现阶乘:
from functools import reduce
N = 5
result = reduce(lambda x, y: x * y, list(range(1, N + 1)))
print(result)
运行结果:
120
3)filter() 函数

例如:
1)找出1--10之间能被3整除的数:(用filter()和列表生成式实现):
# filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
result = filter(lambda x: x % 3 == 0, list(range(1, 11)))
print(list(result))
#和下列列表生成式具有同样的功能
result = [item for item in range(1, 11) if item % 3 == 0]
print(result)
运行结果:
[3, 6, 9]
[3, 6, 9]
2)判断100以内的质数:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
def is_prime(num):
for i in range(2,num):
if num % i == 0:
return False
else:
return True
result = filter(is_prime, list(range(2, 101)))
print(list(result))
运行结果:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
4、sorted() 函数(sorted(iterable, key=None, reverse=False))
例如:1)列表有sort()方法:
import random
def is_odd(x):
"""判断是否为偶数"""
return x % 2 == 0
# 排序需求: 偶数排前面, 奇数排后面
nums = list(range(20)) #20以内的整数
random.shuffle(nums)
print("排序前: ", nums)
nums.sort(key=lambda x: 0 if is_odd(x) else 1)
print("排序后: ", nums)
运行结果:
排序前: [15, 12, 19, 7, 18, 2, 16, 10, 1, 3, 13, 0, 6, 14, 17, 4, 9, 5, 11, 8]
排序后: [12, 18, 2, 16, 10, 0, 6, 14, 4, 8, 15, 19, 7, 1, 3, 13, 17, 9, 5, 11]
2)sorted 可以对所有可迭代的对象进行排序操作(以集合为例)
result = sorted({2, 3, 4, 5, 1})
print(list(result))
运行结果:
[1, 2, 3, 4, 5]
七:内置高阶函数练习题:
import math
def abs(x):
if x >= 0:
return x
else:
return x * -1
result = map(abs, [1, -2, 3, -5])
print(list(result))
测试结果:
[1, 2, 3, 5]
2. 接收用户输入的数字 ; 依次将接收的数转换为整形
思路:python 的 input() 方法可以获取用户输入的一行内容,用 float() 来把这行内容转化成数字, 再用 int() 把小数部分丢弃掉
代码:
import sys
print('请输入数字:')
input_string = input()
try:
number = float(input_string)
except ValueError:
print('输入的不是数字!')
sys.exit(0)
item = int(number)
print('转化后的数字为: %s' % item)
测试结果:
请输入数字:
3.3
转化后的数字为: 3
请输入数字:
abc
输入的不是数字!
3. 拿出1~100之间所有的素数(素数又叫质数(prime number)质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。)
思路:根据素数的定义,再用filter()函数过滤:
代码:
def is_prime(num):
if num == 1:
return True
for i in range(2,num):
if num % i == 0:
return False
else:
return True
result = filter(is_prime, list(range(1,101)))
print(list(result))
测试结果:
[1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
总结: 熟练掌使用生成器和高阶函数,可以节省大量 代码空间。