python中chs_chs/ch4.md · ChangWenhan/python-small-examples - Gitee.com

🚀 🚀 告别枯燥,60 秒学会一个 Python 小例子 🔥🔥

是的,60 秒学会一个 Python 小例子 🔥🔥

当前库已有🍎🍎 **223** 🍎🍎个实用的小例子

下载本库所有例子 **PDF** 版本,请关注 《Python小例子》官方公众号后回复 **mypy** 🍏 🍏

如果转载本库小例子,请附上例子来源,链接:https://github.com/jackzhenguo/python-small-examples

### 四、Python三大利器

Python中的三大利器包括:`迭代器`,`生成器`,`装饰器`,利用好它们才能开发出最高性能的Python程序,涉及到的内置模块 `itertools`提供迭代器相关的操作。此部分收录有意思的例子共计`15`例。

#### 142 寻找第n次出现位置

```python

def search_n(s, c, n):

size = 0

for i, x in enumerate(s):

if x == c:

size += 1

if size == n:

return i

return -1

print(search_n("fdasadfadf", "a", 3))# 结果为7,正确

print(search_n("fdasadfadf", "a", 30))# 结果为-1,正确

```

#### 143 斐波那契数列前n项

```python

def fibonacci(n):

a, b = 1, 1

for _ in range(n):

yield a

a, b = b, a + b

list(fibonacci(5)) # [1, 1, 2, 3, 5]

```

#### 144 找出所有重复元素

```python

from collections import Counter

def find_all_duplicates(lst):

c = Counter(lst)

return list(filter(lambda k: c[k] > 1, c))

find_all_duplicates([1, 2, 2, 3, 3, 3]) # [2,3]

```

#### 145 联合统计次数

Counter对象间可以做数学运算

```python

from collections import Counter

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) < 1):

return

mapc = map(Counter, c)

s = Counter([])

for ic in mapc: # ic 是一个Counter对象

s += ic

return s

#Counter({'orange': 3, 'computer': 3, 'apple': 1, 'abc': 1, 'face': 1})

sumc(a, b, ['abc'], ['face', 'computer'])

```

#### 146 groupby单字段分组

天气记录:

```python

a = [{'date': '2019-12-15', 'weather': 'cloud'},

{'date': '2019-12-13', 'weather': 'sunny'},

{'date': '2019-12-14', 'weather': 'cloud'}]

```

按照天气字段`weather`分组汇总:

```python

from itertools import groupby

for k, items in groupby(a,key=lambda x:x['weather']):

print(k)

```

输出结果看出,分组失败!原因:分组前必须按照分组字段`排序`,这个很坑~

```python

cloud

sunny

cloud

```

修改代码:

```python

a.sort(key=lambda x: x['weather'])

for k, items in groupby(a,key=lambda x:x['weather']):

print(k)

for i in items:

print(i)

```

输出结果:

```python

cloud

{'date': '2019-12-15', 'weather': 'cloud'}

{'date': '2019-12-14', 'weather': 'cloud'}

sunny

{'date': '2019-12-13', 'weather': 'sunny'}

```

#### 147 itemgetter和key函数

注意到`sort`和`groupby`所用的`key`函数,除了`lambda`写法外,还有一种简写,就是使用`itemgetter`:

```python

a = [{'date': '2019-12-15', 'weather': 'cloud'},

{'date': '2019-12-13', 'weather': 'sunny'},

{'date': '2019-12-14', 'weather': 'cloud'}]

from operator import itemgetter

from itertools import groupby

a.sort(key=itemgetter('weather'))

for k, items in groupby(a, key=itemgetter('weather')):

print(k)

for i in items:

print(i)

```

结果:

```python

cloud

{'date': '2019-12-15', 'weather': 'cloud'}

{'date': '2019-12-14', 'weather': 'cloud'}

sunny

{'date': '2019-12-13', 'weather': 'sunny'}

```

#### 148 groupby多字段分组

`itemgetter`是一个类,`itemgetter('weather')`返回一个可调用的对象,它的参数可有多个:

```python

from operator import itemgetter

from itertools import groupby

a.sort(key=itemgetter('weather', 'date'))

for k, items in groupby(a, key=itemgetter('weather')):

print(k)

for i in items:

print(i)

```

结果如下,使用`weather`和`date`两个字段排序`a`,

```python

cloud

{'date': '2019-12-14', 'weather': 'cloud'}

{'date': '2019-12-15', 'weather': 'cloud'}

sunny

{'date': '2019-12-13', 'weather': 'sunny'}

```

注意这个结果与上面结果有些微妙不同,这个更多是我们想看到和使用更多的。

#### 149 sum函数计算和聚合同时做

Python中的聚合类函数`sum`,`min`,`max`第一个参数是`iterable`类型,一般使用方法如下:

```python

a = [4,2,5,1]

sum([i+1 for i in a]) # 16

```

使用列表生成式`[i+1 for i in a]`创建一个长度与`a`一行的临时列表,这步完成后,再做`sum`聚合。

试想如果你的数组`a`长度十百万级,再创建一个这样的临时列表就很不划算,最好是一边算一边聚合,稍改动为如下:

```python

a = [4,2,5,1]

sum(i+1 for i in a) # 16

```

此时`i+1 for i in a`是`(i+1 for i in a)`的简写,得到一个生成器(`generator`)对象,如下所示:

```python

In [8]:(i+1 for i in a)

OUT [8]: at 0x000002AC7FFA8CF0>

```

