max([x for x in something]) vs max(x for x in something):为什么有区别,区别是什么?

max([x for x in something]) vs max(x for x in something): why is there a difference and what is it?

我正在为 class 开发一个项目,我的代码没有产生与参考代码相同的结果。

我将我的代码与参考代码逐行比较,它们看起来几乎完全一样。一切似乎在逻辑上都是等价的。最终我开始更换线路并进行测试,直到找到重要的线路。

原来是这样的(编辑:确切的代码在下面):

# my version:
max_q = max([x for x in self.getQValues(state)])

# reference version which worked:
max_q = max(x for x in self.getQValues(state))

现在,这让我感到困惑。我尝试了一些使用 Python (2.7) 解释器的实验,运行 测试使用 max 对列表推导式(带和不带方括号)进行测试。结果似乎完全一样。

即使通过 PyCharm 进行调试,我也找不到我的版本没有产生与参考版本完全相同的结果的原因。到目前为止,我认为我对列表理解的工作方式(以及 max() 函数的工作方式)有很好的处理,但现在我不太确定,因为这是一个奇怪的差异。

这是怎么回事?为什么我的代码产生的结果与参考代码(在 2.7 中)不同?传递不带括号的理解与传递带括号的理解有何不同?

编辑 2:确切的代码是这样的:

# works
max_q = max(self.getQValue(nextState, action) for action in legal_actions)

# doesn't work (i.e., provides different results)
max_q = max([self.getQValue(nextState, action) for action in legal_actions])

我不认为这应该被标记为重复——是的,另一个问题是关于理解对象和列表对象之间的区别,但不是为什么 max() 在给定 [= 时会提供不同的结果31=],而不是单独的 'X comprehension'。

在列表理解周围使用 [] 实际上会生成一个列表到您的变量中,或者在本例中生成到您的 max 函数中。如果没有括号,您将创建一个 generator 对象,该对象将被送入 max 函数。

results1 = (x for x in range(10))
results2 = [x for x in range(10)]
result3 = max(x for x in range(10))
result4 = max([x for x in range(10)])
print(type(results1)) # <class 'generator'>
print(type(results2)) # <class 'list'>
print(result3) # 9
print(result4) # 9

据我所知,它们在 max 函数中的工作原理应该基本相同。

我不知道为什么你的项目中有不同的值,但我可以给你一个活生生的例子,当它发生时。生成器比列表更有效,所以我们会有不同的内存使用。我正在使用 Python 3.

returns 当前内存使用量 Python 的函数:

import os
import psutil


def memory_usage():
    """Get process virtual memory (vms) usage in MB."""
    process = psutil.Process(os.getpid())
    memory = process.memory_info()[1] / (1024.0 * 1024.0)
    return memory

试试这个代码:

# Generator version:
max_q = max(memory_usage() for i in range(100000))
print(max_q)  # 7.03125

我测试了几次代码,我在我的机器上得到了一些超过 7 的东西。

用列表版本替换生成器版本:

# List version:
max_q = max([memory_usage() for i in range(100000)])
print(max_q)  # 11.44921875

我的机器上 11 有点问题。

如你所见,代码几乎相同,但你会得到不同的输出。

可能在您的项目中,getQValue() 会根据一些已计算的值为您提供不同的值。但是,如果您使用生成器,垃圾收集器可以更快地删除现有值。

您是否泄漏了影响后续代码的局部变量?

# works
action = 'something important'
max_q = max(self.getQValue(nextState, action) for action in legal_actions)
assert action == 'something important'

# doesn't work (i.e., provides different results)
max_q = max([self.getQValue(nextState, action) for action in legal_actions])
assert action == 'something important'  # fails!

生成器和字典理解创建一个新的范围,但在 py3 之前,列表理解没有,为了向后兼容

简单的测试方法 - 将您的代码更改为:

max_q = max([self.getQValue(nextState, action) for action in legal_actions])
max_q = max(self.getQValue(nextState, action) for action in legal_actions)

假设 self.getQValue 是纯的,那么第一行唯一持久的副作用就是弄乱了局部变量。如果这打破了它,那么这就是你的问题的原因。