@TOC
1.生成式详解
- 列表生成式就是一个用来生成列表的特定语法形式的表达式。是Python提供的一种生成列表的简洁形式, 可快速生成一个新的list。
- 普通的语法格式:[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.1 列表生成式
#1-1) 普通:需求:生成100个验证码(4个字母组成的验证码)
import string,random
codes = []
for count in range(100):
code = "".join(random.sample(string.ascii_letters,4))
codes.append(code)
print(codes)
#1-2).列表生成式
codes = ["".join(random.sample(string.ascii_letters,4)) for i in range(100)]
print(codes)
#2.需求:找出1-100之间可以被3整除的数
#2-1)普通
nums = []
for num in range(101):
if num%3==0:
nums.append(num)
print(nums)
#2-2)优化版
nums = [num for num in range(101) if num%3==0]
print(nums)
1.2 集合和字典生成式
#3. 集合生成式
s = {1,2,3,4}
print(sorted({i**2 for i in s})) #[1, 4, 9, 16]
#4.字典生成式
result = {i:i**2 for i in range(10)}#{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
print(result)
2.生成器
- 什么叫生成器?
在Python中,一边循环一边计算的机制,称为生成器:Generator。
什么时候需要使用生成器?
性能限制需要用到,比如读取一个10G的文件,如果一次性将10G的文件加载到内存处理的话(read方法),内存肯定
会溢出;但使用生成器把读写交叉处理进行,比如使用(readline和readlines)就可以再循环读取的同时不断处理,
这样就可以节省大量的内存空间.
- 如何创建生成器?
第一种方法: 列表生成式的改写。 []改成()
第二种方法: yield关键字。
- 如何打印生成器的每一个元素呢?
通过for循环, 依次计算并生成每一个元素。
如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值。
- 生成器的特点是什么?
解耦. 爬虫与数据存储解耦;
减少内存占用. 随时生产, 即时消费, 不用堆积在内存当中;
可不终止调用. 写上循环, 即可循环接收数据, 对在循环之前定义的变量, 可重复使用;
生成器的循环, 在 yield 处中断, 没那么占 cpu.
#1.生成式:缺点:
# - 需要等待长时间
# - 会将值返回
num = [i**2 for i in range(1000)]
print(num)
#2.生成器实现的第一种方法:将生成式改写成生成器,使用()
nums = (i**2 for i in range(1000))
print(nums) #<generator object <genexpr> at 0x016CCFB0>
for num in nums:
print(num)
#3.生成器实现的第二种方法:yield关键字,使用next进行依次访问
# return: 函数遇到return就返回,return后面的代码并不会执行
# yield: 遇到yield,则停止执行代码,当再次调用next方法时,会从上次停止的地方继续执行
# (带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,
# 会在前面停留的位置继续执行,直到程序结束)
def login():
a =1
return 'login'
print(a) #不会打印a的值
result = login()
print(result)
def login():
print('step 1')
yield 1 ##返回数字1,输出1,
print('step 2')
yield 2
print('step 3')
yield 3
#如果函数里面有yield关键字,说明函数的返回值就是一个生成器
g = login()
print(next(g)) #step 1 , 1
print(next(g)) #step 2 , 2
3. 生成器、迭代器与可迭代对象
- 迭代是访问容器元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
3.1 Python面试真题: 迭代器和生成器的区别 ?
4. 闭包
- 什么是闭包?如何实现闭包?
闭包就是指有权访问另一个函数作用域中的变量的函数。
- 创建闭包最常见方式,就是在一个函数内部创建另一个函数。
- 常见形式: 内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用。
- 闭包的一个常用场景就是装饰器。
- 优点: 闭包也具有提高代码可复用性的作用
#1. 时间戳的拓展
import time
print(time.time()) #1612668195.066301从1970年到现在经历的秒数
start_time = time.time()
time.sleep(2)
end_time = time.time()
print(end_time-start_time) #2.00152587890625
# #2.闭包
# #2-1)函数里面嵌套函数
def timieit():
def wrpper(): ##遇到定义函数并不会执行函数
print('wrpper')
print('timieit')
timieit() #timieit
#2-2)外部函数的返回值是内部函数的引用
def timieit():
def wrpper(): ##遇到定义函数并不会执行函数
print('wrpper')
print('timieit')
return wrpper
in_fun = timieit()
in_fun() #timieit wrpper
#2-3)内部函数可以使用外部函数的变量
def timieit(name):
def wrpper(): ##遇到定义函数并不会执行函数
print('wrpper'+name)
print('timieit')
return wrpper
in_fun = timieit(name='westos')
in_fun() #timeit wrpperwestos
#小示例
def lin_conf(a:int,b:int):
def line(x):
print(a*x+b)
return a*x+b
return line
line1 = lin_conf(1,1)
line2 = lin_conf(2,1)
line3 = lin_conf(3,1)
print(line1(3)) # 4,4
print(line2(3)) # 7,7
5. 装饰器
5.1 装饰器的介绍及简单示例
- 什么是装饰器?
装饰器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能的工具/函数。
- 为什么使用装饰器?
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以
使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,
对扩展功能指的是开放的。
- 装饰器的实现必须遵循两大原则:
封闭: 对已经实现的功能代码块封闭。 不修改被装饰对象的源代码
开放: 对扩展开发
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
- 如何实现装饰器?
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外
的功能,装饰器的返回值也是一个函数对象。
#1.装饰器:用来装饰函数的工具
#2.功能:在不改变源代码的情况下,添加额外功能(eg:计算机运行时间,记录日志,权限判断)的工具
#3.如何实现装饰器
import time
def timeit(f):
def wrapper(x,y):
start_time = time.time()
result = f(x,y) #f实质上的add函数
end_time = time.time()
print("函数的运行时间为:%.4f" %(end_time-start_time))
return result
return wrapper
@timeit #语法糖 用法: add=timeit(add),装饰了add函数
def add(x,y):
return x+y
result = add(1,3)
print(result)
5.2 万能装饰器的实现
"""
装饰器的万能模板:
def 装饰器名称(f):
@wraps(f) # 保留被装饰函数的属性信息和帮助文档
def wrapper(*args, **kwargs):
# 执行函数之前做的事情
result = f(*args, **kwargs)
# 执行函数之后做的事情
return result
return wrapper
"""
#需求:计算函数的运行时间
import time
from functools import wraps
from functools import lru_cache ##系统内置的装饰器,处理缓存的
def timeit(f): ##写一个形参,来表示装饰的函数
"""计时器装饰器"""
@wraps(f) ##保留被装饰函数的属性信息和帮助文档
def wrapper(*args,**kwargs): ##args 元组 kwargs 字典
"""wrapper内部函数"""
start = time.time()
result = f(*args,**kwargs)
end = time.time()
print(f'函数{f.__name__}运行时间为{end-start}秒')
return result
return wrapper ##函数名加(),是执行函数
@timeit
def login():
"""login doc"""
print('login...')
login()
##爬虫测试
import requests
@timeit
def crawl():
url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F00%2F23%2F85%2F4756cefdce8d33a.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1615269462&t=7a417a7f6b59091fd2a41a1ef973d5e1'
content = requests.get(url=url).content
with open('doc/python.jpg','wb') as f:
f.write(content)
crawl()
5.3 有参数的装饰器
#需求:计算函数的运行时间
import time
from functools import wraps
from functools import lru_cache ##系统内置的装饰器,处理缓存的
def timeit(args='seconds'):
def desc(f): ##写一个形参,来表示装饰的函数
"""计时器装饰器"""
@wraps(f) ##保留被装饰函数的属性信息和帮助文档
def wrapper(*args,**kwargs): ##args 元组 kwargs 字典
"""wrapper内部函数"""
start = time.time()
result = f(*args,**kwargs)
end = time.time()
if args == 'seconds':
print(f'函数{f.__name__}运行时间为{end-start}秒')
elif args == 'minutes':
print(f'函数{f.__name__}运行时间为{(end - start)/60}秒')
return result
return wrapper ##函数名加(),是执行函数
return desc
@timeit(args='minutes') #@timeit() @desc ===>login=desc(login)
def login():
"""login doc"""
print('login...')
login()
5.4 多装饰器
from functools import wraps
def is_login(f):
@wraps(f)
def wrapper1(*args,**kwargs):
print('is_login,用户是否登录')
result = f(*args,**kwargs)
return result
return wrapper1
def is_permission(f):
@wraps(f)
def wrapper2(*args,**kwargs):
print('is_permission,用户是否有权限')
result = f(*args,**kwargs)
return result
return wrapper2
##规则:执行装饰器内容是从上到下,被装饰的顺序是从下到上
@is_login #show_hosts = is_login(wrapper2) show_hosts = wrapper1
@is_permission #show_hosts = is_permission(show_hosts) show_hosts = wrapper2
def show_hosts():
print("显示所有的云主机")
show_hosts()
"""
-- : show_hosts() 执行顺序如下:
1).wrapper1
2).wrapper2
3).show_hosts
"""
"""
结果
is_login,用户是否登录
is_permission,用户是否有权限
显示所有的云主机
"""
6.内置高阶函数
- 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返
回一个函数!Python对函数式编程提供部分支持。
- map() 会根据提供的函数对指定序列做映射。
- reduce() 函数会对参数序列中元素进行累积。
- filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表
- sorted() 函数对所有可迭代的对象进行排序操作。返回重新排序的列表。
sorted(iterable, key=None, reverse=False)
key: 主要是用来进行比较的元素,只有一个参数,
reverse: 排序规则,True 降序 ,False 升序(默认)。
6.1 map映射
#1-1)map只有一个序列
result = map(lambda x:x**2 , [1,2,4,5])
print(list(result)) #[1, 4, 16, 25]
print(list(result)) #[]再次打印为空,因为已经算完了
#1-2)多个序列
result = map(lambda x,y:x+y , [1,2,3],[1,2,3])
print(list(result)) ##[2, 4, 6]
6.2 reduce元素累积
#2)reduce函数
from functools import reduce
result = reduce(lambda x,y:x+y,[1,2,3,4,5])
print(result) #15
# 练习: 求1*2*..100的结果, 用reduce和匿名函数实现
#方法yi
nums = []
for i in range(1,101):
nums.append(i)
result = reduce(lambda x,y:x*y,nums)
print(result)
#方法二
result = reduce(lambda x,y:x*y,range(1,101))
print(result)
6.3 filter过滤
#3)filter过滤
#筛选所有的偶数
result = filter(lambda x: x%2==0,[1,2,3,5,5674,76,8])
print(list(result)) #[2, 5674, 76, 8]
#筛选所有的奇数
result = filter(lambda x:x%2!=0,[1,2,3,5,5674,76,8])
print(list(result)) #[1, 3, 5]
6.4 sorted
- python排序sort()和sorted()的区别是什么?
- 1. 排序对象不同: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
2. 返回值不同:
list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,
内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
#4)sorted
result = sorted([1,2,5,4,7,6,8,2,1])
print(result) #[1, 1, 2, 2, 4, 5, 6, 7, 8]
result = sorted([1,2,5,4,7,6,8,2,1],reverse=True)
print(result) #[8, 7, 6, 5, 4, 2, 2, 1, 1]
result = sorted([0,2,54,5,89,6,0,4,0,4,0],key=lambda x: 0 if x==0 else 1)
print(result) #[0, 0, 0, 0, 2, 54, 5, 89, 6, 4, 4]