Python 是不是;否则";你有开销吗?

Python 是不是;否则";你有开销吗?,python,Python,这两者之间是否存在性能差异: for item in collection: if item == badItem: break else doFunction(item) 这是: for item in collection: if item == badItem: break doFunction(item) 假设我这样做了两亿次,那么任何性能差异都会有所帮助 编辑: 我并不是根据这个问题的结果来实现这个,我

这两者之间是否存在性能差异:

for item in collection:
    if item == badItem:
        break
    else
        doFunction(item)
这是:

for item in collection:
    if item == badItem:
        break
    doFunction(item)
假设我这样做了两亿次,那么任何性能差异都会有所帮助

编辑:
我并不是根据这个问题的结果来实现这个,我只是想知道理论上什么更快。我只是好奇。

这听起来像是早熟的优化:不要这样做。

如果必须的话,在尝试优化程序之前,您应该让程序正常工作

如果你完成的应用程序比你需要的慢,那么测量,测量,测量。使用分析工具。速度慢的部分可能会让你感到惊讶。不要浪费时间修理速度不慢的部件


但是回到第一点:不要试图优化一个功能不完整的程序。

正如Grant Birchmeier所说:测量,测量,测量

在linux上使用Python 3.3.1(默认值,2013年4月17日,22:30:32)[GCC 4.7.3]在我的对话框中我得到以下结果:

testA 0.7911653139999544
testB 0.7868194140028208
testC 0.7771379340010753
使用:

collection = [random.randint (1, 10000000) for _ in range (10000000) ]
badItem = 0
collection [5000000] = 0

def doFunction (item): pass

def testA ():
    for item in collection:
        if item == badItem: break
        else: doFunction (item)

def testB ():
    for item in collection:
        if item == badItem: break
        doFunction (item)

def testC ():
    badIndex = collection.index (badItem)
    for item in collection [:badIndex]:
        doFunction (item)

YMMV.我只是比较整数,没有实际数据。我不知道你的
\uuuu eq\uuuu
有多贵,以及
函数的功能等等。

以下是两个版本的并排说明:

0 SETUP_LOOP 40 (to 43) | 0 SETUP_LOOP 40 (to 43) 3 LOAD_GLOBAL 0 (collection) | 3 LOAD_GLOBAL 0 (collection) 6 GET_ITER | 6 GET_ITER 7 FOR_ITER 32 (to 42) | 7 FOR_ITER 32 (to 42) 10 STORE_FAST 0 (item) | 10 STORE_FAST 0 (item) | 13 LOAD_FAST 0 (item) | 13 LOAD_FAST 0 (item) 16 LOAD_GLOBAL 1 (badItem) | 16 LOAD_GLOBAL 1 (badItem) 19 COMPARE_OP 2 (==) | 19 COMPARE_OP 2 (==) 22 POP_JUMP_IF_FALSE 29 | 22 POP_JUMP_IF_FALSE 29 | 25 BREAK_LOOP | 25 BREAK_LOOP 26 JUMP_ABSOLUTE 7 | 26 JUMP_FORWARD 0 (to 29) | 29 LOAD_GLOBAL 2 (doFunction) | 29 LOAD_GLOBAL 2 (doFunction) 32 LOAD_FAST 0 (item) | 32 LOAD_FAST 0 (item) 35 CALL_FUNCTION 1 | 35 CALL_FUNCTION 1 38 POP_TOP | 38 POP_TOP 39 JUMP_ABSOLUTE 7 | 39 JUMP_ABSOLUTE 7 42 POP_BLOCK | 42 POP_BLOCK 43 LOAD_CONST 0 (None) | 43 LOAD_CONST 0 (None) 46 RETURN_VALUE | 46 RETURN_VALUE 0设置循环40(至43)| 0设置循环40(至43) 3加载全局0(集合)| 3加载全局0(集合) 6获得ITER | 6获得ITER 国际热核实验堆第32号反应堆为7座(至42座)|国际热核实验堆第32号反应堆为7座(至42座) 10店快0(物品)| 10店快0(物品) | 13加载快0(项目)| 13加载快0(项目) 16负载_全局1(坏项)| 16负载_全局1(坏项) 19比较运算2(=);19比较运算2(=) 22 POP_JUMP_IF_FALSE 29 22 POP_JUMP_IF_FALSE 29 | 25断开循环| 25断开循环 26跳|绝对7 | 26跳|向前0(到29) | 29加载_全局2(doFunction)| 29加载_全局2(doFunction) 32加载快0(项目)| 32加载快0(项目) 35呼叫|功能1 | 35呼叫|功能1 38件流行上衣| 38件流行上衣 39跳|绝对7 | 39跳|绝对7 42 POP_块| 42 POP_块 43负载常数0(无)| 43负载常数0(无) 46返回值| 46返回值 如您所见,唯一的区别是
JUMP\u ABSOLUTE
(带
else
)与
JUMP\u FORWARD
(不带它)。由于这两个操作码都紧跟在中断循环之后,因此它们在任何情况下都不会运行,因此这两个版本完全等效


