如何遍历 dic 和 list

How to iterate over dic and list

我有一个包含一堆 .SQL 文件的文件夹,我希望这些文件与 ALL_TAB_COLUMNS 的提取相匹配(只有 TABLE_NAMECOLUMN_NAME 列在column_list.csv)。

我的 objective 是为每个报告使用 tables/columns 的关系,生成如下输出:
sql_file_name.sql | table_name | column_name

似乎当我从第二个嵌套循环移动到第三个嵌套循环时,变量得到 'out of reach',Python (2.7.13) 将它们视为新的:

import os
import csv

path = "./search_files"
folder = os.listdir(path)

search_dict = csv.DictReader(open("column_list.csv"))

for files in folder:
    files = os.path.join(path, files)
    file = open(files, "r")
    sql = file.readlines()

    sql = [x.strip() for x in sql] # Remove \n

    hit_count = 0

    for i in sql:
        i = i.upper() # Make SQL code UPPERCASE
        print str(hit_count) + ": " + i # i has values here
        hit_count = hit_count + 1

        for row in search_dict:
            print str(hit_count) + ": " + i # i is blank here
            hit_count = hit_count + 1

            if row['TABLE_NAME'] in i or row['COLUMN_NAME'] in i:
                print row['TABLE_NAME'] + " | " + row['COLUMN_NAME']

您的问题是 search_dict 是一个迭代器,您正试图对其进行多次迭代。那是行不通的。第一次到达输入文件末尾后,迭代器将永远为空。

有几种方法可以解决这个问题。最简单的可能是将迭代器中的值转储到列表中:

search_dict = list(csv.DictReader(open("column_list.csv")))

这种方法唯一真正的缺点是,如果您的 CSV 文件很大,它可能会占用大量内存。

或者,您可以 "cheat" 使用迭代器协议。通常引发了 StopIteration 异常的迭代器应该永远持续下去。但是文件是迭代器,可以让你作弊。如果你 seek 文件回到开头,你可以再次遍历它的内容。这 可能 让您继续迭代其他迭代器,这些迭代器从您回绕的文件中获取输入。无法保证这将始终有效。它适用于 csv.readercsv.DictReader,但不适用于纯 Python.

编写的生成器函数

所以这是您可以调整代码的一种方法:

search_file = open("column_list.csv")        # keep a reference to this file for later
search_dict = csv.DictReader(search_file)

for files in folder:
    #...
    for i in sql:
        #...

        search_file.seek(0)                  # ensure we're at the start of the file
        for row in search_dict:
            #...

另一种方法是在每次启动内部循环时简单地重新打开文件。

另一个与循环问题无关的建议:关闭文件时您应该更加小心。如果您使用 with 语句,这在 Python 中很容易做到。不要做类似 var = open(filename) 的事情,而是使用 with open(filename) as var: 并将使用该文件的所有代码缩进到它下面的一个块中。当您退出缩进块时,该文件将自动关闭(即使您因异常退出)。我上面的示例保留了您当前打开文件的风格,但如果我为自己的代码编写它们,我会写:

with open("column_list.csv") as search_file:
    search_dict = list(csv.DictReader(search file))

# rest of the code is not indented extra

或者对于 seek(0) 版本:

with open("column_list.csv") as search_file:   # the whole code is indented under this block
    search_dict = csv.DictReader(search_file)

    for files in folder:    # files is a really misleading variable name
        files = os.path.join(path, files)
        with open(files, "r") as file:
            sql = file.readlines()  # only a single line needs to be indented extra this time

        #...

最后一个建议:使用更好的变量名。这个问题的部分混乱是由于名称 search_dict。这个名字听起来像是它应该包含一个字典。但这并不是代码中的实际情况(它实际上是一个 csv.DictReader 实例,您将其用作迭代器)。同样,您的 files 变量(由最外层的 for 循环创建)也具有误导性。它包含一个文件名,而不是几个文件(复数名称暗示)。