是否有一种 Pythonic 的方式来跳过 for 循环中的 if 语句以使我的代码运行得更快?
Posted
技术标签:
【中文标题】是否有一种 Pythonic 的方式来跳过 for 循环中的 if 语句以使我的代码运行得更快?【英文标题】:Is there a Pythonic way of skipping if statements in a for loop to make my code run faster? 【发布时间】:2019-11-08 13:33:38 【问题描述】:我正在用 Python 编写一个脚本,该脚本本质上是掷骰子并检查掷骰子是否超过数字x
。我想重复这个过程n
次,得到掷骰子数超过x
的概率。例如
Count = 0
for _ in itertools.repeat(None, Iterations):
x = 3
die_roll = rnd.randint(1,6)
if die_roll > x:
Count += 1
Probability_of_exceed = Count / Iterations
我想根据用户输入同时修改骰子和 x。此用户输入将选择不同的例程来修改脚本,例如"Andy's_Routine"
可能会将 x
更改为 4
。目前,我在 for 循环中使用 if 语句来检查哪些例程处于活动状态,然后应用它们,例如
Count = 0
for _ in itertools.repeat(None, Iterations):
x = 3
if "Andy's_Routine" in Active_Routines:
x = 4
die_roll = rnd.randint(1,6)
if "Bill's_Routine" in Active_Routines:
die_roll += 1
if "Chloe's_Routine" in Active_Routines:
# do something
pass
if "Person_10^5's_Routine" in Active_Routines:
# do something else
pass
if die_roll > x:
Count += 1
Probability_of_exceed = Count / Iterations
在实践中,例程并不是那么简单,以至于它们可以被概括,例如,它们可能会添加额外的输出。这些例程可以同时执行。问题是可能有成千上万个不同的例程,这样每个循环将花费大部分时间检查 if 语句,从而减慢程序的速度。
有没有更好的方法来构造代码来检查哪些例程只使用一次,然后以某种方式修改迭代?
【问题讨论】:
您在寻找continue
声明吗?
你为什么要使用 itertools.repeat?
您在寻找类似 switch 语句的东西吗?如果是这样,python 不支持,但你可以使用 dict
来做类似的事情(虽然在你的情况下会做更多的工作)。
不是Active_Routines
是一个字符串列表,而是一个函数列表,然后在每个循环迭代中执行所有这些函数。
if
语句不太可能导致您的代码运行缓慢。这更有可能是因为您正在执行许多顺序功能。如果您的迭代次数很高,那么可以为x
和die_roll
的组合构建一个结果表,这样如果您的迭代具有与前一个相同的状态,而不是重新计算所有函数,只需查找您之前计算的结果(即记忆化)。
【参考方案1】:
您在这里问两件事 - 您希望您的代码更加 Pythonic,并且您希望它运行得更快。
第一个更容易回答:将Active_Routines
设为函数列表而不是字符串列表,然后从列表中调用函数。由于这些函数可能需要更改本地状态(x
和 die_roll
),因此您需要将状态作为参数传递给它们,并让它们返回新状态。重构可能如下所示:
def Andy(x, die_roll):
return (4, die_roll)
def Bill(x, die_roll):
return (x, die_roll + 1)
def Chloe(x, die_roll):
# do something
return (x, die_roll)
Active_Routines = [Andy, Bill, Chloe]
Count = 0
for i in range(Iterations):
x = 3
die_roll = rnd.randint(1,6)
for routine in Active_Routines:
x, die_roll = routine(x, die_roll)
if die_roll > x:
Count += 1
Probability_of_exceed = Count / Iterations
第二个更难回答。这种重构现在进行了大量的函数调用,而不是检查if
条件;所以missed branch predictions 可能会更少,但function call overhead 会更多。您必须对其进行基准测试(例如使用timeit library)才能确定。不过,至少这段代码应该更容易维护。
【讨论】:
如果你要让代码更 Pythonic,你应该使用 pep8 命名约定。也可能为可读性而解包元组,即代替state
x, die_roll = routine(x, die_roll)
。
确实如此;我选择保持名称不变,以防它与任何其他代码交互,因为命名与问题无关,但你是对的,遵循 Python 的命名约定会改进代码。
对不起,我应该把“be pythonic”条件排除在我的问题之外。我真的只想知道是否有更好的方法来构造代码以使其运行得更快。我添加了这个条件,因为我的第一个想法是写一些东西来编写另一个 python 脚本然后运行它,这听起来不太明显......以上是关于是否有一种 Pythonic 的方式来跳过 for 循环中的 if 语句以使我的代码运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章
是否有一种优雅的 Pythonic 方式来计算已处理的数据? [复制]
在 Pandas 中是不是有一种 pythonic 的方法来做一个列联表?