python - Python3中使用和不使用生成器的执行时间差异
问题描述
我正在学习在 Python 3 中使用生成器,这也是我刚开始学习的。所以我正在做一个实验,看看生成器真正节省了多少时间。下面是我的代码。
import time
def fib(n):
a = b = 1
result = []
for i in range(n):
result.append(a)
a, b = b, a + b
return result
def fib_gen(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + b
def calc(n):
start_time = time.perf_counter()
fib(n)
end_time = time.perf_counter() - start_time
print(f'fib({n})',end_time, 'secs')
start_time2 = time.perf_counter()
fib_gen(n)
end_time2 = time.perf_counter() - start_time2
print(f'fib_gen({n})',end_time2, 'secs')
print('Improvement %ge ',((end_time-end_time2)/end_time)*100)
for x in range(1, 10000, 1000):
calc(x)
输出:
fib(1) 4.976999999999759e-06 secs
fib_gen(1) 3.7599999999984868e-06 secs
Improvement %ge 24.452481414533484
fib(1001) 0.000278241999999998 secs
fib_gen(1001) 1.8640000000007262e-06 secs
Improvement %ge 99.33007957102065
fib(2001) 0.0006537540000000029 secs
fib_gen(2001) 1.4999999999980307e-06 secs
Improvement %ge 99.77055589717263
fib(3001) 0.0009883400000000007 secs
fib_gen(3001) 1.7100000000019877e-06 secs
Improvement %ge 99.82698261731773
fib(4001) 0.001559401999999998 secs
fib_gen(4001) 2.5110000000001798e-06 secs
Improvement %ge 99.8389767359539
fib(5001) 0.001501413 secs
fib_gen(5001) 1.582999999999446e-06 secs
Improvement %ge 99.89456598550835
fib(6001) 0.0019986470000000027 secs
fib_gen(6001) 1.186999999999716e-06 secs
Improvement %ge 99.94060982254497
fib(7001) 0.002645030999999999 secs
fib_gen(7001) 1.3070000000024729e-06 secs
Improvement %ge 99.95058659047842
fib(8001) 0.0035235960000000004 secs
fib_gen(8001) 3.18499999999583e-06 secs
Improvement %ge 99.90960938768248
fib(9001) 0.004458613 secs
fib_gen(9001) 5.944000000000782e-06 secs
Improvement %ge 99.866684998227
我不想相信的是,每次生成器的性能都提高了近 99%。怎么会这样?我需要做什么才能更好地掌握它。
解决方案
当您调用生成器函数时,您实际上并没有在其中运行计算。
def generate_range(n):
for i in range(n):
yield i
print(generator()) # prints "<generator object f at 0x123456789>"
要从生成器中获取值,您需要调用next
它们或迭代它们:
gen = generate_range(3)
print(next(gen)) # prints 0
print(next(gen)) # prints 1
print(next(gen)) # prints 2
print(next(gen)) # raises StopIteration
for x in generator(3):
print(x) # prints 0, then 1, then 2
从最后一个示例中,您还可以看到生成器yield
按顺序获取其值,而不是一次全部获取。这允许生成器用于惰性计算——在被要求提供下一个结果之前,它们不会执行任何计算。
这是一个演示生成器如何惰性的示例:
import time
# In the real world, this could be any slow operation,
# like getting something from the internet.
def get_number_slowly(n):
time.sleep(n ** 2)
return n
def get_with_function(numbers):
results = []
for number in numbers:
results.append(get_number_slowly(number))
return results
def get_with_generator(numbers):
for number in numbers:
yield get_number_slowly(number)
numbers = [3, 2, 0, 3, 5, 1018]
# Find the first number greater than four using each of the two methods
for number in get_with_generator(numbers):
print("Checking", number, "> 4")
if number > 4:
print(number, "is greater than 4")
break
for number in get_with_function(numbers):
print("Checking", number, "> 4")
if number > 4:
print(number, "is greater than 4")
break
这两个函数将具有相同的输出,但生成器一次打印一行,总共等待 47 秒,然后打印“5 大于 4”并停止。在打印相同的get_with_function
输出之前等待了将近 12 天,因为get_number_slowly
它甚至在开始循环之前就调用了它给出的所有数字,所以它必须等待 1018 2秒才能获得最后一个数字。
所以如果你想在不同的计算之间做一些事情,或者你只想对你需要的数据进行计算,那么生成器会非常有用。
推荐阅读
- javascript - 带有 ng-if 的模板字符串有错误:$parse:syntax
- .net - 点网框架与点网核心 sdk
- rest - 按主机名过滤在我的 WireShark 中不起作用
- sql - 如何在 SQL 中查找两个日期时间行的时间平均值?
- objective-c - 如何将 Win32 FILETIME 转换为 NSDate
- reactjs - 如何在 reactjs 的新选项卡/窗口中打开表单
- angular - ngFor="Lets" 加载所有代码没有错误,但将页面留空
- unity3d - Unity3D - 从相同世界位置的原始平面获取数学平面
- passwords - 支持像 lastpass 这样的随机密码生成器的密码策略
- javascript - 在使用 create-react-app 制作的 reactjs 应用程序中,如何在项目目录结构之外的文件中导入 js 模块?