这就是说,在中断语句(
break/continue/return
)之后的
else
通常被认为是一种代码味道(并且需要额外的无用行)

如果您对最大性能感兴趣,那么可能值得考虑
.index
itertools.takewhile
而不是
If
的简单循环:如果您关心性能,首先运行代码,然后改进什么,最后改进那些东西

为了子孙后代,以下是我的计时结果。简单的回答是:即使经过数十亿次迭代,也没有真正的区别

With Else:
min: 0.001327058799944325
max: 0.0037289344766406884
mean: 0.002665085947631951

Without Else:
min: 0.0013189987034252226
max: 0.003550914613782652
mean: 0.002147321588495288
以及守则:

C:\>python
Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> else_version = timeit.repeat('for i in c:\n\tif i == 0: break\n\telse:i += 1','import random;c=[random.randint(0,1) for _ in range(1000000)]', number = 10000, repeat=10)
>>> nelse_version = timeit.repeat('for i in c:\n\tif i == 0: break\n\ti += 1','import random;c=[random.randint(0,1) for _ in range(1000000)]', number = 10000, repeat=10)
>>> min(else_version)
0.001327058799944325
>>> max(else_version)
0.0037289344766406884
>>> sum(else_version)/10
0.002665085947631951
>>>
>>> min(nelse_version)
0.0013189987034252226
>>> max(nelse_version)
0.003550914613782652
>>> sum(nelse_version)/10
0.002147321588495288
>>>


无论拥有
else
语句的实际成本是多少,它显然比您正在执行的任何实际操作(例如您的
\uuuuuueq\uuuuu
实现,或者您的实际
doFunction
是什么,甚至只是您机器上发生的其他事情)都要低。

检查
timeit
模块,或者
iPython
的魔力
%timeit
自己来解决这个问题。试试计时你的vs
[doFunction(item)for item in collection]
我认为第二个选项更好,因为它暗示你想做一个动作,除非有坏的项目。我非常怀疑额外的一个词会增加更多的执行时间……担心这些微小细节的性能差异,就像担心你每年露营时篝火的碳足迹一样。从技术上讲,它是非零的,我想有一些罕见的情况下,它是重要的,但总的来说,有一百万个更重要的贡献者在你的总碳足迹,即使你优化了所有这些,剩下的优化是很少值得的努力。你如何衡量(
timeit
或其他东西),记录在案?另外,这些差异是可重复的还是仅仅是噪音?
对于f in(testA,testB,testC):打印(f.\uu name,timeit.timeit(f,number=1))
这是一种糟糕的使用
timeit
的方法。仅测量一次运行,可对各种噪声提供零保护。如果需要太长时间才能运行数千次,则将基准测试缩小(但仍具有代表性)。如果你不能使它变小,至少运行几次。@delnan可以随意这样做。“并且需要额外的无用线路”。。。以及一个额外的缩进水平!虽然在很多情况下都是这样,但在这个特定的案例中,没有什么需要衡量的——两个版本生成的操作码基本相同。@volcano——他直接询问了性能影响,这就是我关注的部分。我同意你的观点,无用的代码是无用的。@火山式风格与性能不同,而且