文章目录
一、生成式
1、定义
生成式就是一个用来快速生成特定语法形式的表达式。
列表生成式:用来快速生成列表
字典生成式:用来快速生成字典
集合生成式:用来快速生成集合
2、语法格式
(1)普通的语法格式:[exp for iter_var in iterable]
(2)带过滤功能语法格式: [exp for iter_var in iterable if_exp]
(3)循环嵌套语法格式: [exp for iter_var_A in iterable_A for iter_var_B in iterable_B]
例如:
#(1)普通的语法格式:[exp for iter_var in iterable]
# 需求: 生成10个1~50之间的随机数值。
import random
def list_expression(count=10, start=0, end=50):
return [random.randint(start, end) for count in range(count)]
print(list_expression())
#(2)带过滤功能语法格式: [exp for iter_var in iterable if_exp]
# 需求: 找出1-50之间能被3整除的数值;
def list_expression(start=1, end=50, div_num=3):
return [num for num in range(start, end + 1) if num % div_num == 0]
print(list_expression())
#(3)循环嵌套语法格式: [exp for iter_var_A in iterable_A for iter_var_B in iterable_B]
nums = [item1+ item2 for item1 in 'abc' for item2 in '123']
print(nums)
运行结果:
#集合生成式
# 需求: 生成10个1~20之间的随机且不重复数值。
import random
nums = {random.randint(1, 20) for count in range(10)}
print(nums)
3、生成式练习
# (1)求以r为半径的圆的面积和周长(r的范围从1到10)
import math
def circle_area(r):
return math.pi * pow(r,2)
def circle_perimeter(r):
return 2 * math.pi * r
area = [circle_area(r) for r in range(1, 11)]
perimeter = [circle_perimeter(r) for r in range(1,11)]
print(area)
print(perimeter)
#(2)找出1~100之间所有的质数
def is_prime(num):
"""判断是否为质数"""
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)
#(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)
#字典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)
二、生成器
1、定义和特点
(1)在python中,一边循环一边计算的机制,称为生成器(Generator)
(2)应用场景:
性能限制需要用到,比如读取一个10G的文件,如果一次性将10G的文件加载到内存处理的话 (read方法),内存肯定会溢出。但使用生成器把读写交叉处理进行,比如使用(readline和readlines) 就可以再循环读取的同时不断处理,这样就可以节省大量的内存空间。
(3)特点:
1> 解耦。 爬虫与数据存储解耦;
2> 减少内存占用.。随时生产, 即时消费, 不用堆积在内存当中;
3> 可不终止调用.。写上循环, 即可循环接收数据, 对在循环之前定义的变量, 可重复使用;
4> 生成器的循环, 在 yield 处中断, 没那么占 cpu
2、创建和访问
(1)创建:
方法一:列表生成式的改写。 []改成()
方法二:yield关键字
(2)访问:
方法一:通过for循环, 依次计算并生成每一个元素
方法二:通过 next() 函数一个一个获取
# 生成器创建方法一:
nums_gen = (num for num in range(1, 10001) if num % 8 == 0)
print(nums_gen) # <generator object <genexpr> at 0x7f8f2cb92350>
print(type(nums_gen)) # <class 'generator'>
# 查看一个对象是否可以for循环?
from collections.abc import Iterable
print("生成器是否为可迭代对象?", isinstance(nums_gen, Iterable))
# 访问方法一: 通过for循环, 依次计算并生成每一个元素。
"""
for num in nums_gen:
if num > 50:
break
print(num)
"""
# 访问方法二: 如果要一个一个打印出来,可以通过next()函数获得。
print(next(nums_gen)) # 执行一次next生成一个值
print(next(nums_gen))
print(next(nums_gen))
# 生成器创建方法二: 函数中包含yield关键字
def fib2(num):
"""不使用递归方式实现Fib数列,返回前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)
(3)比较yield和return,理解yield的工作原理
return:
遇到return, 函数就执行结束。 后面的代码不会执行
def return_example():
print('step 1') #打印 'step 1'
return True
print('step 2') # 不执行
yield:
函数中包含yield关键字, 返回的是生成器对象
当第一次调用next(genObj), 才开始执行函数内容。
遇到yield关键字, 执行停止。
再次调用next方法时, 从上次停止的代码位置继续执行。
遇到yield关键字, 执行停止。
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
运行结果:
3、生成器的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') #将字符串传递给生成器,并用request来接收
grep_gen.send('I love Java')
案例:聊天机器人
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)
聊天机器人进阶版:使用青云客智能聊天机器人API
#代码需要联网运行
# requests库是python实现的最简单易用的HTTP库,多用于网络爬虫。
#安装 pip install requests
import requests
# json库是进行数据交换
import json
def robot_api(word):
# 青云客提供的聊天机器人API地址
url = 'http://api.qingyunke.com/api.php?key=free&appid=0&msg=%s' %(word)
try:
# 访问URL,通过get()方法,返回一个响应对象(网址的内容),存起来,再通过.text返回文本信息
#在交互式环境可以演示,返回的文本信息是一个字符串
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:
request = yield response
if '姓名' in request:
response = '姓名暂时保密'
elif '你好' in request:
response = '你好!Hello'
else:
response = robot_api(request)
if __name__ == '__main__':
Robot = chatRobot() # 生成器对象
next(Robot) # 调用next方法
while True:
request = input("Me: ")
if request == '再见':
print("欢迎下次聊天.....")
break
response = Robot.send(request)
print("Robot: ", response)
4、生成器应用案例:
(1)生产者-消费者模型
(2)发布-订阅模型
三、生成器、迭代器、可迭代对象总结
生成式:快速生成列表,集合,字典
生成器(generator):一边循环一边计算
迭代器(iterator):可以调用next()方法访问元素
可迭代对象:可以通过for循环访问
生成器都是迭代器,都是可迭代对象
可迭代对象不一定是迭代器,例如:str,list,tuple,set,dict
怎么将可迭代对象转换成迭代器:通过iter()关键字
四、闭包
1、定义
闭包就是指有权访问另一个函数作用域中的变量的函数。
常见形式:
(1)函数嵌套
(2)内部函数使用外部函数的变量
(3)外部函数的返回值是内部函数的名称
应用场景:装饰器
优点:提高代码可复用性
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)
五、装饰器
1、定义
(1)器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能的工具/函数。
(2)为什么要使用装饰器
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时 候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修 改源代码是封闭的,对扩展功能指的是开放的。
装饰器的实现必须遵循两大原则:
1> 封闭: 对已经实现的功能代码块封闭。 不修改被装饰对象的源代码
2> 开放: 对扩展开发 装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
(3)如何实现装饰器:
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器模板:
from functools import wraps
def function(func): #func===被装饰的函数
@wraps(func) #保留被装饰函数的名字和帮助文档
def wrapper(*args, **kwargs): #形参,可变参数(元组),关键字参数(字典)
result = func(*args, **kwargs) # 实参,解包
return result
return wrapper
2、装饰器的应用场景
装饰器经常用于有切面需求的场景,比如:
插入日志、性能测试、事务处理、缓存、 权限校验等应用场景。
(1)性能测试
比如:计算代码运行时间
from functools import wraps
import time
#定义装饰器
def timeit(func): # 2 传入被装饰函数
@wraps(func)
def wrapper(*args, **kwargs): # 5
start_time = time.time() # 6
result = func(*args, **kwargs) # 7 执行func函数,即被装饰函数,即download_music,将执行结果存给result
end_time = time.time() # 11
print("%s函数运行的时间是%fs" %(func.__name__,end_time-start_time)) # 12
return result # 13
return wrapper # 3 返回wrapper给download_music
@timeit # 1 语法糖 执行的是: download_music = timeit(download_music) 调用timeit()函数
def download_music(name): # 8
time.sleep(0.2) # 9
print("下载成功") # 10
download_music('music') # 4 download_music == wrapper 即调用wrapper函数
运行结果:
(2)插入日志
#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函数
(3)事务处理
from functools import wraps
import json
import string
def json_result(func): # 2
"""转为json格式的装饰器"""
def wrapper(*args, **kwargs): # 5 这里的wrapper() 其实是get_user()函数,
result = func(*args, **kwargs) # 6 这里的func() == get_user() ,将参数进行解包 运行函数 ——> 20行,运行完有一个返回值给result
return json.dumps(result) # 9 将实际函数的运行结果转为json并返回, 也就是对函数进行了装饰
return wrapper # 3 返回一个wrapper 是给19行返回的 ,所以get_user() == wrapper
@json_result #get_user() = json_result(get_user) # 1
def get_user(): # 7
return {'user'+ item : 'password' + item for item in string.digits} # 8
result = get_user() # 4 此时,get_user() == wrapper,所以是调用wrapper函数
print(result) # 10
print(type(result))
运行结果:
(4)缓存
比如:实现斐波那契数列,由于重复性占用很大的空间且计算缓慢,所以可以利用缓存来节省时间
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()
运行结果:
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()
运行结果:
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()
这里的 @auth(‘local’) ,实际上先执行 auth(‘local’) 函数,返回函数名 desc , 再 @desc
即,@后面要跟装饰器的名称,如果@后不是名称,要先执行函数,再和@结合
六、内置高阶函数
把函数作为参数传入,或者作为返回值返回
1、map()函数
map(function, *iterable)
传入函数和可迭代对象
根据提供的函数对指定序列做映射。
当序列多于一个时,map可以并行(注意是并行)地对每个序列执行如下图所示的过程:
#传递的函数可以是内置函数
result = map(int,['1','2','3'])
for item in result:
print(item,type(item))
result1 = (int(item) for item in ['1','2','3'])
for item in result1:
print(item,type(item))
print("***********************")
#传递的函数可以是匿名函数
result2 = map(lambda x: x*2 ,[1,2,3,4])
print(result2) # 返回一个map对象,其实是一个生成器
print(list(result2)) #转成列表打印
print("***********************")
#传递的函数可以是自定义的函数
def data_process(x):
return x+4
result3 = map(data_process,[1,2,3,4])
print(list(result3))
print("***********************")
#传递的可迭代对象可以是多个
result4 = map(lambda x,y: x*2+y , [1,2,3],[1,2,3])
print(list(result4))
# x, y = 1, 1
# x, y = 2, 2
运行结果:
2、reduce() 函数
reduce(function, sequence[, initial]) -> value
对参数序列中元素进行累积
在python2中,reduce是内置高阶函数
在python3中,reduce函数需要导入:
from functools import reduce
from functools import reduce
# ((1+2)+3)+4)+5
num_add = reduce(lambda x, y: x + y,[1,2,3,4,5])
print(num_add)
#阶乘
result = reduce(lambda x, y: x * y ,[1,2,3,4,5])
print(result)
3、filter() 函数
filter(function or None, iterable)
用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
#找出1~10能被3整除的数
result = filter(lambda x : x%3 == 0,list(range(1,10)))
print(result)
print(list(result))
# 判断质数
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,50)))
print(list(result))
4、sorted() 函数
sorted(iterable, key=None, reverse=False)
key: 主要是用来进行比较的元素,只有一个参数
reverse: 排序规则,True 降序 ,False 升序(默认)。
对所有可迭代的对象进行排序操作。返回重新排序的列表。
result = sorted({1,25,3,9})
print(list(result))
在列表中有 sort() 方法
import random
#判断是否为偶数
def is_odd(x):
return x % 2 ==0
nums = list(range(10))
random.shuffle(nums) #将列表随机打乱顺序
print(nums)
#将列表中偶数排在前面,奇数排在后面
nums.sort(key=lambda x : 0 if is_odd(x) else 1)
print(nums)
sort() 和 sorted() 的区别:
(1)排序对象不同: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
(2)返回值不同:
list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值
内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
内置高阶函数练习:
from functools import reduce
# (1)对于序列每个元素求绝对值
import math
def fun(x):
if x >= 0 :
return x
else:
return (-1 * x)
result = map(fun,[6,-7,5,-9])
print(list(result))
# (2)接收用户输入3个数字 ; 依次将接收的三个数转换为整形
li = []
for count in range(3):
num = input("请输入数字:")
li.append(num)
result = map(int,li)
for item in result:
print(item)
# (3)对于序列每个元素求阶乘; 5!
def jiechen(num):
l = []
for i in range(num+1):
l.append(i)
result = reduce(lambda x, y: x * y ,[1,2,3,4,5]) #阶乘
print(result)
# (4)拿出1~100之间所有的素数
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))