在我们需要根据条件搜索最佳元素的函数中(如Min-Max函数:搜索最小值或最大值(,我看到一些(或许多(人在条件检查的同时,在循环中检查包含最佳答案的变量是否为None。
为了说明这一点,让以下2个源代码:
A(在循环中使用测试is None
:
list_vals = [10,9,8,7,6,5,4,3,2,1]
min_val = None
for val in list_vals:
if min_val is None or val<min_val:
min_val = val
print("min = ", min_val)
B( 最佳接收第一元素
list_vals = [10,9,8,7,6,5,4,3,2,1]
min_val = list_val[0]
for val in list_vals:
if val<min_val:
min_val = val
print("min = ", min_val)
我的问题是:
- 为什么有些人在做第一种形式(A(?这是一个很好的练习吗
- 如果我们使用第一个from,这意味着在每次迭代中,我们都会检查变量是否为None,我不知道编译器是否对此进行了优化
为了查看这个例子的执行时间是否相似,我用计算了A和B两种方法的时间
nb = 100000000 # 100_000_000
list_vals = random.sample(range(nb), nb)
- 时间(A(=24.00 s
- 时间(B(=19.31秒
我用其他长度重复了测试,结果是一样的,B方法的分析速度(相对(比A方法快。
谢谢你的帮助。
编辑一个:假设list_vals不为空。谢谢@0x5453
在通用代码中,您可能希望使用任意可迭代项,而不仅仅是list
,在这种情况下,只有第一种形式才有效(不能对非序列进行索引(。您可能还需要处理潜在的空输入(A通过生成None
来处理它们,而B则引发IndexError
(。它确实增加了非零成本(CPython只有最简单、最局部的优化器;它不能像"min_val
在第一个循环上只有None
"这样进行广泛的推导(。如果性能至关重要,您可以通过以下方式两全其美(尽管代码稍丑(:
vals = ... could be any iterable ...
iter_vals = iter(vals) # Explicitly convert to iterator (if already an iterator, just returns vals at trivial cost)
min_val = next(iter_vals, None) # Pulls first value, or None if vals was empty
for val in iter_vals: # Iterates rest of values looking for minimum
if val < min_val:
min_val = val
print("min = ", min_val)
在这种情况下,您不需要假设输入是一个序列,也不需要对数据进行不必要的复制(切片list_vals[1:]
可以做到这一点(,也不必为初始情况创建一个sentinel值,因为您可以安全地获取第一个元素一次,而且不需要对其本身测试初始值(因为有状态迭代器只生成一次初始值(。