Python利器
Python中的三大利器包括:迭代器,生成器,装饰器,利用好它们才能开发出最高性能的Python程序,涉及到的内置模块itertools提供迭代器相关的操作。此部分收录有意思的例子共计14例。
1 寻找第n次出现位置
def search_n(s, c, n):
size =
fori, xinenumerate(s):
ifx == c:
size +=1
ifsize == n:
returni
return-1
print(search_n("fdasadfadf","a",3))# 结果为7,正确
print(search_n("fdasadfadf","a",30))# 结果为-1,正确
2 斐波那契数列前n项
def fibonacci(n):
a, b =1,1
for_inrange(n):
yielda
a, b = b, a + b
list(fibonacci(5))# [1, 1, 2, 3, 5]
3 找出所有重复元素
fromcollectionsimportCounter
def find_all_duplicates(lst):
c = Counter(lst)
returnlist(filter(lambdak: c[k] >1, c))
find_all_duplicates([1,2,2,3,3,3])# [2,3]
4 联合统计次数
Counter对象间可以做数学运算
fromcollectionsimportCounter
a = ['apple','orange','computer','orange']
b = ['computer','orange']
ca = Counter(a)
cb = Counter(b)#Counter对象间可以做数学运算
ca + cb# Counter({'orange': 3, 'computer': 2, 'apple': 1})
# 进一步抽象,实现多个列表内元素的个数统计
def sumc(*c):
if(len(c)
return
mapc = map(Counter, c)
s = Counter([])
foricinmapc:# ic 是一个Counter对象
s += ic
returns
#Counter({'orange': 3, 'computer': 3, 'apple': 1, 'abc': 1, 'face': 1})
sumc(a, b, ['abc'], ['face','computer'])
5 groupby单字段分组
天气记录:
a = [{'date':'2019-12-15','weather':'cloud'},
{'date':'2019-12-13','weather':'sunny'},
{'date':'2019-12-14','weather':'cloud'}]
按照天气字段weather分组汇总:
fromitertoolsimportgroupbyfork, itemsingroupby(a,key=lambdax:x['weather']):
print(k)
输出结果看出,分组失败!原因:分组前必须按照分组字段排序,这个很坑~
cloud
sunny
cloud
修改代码:
a.sort(key=lambdax: x['weather'])fork, itemsingroupby(a,key=lambdax:x['weather']):
print(k)
foriinitems:
print(i)
输出结果:
cloud
{'date':'2019-12-15','weather':'cloud'}
{'date':'2019-12-14','weather':'cloud'}
sunny
{'date':'2019-12-13','weather':'sunny'}
6 itemgetter和key函数
注意到sort和groupby所用的key函数,除了lambda写法外,还有一种简写,就是使用itemgetter:
a = [{'date':'2019-12-15','weather':'cloud'},
{'date':'2019-12-13','weather':'sunny'},
{'date':'2019-12-14','weather':'cloud'}]fromoperatorimportitemgetterfromitertoolsimportgroupby
a.sort(key=itemgetter('weather'))fork, itemsingroupby(a, key=itemgetter('weather')):
print(k)
foriinitems:
print(i)
结果:
cloud
{'date':'2019-12-15','weather':'cloud'}
{'date':'2019-12-14','weather':'cloud'}
sunny
{'date':'2019-12-13','weather':'sunny'}
7 groupby多字段分组
itemgetter是一个类,itemgetter('weather')返回一个可调用的对象,它的参数可有多个:
fromoperatorimportitemgetterfromitertoolsimportgroupby
a.sort(key=itemgetter('weather','date'))fork, itemsingroupby(a, key=itemgetter('weather')):
print(k)
foriinitems:
print(i)
结果如下,使用weather和date两个字段排序a,
cloud
{'date':'2019-12-14','weather':'cloud'}
{'date':'2019-12-15','weather':'cloud'}
sunny
{'date':'2019-12-13','weather':'sunny'}
注意这个结果与上面结果有些微妙不同,这个更多是我们想看到和使用更多的。
8 sum函数计算和聚合同时做
Python中的聚合类函数sum,min,max第一个参数是iterable类型,一般使用方法如下:
a = [4,2,5,1]
sum([i+1foriina])# 16
使用列表生成式[i+1 for i in a]创建一个长度与a一行的临时列表,这步完成后,再做sum聚合。
试想如果你的数组a长度十百万级,再创建一个这样的临时列表就很不划算,最好是一边算一边聚合,稍改动为如下:
a = [4,2,5,1]
sum(i+1foriina)# 16
此时i+1 for i in a是(i+1 for i in a)的简写,得到一个生成器(generator)对象,如下所示:
In [8]:(i+1foriina)
OUT [8]: at0x000002AC7FFA8CF0>
生成器每迭代一步吐出(yield)一个元素并计算和聚合后,进入下一次迭代,直到终点。
9 list分组(生成器版)
frommathimportceil
def divide_iter(lst, n):
yieldlst
return
i, div =, ceil(len(lst) / n)
whilei
yieldlst[i * div: (i +1) * div]
i +=1
list(divide_iter([1,2,3,4,5],))# [[1, 2, 3, 4, 5]]
list(divide_iter([1,2,3,4,5],2))# [[1, 2, 3], [4, 5]]
10 列表全展开(生成器版)
#多层列表展开成单层列表
a=[1,2,[3,4,[5,6],7],8,["python",6],9]def function(lst):
foriinlst:
iftype(i)==list:
yieldfromfunction(i)
else:
yieldi
print(list(function(a)))# [1, 2, 3, 4, 5, 6, 7, 8, 'python', 6, 9]
11 测试函数运行时间的装饰器
#测试函数执行时间的装饰器示例importtimedef timing_func(fn):
def wrapper():
start=time.time()
fn()#执行传入的fn参数
stop=time.time()
return(stop-start)
returnwrapper@timing_funcdef test_list_append():
lst=[]
foriinrange(,100000):
lst.append(i)@timing_funcdef test_list_compre():
[iforiinrange(,100000)]#列表生成式
a=test_list_append()
c=test_list_compre()
print("test list append time:",a)
print("test list comprehension time:",c)
print("append/compre:",round(a/c,3))
test list append time:0.0219423770904541
test list comprehension time:0.007980823516845703
append/compre:2.749
12 统计异常出现次数和时间的装饰器
写一个装饰器,统计某个异常重复出现指定次数时,经历的时长。
importtimeimportmath
def excepter(f):
i =
t1 = time.time()
def wrapper():
try:
f()
exceptExceptionase:
nonlocali
i +=1
print(f': ')
t2 = time.time()
ifi == n:
print(f'spending time:')
returnwrapper
关键词nonlocal常用于函数嵌套中,声明变量i为非局部变量;
如果不声明,i+=1表明i为函数wrapper内的局部变量,因为在i+=1引用(reference)时,i未被声明,所以会报unreferenced variable的错误。
使用创建的装饰函数excepter,n是异常出现的次数。
共测试了两类常见的异常:被零除和数组越界。
n =10# except count
@excepterdef divide_zero_except():
time.sleep(0.1)
j =1/(40-20*2)
# test zero divived exceptfor_inrange(n):
divide_zero_except()
@excepterdef outof_range_except():
a = [1,3,5]
time.sleep(0.1)
print(a[3])# test out of range exceptfor_inrange(n):
outof_range_except()
打印出来的结果如下:
division by zero:1
division by zero:2
division by zero:3
division by zero:4
division by zero:5
division by zero:6
division by zero:7
division by zero:8
division by zero:9
division by zero:10
spending time:1.01
list index out of range:1
list index out of range:2
list index out of range:3
list index out of range:4
list index out of range:5
list index out of range:6
list index out of range:7
list index out of range:8
list index out of range:9
list index out of range:10
spending time:1.01
13 定制递减迭代器
#编写一个迭代器,通过循环语句,实现对某个正整数的依次递减1,直到0.class Descend(Iterator):
def __init__(self,N):
self.N=N
self.a=
def __iter__(self):
returnself
def __next__(self):
whileself.a
self.N-=1
returnself.N
raiseStopIteration
descend_iter=Descend(10)
print(list(descend_iter))
[9,8,7,6,5,4,3,2,1,]
核心要点:
1__nex__名字不能变,实现定制的迭代逻辑
2raise StopIteration:通过raise中断程序,必须这样写
14 测试运行时长的装饰器
#测试函数执行时间的装饰器示例importtimedef timing(fn):
def wrapper():
start=time.time()
fn()#执行传入的fn参数
stop=time.time()
return(stop-start)
returnwrapper
@timingdef test_list_append():
lst=[]
foriinrange(,100000):
lst.append(i)
@timingdef test_list_compre():
[iforiinrange(,100000)]#列表生成式
a=test_list_append()
c=test_list_compre()
print("test list append time:",a)
print("test list comprehension time:",c)
print("append/compre:",round(a/c,3))
# test list append time: 0.0219# test list comprehension time: 0.00798# append/compre: 2.749
领取专属 10元无门槛券
私享最新 技术干货