Python 3:检查迭代器的下一个值而不进行迭代

Posted

技术标签:

【中文标题】Python 3:检查迭代器的下一个值而不进行迭代【英文标题】:Python 3: Checking next value of an iterator without iterating 【发布时间】:2015-11-16 13:49:55 【问题描述】:

对于一个项目,如果下一行某列中的值等于当前行同一列中的值,我需要检查一个 csv 文件。我正在使用字典 csv 阅读器,即阅读器中的每一行都是一个字典文件。我可以通过使用列标题作为键来访问某一行的值:row[header] = value

我当前代码的精简版如下所示:

import csv

with open(os.path.abspath(path_to_file), "r") as f:
    reader = csv.DictReader(f, dialect='excel')

    for row in reader:
        current_row = row
        next_row = reader.__next__()
        if current_row[column] == next_row[column]:
            dosomething()

这里的问题当然是我使用__next__()跳过了下一次迭代,即

(1) 我进入循环;行 = 行 1 (2) current_row = row1, next_row = row2 (3) 我进入循环的下一次迭代; row = row3 因为我使用了__next__()。 current_row = row3, next_row = row4

在这个例子中,我永远不会检查row2 == row3

是否可以在不迭代迭代器对象的情况下检查下一行的值? 或者,是否有与__next__()相反的方法,使迭代器后退一步?

请注意:我将当前值与下一个进行比较,而不是当前值到 last 值,因为我不知道我正在阅读的文件有多长。我必须将文件的最后一行与其他文件不同,所以我必须检查reader.__next()__ 以查看是否有最后一行。

【问题讨论】:

我怎样才能读取带有csv.reader 的最后一行而不读取它之前的所有其他行?最后,我将不得不对迭代器的所有元素进行两次迭代。那,或者我误解了你。 'last'/'previous' 行是刚刚读取的行(在最后一个 next'ing 之前)。 【参考方案1】:

您的要求与内置迭代器的想法相冲突。所以我建议你将你的循环封装在自定义迭代器中。 想法是从原始迭代器中产生两个值,最后一行的下一个值为 None。

【讨论】:

【参考方案2】:

我不知道这是否可行,但它适用于 android

reader = csv.DictReader(f, dialect='excel')
  reader2 = csv.DictReader(f, dialect='excel')

  for row in reader:
    current_row = row

        for row2 in reader2
          next_row = reader2.__next__()
          if current_row[column] == next_row[column]:
          dosomething()
          continue

【讨论】:

【参考方案3】:

就个人而言,假设您的场景限制允许,我会回顾而不是向前看:

it = iter(reader)
prev_row = it.next()
while True:
    try:
        cur_row = it.next()
        if cur_row[column] == prev_row[column]:
            dosomething()
        prev_row = cur_row
    except StopIteration:
        break

【讨论】:

【参考方案4】:

试试itertools pairwise 食谱。更通用的解决方案是 tee 您的迭代器(这是成对配方使用的)。另一种可能性是创建一个具有 cur 和 next 变量并产生所需值的函数(基本上是 pairwise 所做的,但您可以使其产生 CSV 中的字段而不是整行)。

来自https://docs.python.org/2/library/itertools.html

def pairwise(iterable):
     "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

我认为pairwise 可以满足您的所有需求,因此无需对您自己的生成器函数或tee 大惊小怪。

reader = csv.DictReader(f, dialect='excel')

for current_row, next_row in pairwise(reader):
    if current_row[column] == next_row[column]:
        dosomething()

请意识到,如果您有一个包含 n 项的迭代,那么将有成对的 n-1 项。

【讨论】:

我不了解您的解决方案中的一些内容。首先,在 Python 3 中没有 itertools.izip 方法,我必须使用 itertools.zip 代替。不过,在这种情况下,这似乎不是问题。当current_row 是阅读器中的最后一项时,next(b, None) 应该为next_row 返回一个None 对象。如果我使用pairwise(reader) 遍历阅读器并在每个步骤中打印current_rownext_row,则循环在到达元组(倒数第二行,最后一行)时停止,但我需要在 for 循环中检查下一行是否是阅读器中的最后一行。 只需将 izip 替换为 map 我不知道地图在这里有什么用处。无论如何,我使用了建议的pairwise 函数,并进行了一些调整以检查循环的最后一行阅读器outside。现在我有一些重复的代码行,但一切都按预期工作。谢谢。【参考方案5】:

由于字典必须保留唯一键(因此不能附加具有相同键的行)并且csv.DictReader 对象不可下标(因此不能引用列或行号),请考虑将 csv 数据读入 列表然后将行与后续行进行比较:

import csv

with open(os.path.abspath(path_to_file), "r") as f:
    reader = csv.reader(f)

# APPEND READER LINES INTO LIST    
csvList = []
for row in reader:
    csvList.append(row)

# ITERATE THROUGH LIST, CHECK AGAINST NEXT ROW
for i in range(len(csvList) - 1):
    # FIND THE COLUMN NUMBER (BELOW USES 1)
    if (csvList[i][1] == csvList[i + 1][1]):
        doSomething()            

【讨论】:

【参考方案6】:
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

(sy,None) 或 (sy,"") 将是最后一个元组的唯一逻辑可能性,因为值从队列中一次弹出一个,直到迭代结束。

“一旦 tee() 进行了拆分,原始的 iterable 不应在其他任何地方使用;否则,在不通知 tee 对象的情况下,iterable 可能会前进。”

【讨论】:

以上是关于Python 3:检查迭代器的下一个值而不进行迭代的主要内容,如果未能解决你的问题,请参考以下文章

Python 常用迭代函数总结

3.4 迭代器

迭代器的注意事项

python迭代器与生成器

python基础教程

python基础学习笔记