生成器
创建生成器
方法1
In [7]: nums = (x*2 for x in range(10))
In [8]: nums
Out[8]: <generator object <genexpr> at 0x7fb2a6137fc0> #生成器对象
In [9]: for num in nums:
...: print(num)
...:
0
2
4
6
8
10
12
14
16
18
方法2
定义一个函数,让这个函数变成生成器
定义一个函数实现斐波那契数列:1 1 2 3 5 8 13…
def fib(num):
a,b,count = 0,1,1
while count <= num:
print(b)
a,b = b,a+b
count +=1
fib(10)
如果在调用函数的时候,发现这个函数中有yeild
那么此时,也就不是调用函数了,而是创建了一个生成器对象
def creat_num(all_num):
print('~~~~~~~~~~~~1~~~~~~~~~~~')
a, b = 0, 1
current_num = 0
while current_num < all_num:
print('~~~~~~~~~~~~2~~~~~~~~~~~')
# 相当于暂停程序
yield b
print('~~~~~~~~~~~~3~~~~~~~~~~~')
a, b = b, a + b
current_num += 1
print('~~~~~~~~~~~~4~~~~~~~~~~~')
obj = creat_num(5)
# 返回生成器对象
print(obj)
# 用for循环不断调用yield,生成数列
for num in obj:
print(num)
# 调用next()分步执行,遇到yield就中断
ret = next(obj)
print(ret)
ret1 = next(obj)
print(ret1)
ret2 = next(obj)
print(ret2)
ret3 = next(obj)
print(ret3)
ret4 = next(obj)
print(ret4)
# 执行5次yield后,已经没有yield可执行了,所以第6次调用next()会报错
# ret5 = next(obj)
# print(ret5)
while True:
try:
ret = next(obj)
print(ret)
except Exception as red:
print(red.value)
break
# 两个生成器对象之间并没有任何关系
obj2 = creat_num(5)
ret6 = next(obj2)
print(ret6)
生成器_send
使用send唤醒程序
使用send()函数来唤醒程序执行,
使用send()函数的好处是:可以在唤醒的同时向断点中传入一个附加的数据
def creat_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
ret = yield b
print('>>>>>> ret >>>>>>',ret)
a, b = b, a + b
current_num += 1
obj = creat_num(10)
# next()不传递值
red = next(obj)
print(red)
# send传递值
red = obj.send('hello')
print(red)
#
red = obj.send(None)
print(red)
next和send得到的都是yield后面的值
不同的是:send传递值而next不传递值
注意:
不能把send放在第一个,会报错,因为第一次执行程序是从开始执行并没有值来接收send
如果你非要把send放在第一个,那么传递的值应该是None
生成器应用
实现多任务
并行(真的):有两个任务,两个cpu,一个任务占一个cpu
并发(假的):有四个任务,两个cpu,四个任务交替占有cpu执行
import time
def task1():
while True:
print('~~~~~~1~~~~~~')
time.sleep(0.1)
yield
def task2():
while True:
print('~~~~~~2~~~~~~')
time.sleep(0.1)
yield
def main():
t1 = task1()
t2 = task2()
"""
类似于两个while True一起执行
先让t1运行一会,当t1遇到yield的时候,再返回到task2()
然后执行t2,当它遇到yield的时候,再次切换到t1中
这样t1/t2/t1/t2的交替运行,最终实现了多任务---->协程
"""
while True:
next(t1)
next(t2)
main()
实现单线程并发
利用了关键字yield一次性返回一个结果,阻塞,重新开始
send 唤醒
import time
def consumer(name):
print('%s 准备学习了~' %(name))
while True:
lesson = yield
print('开始[%s]了,[%s]老师来讲课了~' %(lesson,name))
def producer():
c1 = consumer('A')
c2 = consumer('B')
c1.__next__()
c2.__next__()
print('同学们开始上课了~')
for i in range(3):
time.sleep(1)
print('到了两个同学')
c1.send(i)
c2.send(i)
producer()
使用greenlet完成多任务
为了更好的使用协程来完成多任务,python中的greeblet模块对其进行的封装
下载greenlet:/usr/local/python3/bin/pip3 install greenlet
from greenlet import greenlet
import time
def test1():
while True:
print('~~~~~1~~~~~')
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print('~~~~~2~~~~~')
gr1.switch()
time.sleep(0.5)
# greenlet这个类对yield进行的封装
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
使用gevent完成多任务
使用greenlet时需要 gr1.switch( ) 和 gr1.switch( ) 手动切换
gevent则不需要
import gevent
def f1(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5)
g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()
g2.join()
g3.join()
gevent:三个任务同时进行
gevent实现多张图片同时下载
下载图片:
import urllib.request
def main():
req = urllib.request.urlopen('https://img3.doubanio.com/view/photo/m/public/p2528834770.jpg')
img_content = req.read()
with open('1.jpg','wb') as f:
f.write(img_content)
main()
下载多张图片:
import urllib.request
import gevent
def downloder(img_name,img_url):
req = urllib.request.urlopen(img_url)
img_content = req.read()
with open(img_name,'wb') as f:
f.write(img_content)
def main():
gevent.joinall([
# 图片名 图片地址链接
gevent.spawn(downloder,'2.jpg','https://img3.doubanio.com/view/photo/m/public/p2528834770.jpg'),
gevent.spawn(downloder,'3.jpg','https://img1.doubanio.com/view/photo/l/public/p2541840518.jpg'),
gevent.spawn(downloder,'4.jpg','https://img3.doubanio.com/view/photo/l/public/p2540519750.jpg'),
gevent.spawn(downloder,'4.jpg','https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1544239785&di=0dac6c76bd63b935147a034cbf1ff1aa&src=http://image11.m1905.cn/uploadfile/2018/ss/1023/20181023110107782.jpg')
])
main()
高阶函数
高阶函数
----实参是一个函数名
----函数的返回值是一个函数
函数本身也可以赋值给变量 变量可以指向函数
# abs() 是求绝对值的函数
print(abs(-11))
f = abs
print(f(-10))
传递的参数包含函数名
def fun(x,y,f):
return f(x),f(y)
print(fun(-10,34,abs))
内置高阶函数_map
map( )函数接收两个参数,一个是函数,一个是序列
map( )将传入的函数依次作用到序列的每个元素,并且把结果作为新的序列返回
import random
# 需求1:对于序列[-1,3,-4,-5]的每个元素求绝对值
print(list(map(abs,[1,-3,-4,5])))
# 需求2:对于序列的每个元素求阶乘
def f(x):
res = 1
for i in range(1,x+1):
res = res * i
return res
s = [random.randint(1,6) for i in range(10)]
print(s)
print(list(map(f,s)))
# 需求3:用户接收遗传数字,将该字符串中的所有数字转化为整型并以列表格式输出
def f1(s):
s = int(float(s))
return s
print(list(map(f1,input().split(' '))))
练习:
题目描述:
现在IPV4下用一个32位无符号整数来表示,一般用点分方式来显示,点将IP地址分成4个部分,每个部分为8位,表示成一个无符号整数(因此不需要用正号出现);
如10.137.17.1,是我们非常熟悉的IP地址,一个IP地址串中没有空格出现(因为要表示成一个32数字);
现在需要你用程序来判断IP是否合法。
输入描述:
输入一个ip地址
输出描述:
返回判断的结果YES or NO
示例1:
输入
10.138.15.1
输出
YES
代码:
num = list(map(int, input().split('.')))
count = 0
for i in num:
if i >= 0 or i <= 255:
count += 1
if count == 4:
print('YES')
else:
print('NO')
运行结果:
内置高阶函数_reduce
reduce( )函数:这个函数必须接收两个参数,一个是函数,一个是序列
reduce( )把一个函数作用在一个序列上
reduce( )把结果继续和序列的下一个元素做累积计算
----reduce(f,[x1,x2,x3,x4]) = f(f(f(x1,x2),x3),x4)
from functools import reduce
"""
python2:reduce为内置函数
python3:from functools import reduce
"""
# 累乘
def multi(x,y):
return x*y
print(reduce(multi,range(1,4)))
# 累加
def add(x,y):
return x+y
print(reduce(add,[1,2,3,4,5]))
内置高阶函数_filter
filter( ):过滤函数
和map( )类似,filter( )也接收一个函数和一个序列
但是和map ( )不同的是,filter( )把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素
拿出1~100之间的所有偶数
# 函数只能有一个形参,函数的返回值只能是True或者False
def f(n):
if n % 2 == 0:
return True
else:
return False
print(list(filter(f,range(100))))
拿出1~100之间的所有素数
def f1(n):
for i in range(2,n):
if n % i ==0:
return False
return True
print(list(filter(f1,range(2,101))))
内置高阶函数_sorted
sort( )和sorted( ):
li = [1,2,4,6,3]
# sort()函数直接在原列表进行排序
li.sort()
print(li)
# sorted()函数对所有可迭代的对象进行排序操作,返回新的列表
a = sorted(li)
print(a)
# 默认sort()和sorted()方法由小到大进行排序,reverse=True 由大到小进行排序
a = sorted(li,reverse=True)
print(a)
sorted( )函数也是接收两个参数,一个是序列,一个是函数
不同的是,sorted( )先写序列,再写函数
Info = [
#商品名称 商品数量 商品单价
['apple1', 100, 35],
['apple3', 50, 55],
['apple4', 50, 25],
['apple2', 200, 45]
]
# 默认按商品名称进行排序
print(sorted(Info))
# 按商品数量进行排序
def sorted_by_count(x):
return x[1]
# key代表排序的关键字
print(sorted(Info, key=sorted_by_count))
# 按商品单价进行排序
def sorted_by_price(x):
return x[2]
print(sorted(Info, key=sorted_by_price))
# 先按商品数量进行排序,如果商品数量一致,则按商品单价进行排序
def sorted_by_count_price(x):
return x[1], x[2]
print(sorted(Info, key=sorted_by_count_price))
练习:
题目需求:
给定一个整型数组, 将数组中所有的0移动到末尾, 非0项保持不变;
在原始数组上进行移动操作, 勿创建新的数组;
输入:
第一行是数组长度, 后续每一行是数组的一条记录;
4
0
7
0
2
输出:
调整后数组的内容;
7
2
0
0
代码:
n = int(input('数组长度:'))
str = [int(input()) for i in range(n)]
def move_zero(item):
#给0项赋值1,非0项赋值0
if item == 0:
return 1
else:
return 0
print('输出:')
#将赋值后的列表进行升序排序,则0项移至末尾,非0项不变,然后逐个输出
for i in sorted(str,key=move_zero):
print(i)
运行结果:
匿名函数
匿名函数:
关键字为lambda
冒号前面是形参,冒号后面是返回值
和高阶函数结合使用
reduce
from functools import reduce
# reduce()函数实现累加
def add(x, y):
return x + y
print(reduce(add, [1, 2, 3, 4, 5]))
# 匿名函数结合reduce()函数实现累加
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))
map
# map()函数求序列内元素的平方
def mypow(x):
return x ** 2
print(list(map(mypow, range(5))))
# 匿名函数结合map()函数求序列内元素的平方
print(list(map(lambda x: x ** 2, range(5))))
filter
# filter()函数找序列中的偶数
def is_odd(x):
return x % 2 == 0
print(list(filter(is_odd, range(10))))
# 匿名函数结合filter()函数找序列中的偶数
print(list(filter(lambda x: x % 2 == 0, range(10))))
sorted
# 匿名函数结合sorted()函数对序列进行排序
Info = [
# 商品名称 商品数量 商品单价
['apple1', 100, 35],
['apple3', 50, 55],
['apple4', 50, 25],
['apple2', 200, 45]
]
# 默认按商品名称进行排序
print(sorted(Info))
# 按商品数量进行排序
print(sorted(Info, key=lambda x: x[1]))
# 按商品单价进行排序
print(sorted(Info, key=lambda x: x[2]))
# 先按商品数量进行排序,如果商品数量一致,则按商品单价进行排序
print(sorted(Info, key=lambda x:(x[1],x[2])))
# 对于字典里面嵌套字典进行排序
d = {
'003':{
'name':'apple1',
'count':100,
'price':10
},
'002':{
'name':'apple2',
'count':200,
'price':2
}
}
print(d.items()) # [('key',{}),(),()]
# x:('003', {'name': 'apple1', 'count': 100, 'price': 10})
#
print(sorted(d.items(),key=lambda x:x[1]['count']))
print(sorted(d.items(),key=lambda x:x[1]['price']))
练习_奇偶数排序
问题描述:
有一个整数列表(10个元素), 要求调整元素顺序,把所有的奇数放在前面, 偶数放在后面。
代码:
import random
# 方法一:
li = [random.randint(1,10) for i in range(10)]
print(sorted(li,key=lambda n:(n%2==0)))
# 方法二:
print(sorted(list(map(lambda x:random.randint(1,10),range(10))),key=lambda n:n%2==0))
运行结果:
装饰器
装饰器:
把一个函数当作参数,返回一个替代版的函数
本质上就是一个返回函数的函数
在不改变原函数的基础上,给函数增加功能
def outer(func):
def inner(age):
if age<0:
age = 0
func(age)
return inner
@outer # 语法糖,相当于 say = outer(say)
def say(age):
print('man is %s years old' %age)
# say = outer(say)
say(-1)
装饰器的应用
多个函数使用一个装饰器
def desc(fun): # 需要传递一个函数,要装饰的函数
def add_info(): # 装饰器函数里要嵌套函数
print('元旦快乐~')
fun()
print('欢迎光临西部开源~')
return add_info # 返回值是嵌套的函数对象
@desc # 如何调用装饰器(两种方式)
def login():
print('login....')
# login = desc(login) # 返回值是一个函数
# login()
@desc
def logout():
print('logout...')
@desc
def savemoney():
print('存钱...')
@desc
def transfermoney():
print('转账...')
login()
logout()
savemoney()
transfermoney()
装饰器实现一个计时器
import time
import string
import random
import functools
li = [random.choice(string.ascii_letters) for i in range(10)]
# 问题1:装饰的函数有返回值
# 问题2:如何保留被装饰函数的函数名和帮助信息文档
def timeit(fun):
"""这是一个装饰器timeit"""
@functools.wraps(fun) # 可以保留被装饰函数的函数名和帮助信息文档
def add_time(*args, **kwargs): # 接收可变参数和关键字参数
"""这是一个add_time函数"""
# 在函数执行之前计时
start_time = time.time()
# 执行函数
# 函数有返回值时,将函数赋给res
res = fun(*args, **kwargs)
# 在函数执行之后计时
end_time = time.time()
print('运行时间:%.6f' % (end_time - start_time))
# 返回res
return res
return add_time
@timeit
def con_add():
"""这是con_add函数,被timeit装饰"""
s = ''
for i in li:
s += i + ','
print(s)
@timeit
def join_add():
"""这是join_add函数,被timeit装饰"""
print(','.join(li))
@timeit
def fun_list(n):
# 函数有返回值
return (i for i in range(n) if n % 2 == 0)
@timeit
def fun_filter(n):
return (filter(lambda x: (x % 2 == 0), range(n)))
def func():
"""这是func函数"""
print('hello')
con_add()
join_add()
print(fun_list(5000))
print(fun_filter(5000))
print(func.__doc__)
print(func.__name__)
print(con_add.__doc__)
print(con_add.__name__)
print(join_add.__doc__)
print(join_add.__name__)
问题2的解决:
装饰器实现对用户权限的判断
import functools
import inspect
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# inspect.getcallargs返回一个字典,key值:形参,value值:对应的实参
inspect_res = inspect.getcallargs(fun, *args, **kwargs)
print('inspect的返回值:%s' % inspect_res)
if inspect_res.get('name') == 'admin':
res = fun(*args, **kwargs)
return res
else:
print('not admin user,no permisson!')
return wrapper
@is_admin
def add_student(name):
print('添加学生信息~')
@is_admin
def del_student(name):
print('删除学生信息~')
add_student('admin')
del_student('student')
练习_创建装饰器
创建装饰器,要求如下:
1.创建add_log装饰器,被装饰的函数打印日志信息
2.日志格式为:[字符串时间] 函数名:xxx, 运行时间:xxx, 运行返回值结果:xxx
代码:
import time
import functools
def add_log(fun):
@functools.wraps(fun)
def print_Info(*args, **kwargs):
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print('字符串时间:[%s] 函数名:%s 运行时间:%.6f 运行返回值结果:%s'
%(time.ctime(),fun.__name__,end_time - start_time,res))
return res
return print_Info
@add_log
def fun_filter(n):
time.sleep(1)
return list(filter(lambda x: (x % 2 == 0), range(n)))
fun_filter(5)
运行结果:
多个装饰器
有多个装饰器的时候,函数从上到下执行的
def decorator_a(fun):
print('Get in decorator_a')
def inner_a(*args,**kwargs):
print('Get in inner_a')
return fun(*args,**kwargs)
return inner_a
def decorator_b(fun):
print('Get in decorator_b')
def inner_b(*args,**kwargs):
print('Get in inner_b')
return fun(*args,**kwargs)
return inner_b
"""
当有多个装饰器的时候,装饰器decorator是从下到上调用
但真实的inner函数内容是从上到下执行的
"""
@decorator_b
@decorator_a
def f():
print('Get in f')
f()
应用
用多个装饰器先验证用户是否登陆成功,再验证是否有权限
import functools
import inspect
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
inspect_res = inspect.getcallargs(fun, *args, **kwargs)
if inspect_res.get('name') == 'admin':
res = fun(*args, **kwargs)
return res
else:
print('%s not admin user,no permisson!' % args[0])
return wrapper
login_session = ['admin', 'root', 'westos']
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if args[0] in login_session:
res = fun(*args, **kwargs)
return res
else:
print('%s not login!' % args[0])
return wrapper
@is_login
@is_admin
def add_student(name):
print('添加学生信息~')
add_student('redhat')
add_student('westos')
add_student('admin')
练习_创建装饰器
编写装饰器required_ints,条件如下:
1.确保函数收到的每一个参数都是整数
2.如果参数不是整型数,打印 TypeError:参数必须为整型
代码:
import functools
def required_ints(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
for i in args:
if not isinstance(i, int):
print('TypeError:参数必须为整型')
return
else:
res = fun(*args, **kwargs)
return res
return wrapper
@required_ints
def add(x, y):
print(x+y)
add(1, 2)
add(1, 2.5)
运行结果:
带参数的装饰器
应用
import time
import functools
def log(kind): # kind = 'debug'
def add_log(fun):
@functools.wraps(fun)
def print_Info(*args, **kwargs):
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print('字符串格式:<%s> 字符串时间:[%s] 函数名:%s 运行时间:%.6f 运行返回值结果:%s'
%(kind,time.ctime(),fun.__name__,end_time - start_time,res))
return res
return print_Info
return add_log
@log('debug')
# log('debug') ====> 返回值:add_log
def add(x,y):
time.sleep(1)
return x+y
add(5,6)
练习_创建装饰器
编写装饰器required_types,条件如下:
1.当装饰器为@required_types(int,float)时,确保函数接收到的每一个参数都是int或者float类型;
2.当装饰器为@required_types(list)时,确保函数接收到的每一个参数都是list类型;
3.当装饰器为@required_types(str,int)时,确保函数接收到的每一个参数都是str或者int类型;
4.如果参数不满足条件,打印 TypeError:参数必须为xxxx类型
代码:
import functools
def required_types(*kind):
def required(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
for i in args:
if not isinstance(i, kind):
print('TypeError:参数必须为:',kind)
return
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required
@required_types(int,float)
def add(x, y):
print(x , y)
add(1.5,3)
add('westos', 2.5)
运行结果: