第一部分 生成器
ls = [i**2 for i in range(1, 1000001)]
for i in ls:
pass
- 缺点:占用大量内存
- 生成器
(1)采用惰性计算的方式
(2)无需一次性存储海量数据
(3)一边执行一边计算,只计算每次需要的值
(4)实际上一直在执行next()操作,直到无值可取
1.1 生成器表达式
- 海量数据,不需存储
squares = (i**2 for i in range(1000000))
for i in squares:
pass
- 求0~100的和
无需显式存储全部数据,节省内存(生成器只占用很少的内存)
sum((i for i in range(101)))
5050
1.2 生成器函数——yield
- 生成斐波那契数列
数列前两个元素为1,1 之后的元素为其前两个元素之和
def fib(max):
ls = []
n, a, b = 0, 1, 1
while n < max:
ls.append(a)
a, b = b, a + b
n = n + 1
return ls
print(fib(10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
- 构造生成器函数
在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
def fib(max):
n, a, b = 0, 1, 1
while n < max:
yield a
a, b = b, a + b
n = n + 1
print(fib(10))
for i in fib(10):
print(i)
<generator object fib at 0x000001BE11B19048>
1
1
2
3
5
8
13
21
34
55
第二部分 迭代器
2.1 可迭代对象——Iterable
- 可直接作用于for循环的对象统称为可迭代对象
- 可以使用isinstance()判断一个对象是否是Iterable对象
2.1.1 列表、元组、字符串、字典、集合、文件、range()
from collections import Iterable
print(isinstance([1, 2, 3], Iterable))
print(isinstance({"name": "Sarah"}, Iterable))
print(isinstance('Python', Iterable))
True
True
True
2.1.2 生成器
- 生成器不但可以用于for循环,还可以被next()函数调用
直到没有数据可取,抛出StopIteration
from collections import Iterable
squares = (i**2 for i in range(5))
print(isinstance(squares, Iterable))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
True
0
1
4
9
16
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-63-cb2f72ae7043> in <module>
10 print(next(squares))
11 print(next(squares))
---> 12 print(next(squares))
StopIteration:
2.2 迭代器——Iterator
- 可以被next()函数调用并不断返回下一个值,直至没有数据可取的对象称为迭代器
- 特点:
没有长度
不可索引
不可存在计算(a in b)
会被耗尽 - 可以使用isinstance()判断一个对象是否是Iterator对象
2.2.1 生成器都是迭代器
from collections import Iterator
squares = (i**2 for i in range(5))
print(isinstance(squares, Iterator))
True
2.2.2 列表、元组、字符串、字典、集合不是迭代器
from collections import Iterator
print(isinstance([1, 2, 3], Iterator))
False
可以通过iter(Iterable)创建迭代器
from collections import Iterator
print(isinstance(iter([1, 2, 3]), Iterator))
True
- for item in Iterable 等价于:
先通过iter()函数获取可迭代对象Iterable的迭代器
然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item
当遇到StopIteration的异常后循环结束
2.2.3 zip enumerate 等itertools里的函数是迭代器
from collections import Iterator
x = [1, 2]
y = ["a", "b"]
print(zip(x, y))
for i in zip(x, y):
print(i)
isinstance(zip(x, y), Iterator)
<zip object at 0x0000017678E2ED48>
(1, 'a')
(2, 'b')
True
from collections import Iterator
numbers = [1, 2, 3, 4, 5]
print(enumerate(numbers)) # 把位置与元素组合成元组
for i in enumerate(numbers):
print(i)
print(isinstance(enumerate(numbers), Iterator))
<enumerate object at 0x0000017678E27EA8>
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
True
2.2.4 文件是迭代器
from collections import Iterator
with open("测试文件.txt", "r", encoding = "utf-8") as f:
print(isinstance(f, Iterator))
True
2.2.5 迭代器是可耗尽的
squares = (i**2 for i in range(5))
for square in squares:
print(square)
print('-----------------')
for square in squares:
print(square)
0
1
4
9
16
-----------------
2.2.6 range()不是迭代器
from collections import Iterator
numbers = range(10)
print(isinstance(numbers, Iterator))
print(len(numbers)) # 有长度
print(numbers[0]) # 可索引
print(9 in numbers) # 可存在计算
for number in numbers:# 不会被耗尽
print(number)
print('------------------------')
for number in numbers:
print(number)
next(numbers) # 不可被next()调用
False
10
0
True
0
1
2
3
4
5
6
7
8
9
------------------------
0
1
2
3
4
5
6
7
8
9
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-62-17f2e64f1f80> in <module>
15 print(number)
16
---> 17 next(numbers) # 不可被next()调用
TypeError: 'range' object is not an iterator
- 可以称range()为懒序列
它是一种序列
但并不包含任何内存中的内容
而是通过计算来回答问题
第三部分 装饰器
3.1 需求的提出
(1)需要对已开发上线的程序添加某些功能
(2)不能对程序中函数的源代码进行修改
(3)不能改变程序中函数的调用方式
- 比如说,要统计每个函数的运行时间
def f1():
pass
def f2():
pass
def f3():
pass
f1()
f2()
f3()
3.2 一个简单的装饰器
3.2.1 闭包实现
import time
def timer(func):
def inner():
print("inner run")
start = time.time()
func()
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return inner
def f1():
print("f1 run")
time.sleep(1)
f1 = timer(f1) # 包含inner()和timer的环境,如传递过来的参数func
f1()
inner run
f1 run
f1 函数运行用时1.00秒
- 上面的代码中,f1() 明显已经被偷换
3.2.2 语法糖——@修饰函数名
import time
def timer(func):
def inner():
print("inner run")
start = time.time()
func()
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return inner
@timer # 相当于实现了f1 = timer(f1)
def f1():
print("f1 run")
time.sleep(1)
f1()
inner run
f1 run
f1 函数运行用时1.00秒
3.3 装饰有参函数
import time
def timer(func):
def inner(*args, **kwargs): # 接受任意多的参数
print("inner run")
start = time.time()
func(*args, **kwargs)
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return inner
@timer # 相当于实现了f1 = timer(f1)
def f1(n):
print("f1 run")
time.sleep(n)
f1(2)
inner run
f1 run
f1 函数运行用时2.00秒
3.4 装饰有返回值的函数
import time
def timer(func):
def inner(*args, **kwargs):
print("inner run")
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return res
return inner
@timer # 相当于实现了f1 = timer(f1)
def f1(n):
print("f1 run")
time.sleep(n)
return "wake up"
res = f1(2)
print(res)
inner run
f1 run
f1 函数运行用时2.00秒
wake up
3.5 带参数的装饰器
- 装饰器本身要传递一些额外参数,对不同函数进行不同修饰
需求:有时需要统计绝对时间,有时需要统计绝对时间的2倍
def timer(method):
def outer(func):
def inner(*args, **kwargs):
print("inner run")
if method == "origin":
print("origin_inner run")
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
elif method == "double":
print("double_inner run")
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("{} 函数运行双倍用时{:.2f}秒".format(func.__name__, 2*(end-start)))
return res
return inner
return outer
@timer(method="origin") # 相当于timer = timer(method = "origin") f1 = timer(f1)
def f1(): # timer对应outer,f1对应inner
print("f1 run")
time.sleep(1)
@timer(method="double")
def f2():
print("f2 run")
time.sleep(1)
f1()
print()
f2()
inner run
origin_inner run
f1 run
f1 函数运行用时1.00秒
inner run
double_inner run
f2 run
f2 函数运行双倍用时2.00秒
- 理解闭包是关键!!!
3.6 何时执行装饰器
- 一装饰就执行,不必等调用
func_names=[]
def find_function(func):
print("run")
func_names.append(func)
return func
@find_function
def f1():
print("f1 run")
@find_function
def f2():
print("f2 run")
@find_function
def f3():
print("f3 run")
for func in func_names: # 没有调用,func_names已经捕获到名字,还可以进行执行
print(func.__name__)
func()
print()
run
run
run
f1
f1 run
f2
f2 run
f3
f3 run
3.6 回归本源
- f1被偷换成了inner
原函数的属性被掩盖了
import time
def timer(func):
def inner():
print("inner run")
start = time.time()
func()
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return inner
@timer # 相当于实现了f1 = timer(f1)
def f1():
time.sleep(1)
print("f1 run")
print(f1.__name__)
inner
语法糖——@wraps(参数函数名)
import time
from functools import wraps
def timer(func):
@wraps(func) # 属性仍然是原来的属性
def inner():
print("inner run")
start = time.time()
func()
end = time.time()
print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))
return inner
@timer # 相当于实现了f1 = timer(f1)
def f1():
time.sleep(1)
print("f1 run")
print(f1.__name__)
f1()
f1
inner run
f1 run
f1 函数运行用时1.00秒