python 和具体的迭代 "for line in first_names"

python and iteration specifically "for line in first_names"

如果我 'pop' 从 python 中的数组中取出一个项目,我似乎会因为弄乱数组的总长度而搬起石头砸自己的脚?请参阅以下示例: 我只是个白痴还是这是正常行为?有没有更好的方法来实现我想要做的事情?

first_names = []
last_names = []
approved_names = []
blacklisted_names = []
loopcounter = 0

with open("first_names.txt") as file:
    first_names = file.readlines()
    #first_names = [line.rstrip() for line in first_names]

for line in first_names:
    line = line.strip("\r\n")
    line = line.strip("\n")
    line = line.strip(" ")
    if line == "":
        first_names.pop(loopcounter)
        #first_names.pop(first_names.index("")) # Does not work as expected
        #loopcounter -= 1 # Does not work as expected either......
    loopcounter += 1
        
loopcounter = 0


def save_names():
    with open("first_names2.txt",'wt',encoding="utf-8") as file:
        file.writelines(first_names)

和生成的文件:

first_names.txt 
{

Abbey
Abbie
Abbott
Abby


Abe
Abie
Abstinence
Acton
}

和输出文件

{
Abbey
Abbie
Abbott


Abe
Abie
Abstinence
Acton
}

一般来说,如您在问题中所述,改变您正在迭代的列表不是一个好主意。如果您从列表中弹出一个元素,您不一定会弄乱数组的长度,但在处理要弹出的索引时可能会遇到意想不到的行为。在这种情况下,您可以跳过数组的某些元素。

一个快速的解决方案是复制列表并使用内置的 enumerate() 方法,如下所示:

copy = first_names.copy()
for i, line in enumerate(copy):
    line = line.strip("\r\n")
    line = line.strip("\n")
    line = line.strip(" ")
    if line == "":
        first_names.remove(i)

更多关于 enumerate() here

list.pop() 从列表中删除一个项目和 returns 值(参见 this ref)。对于清理和编写名称列表这一非常基本的任务,一个简单的编辑是:

with open("first_names.txt") as file:
    first_names = file.readlines()

cleaned_lines = []
for line in first_names:
    clean_l = line.strip("\r\n").strip("\n").strip(" ")
    if clean_l != "":
        cleaned_lines.append(clean_l)

with open("first_names2.txt",'wt',encoding="utf-8") as file:
    file.writelines(cleaned_lines)

如果您不想创建列表的干净副本 first_names,您也可以迭代地将单行附加到文件中。

with open("first_names.txt") as file:
    first_names = file.readlines()

with open("first_names2.txt",'wt',encoding="utf-8") as file:
    for line in first_names:
        clean_l = line.strip("\r\n").strip("\n").strip(" ")
        if clean_l != "":
            file.writelines([clean_l, ])

通常的做法是过滤或创建一个新列表,而不是更改您正在迭代的列表。创建一个包含所需更改的新列表,然后将其重新分配回原始变量名的情况并不少见。这是一个列表理解。请注意过滤掉不需要的空行的 if 语句。

first_names = [name.strip() for name in first_names if name.strip()]

https://docs.python.org/3/glossary.html#term-list-comprehension

您可以使用 map 对迭代器执行相同的操作,将函数应用于列表中的每个项目,并过滤以删除空行。

first_names_iterator = filter(lambda x: bool(x), map(lambda x: x.strip(), first_names))
first_names = list(first_names_iterator)

https://docs.python.org/3/library/functions.html#map
https://docs.python.org/3/library/functions.html#filter

最后一行表明您可以直接将迭代器传递给列表的构造函数来获取列表,但迭代器更好。您可以遍历它们而不必一次拥有整个列表。如果你想要一个列表,你应该使用列表理解。

lambda 表示法只是一种编写函数的快速方法。我本可以定义一个具有好名字的函数,但对于 map、filter 或排序键之类的东西来说,这通常是矫枉过正的。

完整代码:

test_cases = [
    'Abbey',
    '  Abbie  ',
    '',
    'Acton',
]

print(test_cases)
first_names = list(test_cases)
first_names = [name.strip() for name in first_names if name.strip()]
print(first_names)

first_names = list(test_cases)
for name in filter(lambda x: bool(x),
                   map(lambda x: x.strip(),
                       first_names)):
    print(name)