您可以使用grouper recipe,izip(*[iterator]*2)
将线条df
分成 2 组。然后,要找到最小的线条对,使用min
及其key
参数来指定用于比较的代理。在这种情况下,对于每一对行,(p, l)
我们希望使用第二行的浮点数float(l)
,作为代理:
import itertools as IT
with open('filepath') as df:
previous, minline = min(IT.izip(*[df]*2),
key=lambda (p, l): float(l))
minline = float(minline)
print(previous)
print(minline)
印刷
Over
0.5678
石斑鱼食谱的解释:
要了解石斑鱼配方,首先看看如果df
是一个列表会发生什么:
In [1]: df = [1, 2]
In [2]: [df]*2
Out[2]: [[1, 2], [1, 2]]
在 Python 中,当您将列表乘以一个正整数时n
,您将获得n
列表中项目的(浅)副本。因此,[df]*2
制作一个包含两个副本的列表df
。
现在考虑zip(*[df]*2)
*
使用的 in具有zip(*...)
特殊的含义。它告诉 Python 解压缩*
要传递给的 into 参数之后的列表zip
。因此,zip(*[df]*2)
完全等价于zip(df, df)
:
In [3]: zip(df, df)
Out[3]: [(1, 1), (2, 2)]
In [4]: zip(*[df]*2)
Out[4]: [(1, 1), (2, 2)]
SaltyCrane 在这里给出了参数解包的更完整的解释。
记下正在做什么zip
。
zip(*[df]*2)
剥离两个副本的第一个元素(在这种情况下都是 1),并形成元组 (1,1)。然后它剥离两个副本的第二个元素(两个 2),并形成元组 (2,2)。它返回一个包含这些元组的列表。
现在考虑当df
是迭代器时会发生什么。迭代器有点像列表,除了迭代器只适用于单次传递。当项目被拉出迭代器时,迭代器永远不能倒带。
例如,文件句柄是一个迭代器。假设我们有一个带有行的文件
1
2
3
4
In [8]: f = open('data')
f
您可以通过调用将项目从迭代器中拉出next(f)
:
In [9]: next(f)
Out[9]: '1\n'
In [10]: next(f)
Out[10]: '2\n'
In [11]: next(f)
Out[11]: '3\n'
In [12]: next(f)
Out[12]: '4\n'
每次调用时next(f)
,我们都会从文件句柄中获取下一行f
. 如果我们next(f)
再次调用,我们会得到一个 StopIteration 异常,表明迭代器是空的。
现在让我们看看石斑鱼食谱的表现如何f
:
In [14]: f = open('data') # Notice we have to open the file again, since the old iterator is empty
In [15]: [f]*2
Out[15]:
[<open file 'data', mode 'r' at 0xa028f98>,
<open file 'data', mode 'r' at 0xa028f98>]
[f]*2
给我们一个列表,其中包含同一个迭代器的两个相同副本f
。
In [16]: zip(*[f]*2)
Out[16]: [('1\n', '2\n'), ('3\n', '4\n')]
zip(*[f]*2)
从第一个迭代器中f
剥离第一项,然后从第二个迭代器中剥离第一项f
。但是迭代器f
两次都是一样的!而且由于迭代器适合单次通过(您永远无法返回),因此每次剥离一个项目时都会得到不同的项目。每次zip
都打电话来剥一个项目。next(f)
所以第一个元组是
('1\n', '2\n')
. 同样,zip
然后从第一个迭代器中剥离下一项,f
从第二个迭代器中剥离下一项f
,并形成元组('3\n', '4\n')
。因此,zip(*[f]*2)
返回
[('1\n', '2\n'), ('3\n', '4\n')]
。
这就是石斑鱼食谱的全部内容。上面,我选择使用IT.izip
而不是zip
这样 Python 会返回一个迭代器而不是一个元组列表。如果文件中有很多行,这将节省大量内存。zip
和之间的区别在这里IT.izip
得到更全面的解释。