目录
一、生成式详解
列表生成式就是一个用来生成列表的特定语法形式的表达式。是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]
字典生成式:用来快速生成字典;
集合生成式:用来快速生成集合;
二、生成器与yield详解
• 什么叫生成器 ?在Python中,一边循环一边计算的机制,称为生成器:Generator。
• 什么时候需要使用生成器?性能限制需要用到,比如读取一个10G的文件,如果一次性将10G的文件加载到内存处理的话(read方法),内存肯定会溢出;但使用生成器把读写交叉处理进行,比如使用(readline和readlines)就可以再循环读取的同时不断处理,这样就可以节省大量的内存空间.
• 如何创建生成器 ?第一种方法: 列表生成式的改写。 []改成()
第二种方法: yield关键字。
• 如何打印生成器的每一个元素呢 ?通过for循环, 依次计算并生成每一个元素。
如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值。
生成器的特点是什么?
• 解耦. 爬虫与数据存储解耦;• 减少内存占用. 随时生产, 即时消费, 不用堆积在内存当中;• 可不终止调用. 写上循环, 即可循环接收数据, 对在循环之前定义的变量, 可重复使用;• 生成器的循环, 在 yield 处中断, 没那么占 cpu.
生产者消费者问题是多线程并发中一个非常经典的问题,也是在互联网面试求职中会经常问到的一个题。
顾名思义,单生产者-单消费者模型中只有一个生产者和一个消费者,生产者不停地往队列库中放入产品,消费者则从队列库中取走产品。
生产者-消费者模型有如下几个特点:
1、队列库容积有一定的限制,只能容纳一定数目的产品。
2、如果生产者生产产品的速度过快,则需要等待消费者取走产品之后,产品库不为空才能继续往产品库中放如新的产品。
3、如果消费者取走产品的速度过快,则可能面临产品库中没有产品可使用的情况,此时需要等待生产者放入一个产品后,消费者才能继续工作。
专业术语描述:
1、当队列元素已满的时候,阻塞插入操作;
2、当队列元素为空的时候,阻塞获取操作;
python中return关键字和yield关键字的区别?
• return:在程序函数中返回某个值,返回之后函数不在继续执行,彻底结束。• yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,会在前面停留的位置继续执行,直到程序结束
三、生成器、迭代器与可迭代对象
迭代是访问容器元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
可迭代对象:可以直接作用于for循环的对象(如何判断是否可以迭代?)
一类是集合数据类型,如list, tuple,dict, set,str等;
一类是generator,包括生成器和带yield的generator function。
• 可以被 next() 函数调用并不断返回下一个值的对象称为迭代器 :Iterator 。• 生成器都是 Iterator 对象 , 但 list 、 dict 、 str 虽然是 Iterable , 却不是 Iterator 。• 把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用 iter () 函数
Python面试真题: 迭代器和生成器的区别 ?
四、闭包
• 什么是闭包 ? 如何实现闭包 ?闭包就是指有权访问另一个函数作用域中的变量的函数。
创建闭包最常见方式,就是在一个函数内部创建另一个函数。
常见形式: 内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用。
闭包的一个常用场景就是装饰器。
函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y=x+1和y=4x+5)。
优点: 闭包也具有提高代码可复用性的作用。
五、装饰器
什么是装饰器?
器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能的工具/函数。
为什么使用装饰器?
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
装饰器的实现必须遵循两大原则:
• 封闭 : 对已经实现的功能代码块封闭。 不修改被装饰对象的源代码• 开放 : 对扩展开发装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
如何实现装饰器?
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景是什么?
装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。
Django框架装饰器应用:
http://y.tsutsumi.io/dry-principles-through-python-decorators.html
装饰器应用:权限校验+多装饰器
多装饰器总结:
一般情况下,在函数中可以使用一个装饰器,但是有时也会有两个或两个以上的装饰器。多个装饰器装饰的顺序是自下而上(就近原则),而调用的顺序是自上而下(顺序执行)。
有参装饰器
无参装饰器只套了两层,有参装饰器: 套三层的装饰器,实现一个用户登录的装饰器。
六、内置高阶函数
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!Python对函数式编程提供部分支持。
什么是高阶函数?
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
map()函数
map() 会根据提供的函数对指定序列做映射。
当序列多于一个时,map可以并行(注意是并行)地对每个序列执行如下图所示的过程:
reduce()函数
reduce() 函数会对参数序列中元素进行累积。
filter()函数
filter()函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
sorted()函数
sorted() 函数对所有可迭代的对象进行排序操作。返回重新排序的列表。
sorted(iterable, key=None, reverse=False)
key: 主要是用来进行比较的元素,只有一个参数,
reverse: 排序规则,True 降序 ,False 升序(默认)。
python排序sort()和sorted()的区别是什么?
1. 排序对象不同: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
2. 返回值不同:
list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,
内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
七、实验操作
7.1 上节回顾
"""
1. 字典和集合
1). 集合(无序不重复)
- 创建
s = {} # s是集合么?不是,空字典
s = set() # 如何创建空集合?
- 集合的特性: in, not
- 集合的方法:
增:add, update
删:pop, remove(删除的value不存在会报错), discard(删除的value不存在不报错)
删:pop, remove(if not exists, error), discard(if not exists, do nothing)
查:交集(s1 & s1), 并集(s1|s2), 差集(s1-s2), issubset, issuperset, isdisjoint
2). 字典
- 创建:
d = {"name":"westos", "age":18, "city":"西安"} # key-value对或者键值对
- 特性: in, not in(注意-判断是否为所有key的成员)
print("name" in d) # True
print("westos" in d) # False
- 常用方法
增: d[key]=value, update, d.setdefault(),key存在do nothing, key不存在则增加
删:pop, popitem, clear
改:d[key]=value, d.setdefault()
d[key]=value, key存在则修改, key不存在则增加
查: keys(), values(), items(), d[key], d.get(key), d.get(key, default-value)
d.get(key), 如果key不存在,则返回None.
2. 函数
3. 文件管理
"""
7.2 词频统计练习
"""
技能需求:
1. 文件操作
2. 字符串的分割操作
3. 字典操作
功能需求:词频统计
1. 读取song.txt文件 with open(filename) as f: content=f.read()
2. 分析文件中的每一个单词,统计每个单词出现的次数。{"hello":2, "python":1, "java":1}
- 分析文件中的每一个单词
content = "hello python hello java"
words = content.split()
- 统计每个单词出现的次数- {"hello":2, "python":1, "java":1}
# words = ['hello', 'python', 'hello', 'java']
3. 获取出现次数最多的5个单词
"""
# 1. 加载文件中所有的单词
with open('doc/song.txt') as f:
words = f.read().split()
# 2. 统计
from collections import Counter
counter = Counter(words)
result = counter.most_common(5)
print(result)
# # 2. 统计
# result = {}
# for word in words:
# if word in result:
# # result[word] = result[word] + 1
# result[word] += 1
# else:
# result[word] = 1
#
# # *小拓展: 友好打印信息
# import pprint
# pprint.pprint(result)
#
7.3 生成式
# 需求: 生成100个验证码(4个字母组成的验证码)
import string
import random
codes = []
for count in range(100):
code = "".join(random.sample(string.ascii_letters, 4))
codes.append(code)
print(codes)
# 列表生成式优化版
codes = ["".join(random.sample(string.ascii_letters, 4)) for i in range(100)]
print(codes)
# 需求: 找出1-100之间可以被3整除的数。
nums = []
for num in range(1, 101):
if num % 3 == 0:
nums.append(num)
print(nums)
# 优化版
nums = [num for num in range(1, 101) if num % 3 == 0]
print(nums)
# 集合生成式
result = {i**2 for i in range(10)}
print(result)
# 字典生成式
result = {i:i**2 for i in range(10)}
print(result)
7.4 生成器
# 生成器实现的第一种方法: 将生成式改写成生成器
nums = (i**2 for i in range(10000))
# 生成器实现的第2种方法:yield关键字
# return: 函数遇到return就返回,return后面的代码并不会执行。
# yield:遇到yield则停止执行代码, 当再次调用next方法时,会从上次停止的地方继续执行,遇到yield停止。。。。
def login():
print('step 1') # 'step 1'
yield 1 # output 1
print('step 2')
yield 2
print('step 3')
yield 3
# 如果函数里面有yield关键字,函数的返回值就是一个生成器
g = login()
print(next(g))
print(next(g))
7.5 闭包
# import time
# start_time = time.time() # 时间戳:从1970年1.1到现在经历的秒数
# time.sleep(2)
# end_time = time.time() # 时间戳:从1970年1.1到现在经历的秒数
# print(end_time-start_time)
# 闭包:
# 1. 函数里面嵌套函数
# 2. 外部函数的返回值是内部函数的引用
# 3. 内部函数可以使用外部函数的变量
def timeit(name):
def wrapper():
print('wrapper ' + name)
print('timeit')
return wrapper
in_fun = timeit(name='westos') # wrapper函数, in_fun实质上就是wrapper函数
in_fun()
7.6 装饰器
# import time
# start_time = time.time() # 时间戳:从1970年1.1到现在经历的秒数
# time.sleep(2)
# end_time = time.time() # 时间戳:从1970年1.1到现在经历的秒数
# print(end_time-start_time)
# 闭包:
# 1. 函数里面嵌套函数
# 2. 外部函数的返回值是内部函数的引用
# 3. 内部函数可以使用外部函数的变量
def timeit(name):
def wrapper():
print('wrapper ' + name)
print('timeit')
return wrapper
in_fun = timeit(name='westos') # wrapper函数, in_fun实质上就是wrapper函数
in_fun()
7.7 万能装饰器的实现
"""
装饰器的万能模板:
def 装饰器名称(f):
@wraps(f) # 保留被装饰函数的属性信息和帮助文档
def wrapper(*args, **kwargs):
# 执行函数之前做的事情
result = f(*args, **kwargs)
# 执行函数之后做的事情
return result
return wrapper
"""
# 需求: 计算函数的运行时间
import time
from functools import wraps
def timeit(f):
"""计时器的装饰器"""
@wraps(f) # 保留被装饰函数的属性信息和帮助文档
def wrapper(*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 desc"""
print('login....')
@timeit
def crawl():
import requests
url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2Fv2-8f51c35f5e8e48d3c8616a4353178689_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1652966157&t=2aab95a477f8d65b890487ec22b91505'
content = requests.get(url).content
with open('doc/python.png', 'wb') as f:
f.write(content)
print("下载图片成功")
# print(help(login))
# login()
crawl()
7.8 含参数的装饰器
# 需求: 计算函数的运行时间
import time
from functools import wraps
def timeit(args='seconds'):
def desc(f):
"""计时器的装饰器"""
@wraps(f) # 保留被装饰函数的属性信息和帮助文档
def wrapper(*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 desc"""
print('login....')
@timeit
def crawl():
import requests
url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Python.svg/1200px-Python.svg.png'
content = requests.get(url).content
with open('doc/python.png', 'wb') as f:
f.write(content)
print("下载图片成功")
# print(help(login))
# login()
crawl()
7.9 多装饰器
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()
1). wrapper1()
2). wrapper2()
3). show_hosts()
"""
show_hosts()
7.10 内置高阶函数
# 1. map函数
result = map(lambda x: x ** 2, [1, 2, 4, 5])
print(list(result))
result = map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6])
print(list(result))
# 2. reduce函数
from functools import reduce
# (((1+2)+3)+4)+5=reduce result
result = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])
print(result)
# 练习: 求1*2*..100的结果, 用reduce和匿名函数实现
result = reduce(lambda x,y: x*y, range(1, 11))
print(result)
# 3. filter:
# 筛选所有的偶数
result = filter(lambda x: x % 2 == 0, [1, 2, 4, 5, 8])
print(list(result))
# 筛选所有的奇数
result = filter(lambda x: x % 2 != 0, [1, 2, 4, 5, 8])
print(list(result))
# 4. sorted:
result = sorted([1, 29, 2, 3])
print(result)
result = sorted([0, 29, 2, 0], reverse=True)
print(result)
result = sorted([0, 8, 9, 0, 16], key=lambda x:0 if x==0 else 1)
print(result)