是否有一种 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 语句不太可能导致您的代码运行缓慢。这更有可能是因为您正在执行许多顺序功能。如果您的迭代次数很高,那么可以为xdie_roll 的组合构建一个结果表,这样如果您的迭代具有与前一个相同的状态,而不是重新计算所有函数,只需查找您之前计算的结果(即记忆化)。 【参考方案1】:

您在这里问两件事 - 您希望您的代码更加 Pythonic,并且您希望它运行得更快。

第一个更容易回答:将Active_Routines 设为函数列表而不是字符串列表,然后从列表中调用函数。由于这些函数可能需要更改本地状态(xdie_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 命名约定。也可能为可读性而解包元组,即代替statex, die_roll = routine(x, die_roll) 确实如此;我选择保持名称不变,以防它与任何其他代码交互,因为命名与问题无关,但你是对的,遵循 Python 的命名约定会改进代码。 对不起,我应该把“be pythonic”条件排除在我的问题之外。我真的只想知道是否有更好的方法来构造代码以使其运行得更快。我添加了这个条件,因为我的第一个想法是写一些东西来编写另一个 python 脚本然后运行它,这听起来不太明显......

以上是关于是否有一种 Pythonic 的方式来跳过 for 循环中的 if 语句以使我的代码运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种优雅的 Pythonic 方式来计算已处理的数据? [复制]

在 Pandas 中是不是有一种 pythonic 的方法来做一个列联表?

Scrapy:: 如何在 CSS 选择器中使用“not”来跳过元素

一种灵活的智能合约协作方式

是否有一种有效的算法以既避免冲突又允许偏见的方式分配资源?

我无法执行“click()”来跳过屏幕