生成器每迭代一步吐出(`yield`)一个元素并计算和聚合后,进入下一次迭代,直到终点。

#### 150 list分组(生成器版)

```python

from math import ceil

def divide_iter(lst, n):

if n <= 0:

yield lst

return

i, div = 0, ceil(len(lst) / n)

while i < n:

yield lst[i * div: (i + 1) * div]

i += 1

list(divide_iter([1, 2, 3, 4, 5], 0)) # [[1, 2, 3, 4, 5]]

list(divide_iter([1, 2, 3, 4, 5], 2)) # [[1, 2, 3], [4, 5]]

```

#### 151 列表全展开(生成器版)

```python

#多层列表展开成单层列表

a=[1,2,[3,4,[5,6],7],8,["python",6],9]

def function(lst):

for i in lst:

if type(i)==list:

yield from function(i)

else:

yield i

print(list(function(a))) # [1, 2, 3, 4, 5, 6, 7, 8, 'python', 6, 9]

```

#### 152 测试函数运行时间的装饰器

```python

#测试函数执行时间的装饰器示例

import time

def timing_func(fn):

def wrapper():

start=time.time()

fn() #执行传入的fn参数

stop=time.time()

return (stop-start)

return wrapper

@timing_func

def test_list_append():

lst=[]

for i in range(0,100000):

lst.append(i)

@timing_func

def test_list_compre():

[i for i in range(0,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

```

#### 153 统计异常出现次数和时间的装饰器

写一个装饰器,统计某个异常重复出现指定次数时,经历的时长。

```python

import time

import math

def excepter(f):

i = 0

t1 = time.time()

def wrapper():

try:

f()

except Exception as e:

nonlocal i

i += 1

print(f'{e.args[0]}: {i}')

t2 = time.time()

if i == n:

print(f'spending time:{round(t2-t1,2)}')

return wrapper

```

关键词`nonlocal`常用于函数嵌套中,声明变量i为非局部变量;

如果不声明,`i+=1`表明`i`为函数`wrapper`内的局部变量,因为在`i+=1`引用(reference)时,`i`未被声明,所以会报`unreferenced variable`的错误。

使用创建的装饰函数`excepter`, `n`是异常出现的次数。

共测试了两类常见的异常:`被零除`和`数组越界`。

```python

n = 10 # except count

@excepter

def divide_zero_except():

time.sleep(0.1)

j = 1/(40-20*2)

# test zero divived except

for _ in range(n):

divide_zero_except()

@excepter

def outof_range_except():

a = [1,3,5]

time.sleep(0.1)

print(a[3])

# test out of range except

for _ in range(n):

outof_range_except()

```

打印出来的结果如下:

```python

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

```

#### 154 测试运行时长的装饰器

```python

#测试函数执行时间的装饰器示例

import time

def timing(fn):

def wrapper():

start=time.time()

fn() #执行传入的fn参数

stop=time.time()

return (stop-start)

return wrapper

@timing

def test_list_append():

lst=[]

for i in range(0,100000):

lst.append(i)

@timing

def test_list_compre():

[i for i in range(0,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

```

#### 155 装饰器通俗理解

再看一个装饰器:

```python

def call_print(f):

def g():

print('you\'re calling %s function'%(f.__name__,))

return g

```

使用`call_print`装饰器:

```python

@call_print

def myfun():

pass

@call_print

def myfun2():

pass

```

myfun()后返回:

```python

In [27]: myfun()

you're calling myfun function

In [28]: myfun2()

you're calling myfun2 function

```

**使用call_print**

你看,`@call_print`放置在任何一个新定义的函数上面,都会默认输出一行,你正在调用这个函数的名。

这是为什么呢?注意观察新定义的`call_print`函数(加上@后便是装饰器):

```python

def call_print(f):

def g():

print('you\'re calling %s function'%(f.__name__,))

return g

```

它必须接受一个函数`f`,然后返回另外一个函数`g`.

**装饰器本质**

本质上,它与下面的调用方式效果是等效的:

```

def myfun():

pass

def myfun2():

pass

def call_print(f):

def g():

print('you\'re calling %s function'%(f.__name__,))

return g

```

下面是最重要的代码:

```

myfun = call_print(myfun)

myfun2 = call_print(myfun2)

```

大家看明白吗?也就是call_print(myfun)后不是返回一个函数吗,然后再赋值给myfun.

再次调用myfun, myfun2时,效果是这样的:

```python

In [32]: myfun()

you're calling myfun function

In [33]: myfun2()

you're calling myfun2 function

```

你看,这与装饰器的实现效果是一模一样的。装饰器的写法可能更加直观些,所以不用显示的这样赋值:`myfun = call_print(myfun)`,`myfun2 = call_print(myfun2)`,但是装饰器的这种封装,猛一看,有些不好理解。

#### 156 定制递减迭代器

```python

#编写一个迭代器,通过循环语句,实现对某个正整数的依次递减1,直到0.

class Descend(Iterator):

def __init__(self,N):

self.N=N

self.a=0

def __iter__(self):

return self

def __next__(self):

while self.a

self.N-=1

return self.N

raise StopIteration

descend_iter=Descend(10)

print(list(descend_iter))

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

```

核心要点:

1 `__nex__ `名字不能变,实现定制的迭代逻辑

2 `raise StopIteration`:通过 raise 中断程序,必须这样写

一键复制

编辑

Web IDE

原始数据

按行查看

历史

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值