列表推导式的保护条件被忽略了。

4
假设我有一个 dict(字典):
d = {'AA': 'BB', 'BB': None}

还有这个 for comprehension:

[v for t in u'{}'.format(v.lower()) for k, v in d.items()]

很明显会失败,原因是'NoneType'对象没有'lower'属性,让我们试试这个:

[v for t in u'{}'.format(v.lower()) for k, v in d.items() if v is not None]

同样的事情发生了!为什么?如果我再增加一个守卫:
[v for t in u'{}'.format(v.lower()) if v is not None for k, v in d.items() if v is not None]

同样的事情。

为什么即使有保护条件,v.lower() 也会被调用?

然而,这个方法可以正常工作:

for k,v in d.items():
    if v is not None:
        [v for t in u'{}'.format(v.lower())]

更新

这是实际引起问题的代码。上面的代码是为了简化示例,但考虑到下面提供的答案,我想发表实际代码:

x = {'A': 'This is a Line to Be tokenized'}
for k,v in x.items():
    if v is not None:
        pat = [{'LOWER': str(t)} for t in tokenizer(u'{}'.format(v.lower()))]

这将生成Spacy的模式,格式如下:

[{'LOWER': 'this'},
 {'LOWER': 'is'},
 {'LOWER': 'a'},
 {'LOWER': 'line'},
 {'LOWER': 'to'},
 {'LOWER': 'be'},
 {'LOWER': 'tokenized'}]

所以,我原本用的for循环推导式生成那个输出是:

[{'LOWER': str(t)} for k, v in x.items() if v is not None for t in tokenizer(u'{}'.format(v))]

但是,如上所述,当字典的valueNone时,即使提供了guard,也会失败。

更新2

这里有更多例子:

x = {'A': 'This is a Line to Be tokenized', 'B': 'Hello'}
for k,v in x.items():
    if v is not None:
        pat = [{'LOWER': str(t)} for t in tokenizer(u'{}'.format(v.lower()))]
        print(pat)

# [{'LOWER': 'this'}, {'LOWER': 'is'}, {'LOWER': 'a'}, {'LOWER': 'line'}, {'LOWER': 'to'}, {'LOWER': 'be'}, {'LOWER': 'tokenized'}]
# [{'LOWER': 'hello'}]

基本上我想将那个循环转换为一个推导式。


在你的第一个例子中,for循环的顺序是错误的。 - yatu
[v.lower() if v is not None else v for k, v in d.items()] - Rakesh
[v.lower() if v else v for k, v in d.items()] 也不需要为每个字符调用 str.lower... 为什么不是整个字符串呢? - yatu
2个回答

3
您需要更改forif的顺序:
>>> [v for k, v in d.items() if v is not None for t in u'{}'.format(v.lower())]
['BB', 'BB']
>>> 
  • if语句必须放在它会引起错误的位置之前。

  • 第二个for循环必须放在包含第二个for循环所使用的迭代器的for循环之后。


我将要更新我的示例,因为这不是真实的示例,我需要for循环以具有该结构。 - Alejandro Alcalde

1
它可以写成以下形式:

x = {'A': 'This is a Line to Be tokenized', 'B':None}
for k,v in x.items():
    if v is not None:
        pat = [{'LOWER': str(t)} for t in tokenizer(u'{}'.format(v.lower()))]


res = [[{'LOWER': str(t)} for t in tokenizer(u'{}'.format(v.lower()))] for k, v in x.items() if v is not None]

输出:

[[{'LOWER': 'this'},
  {'LOWER': 'is'},
  {'LOWER': 'a'},
  {'LOWER': 'line'},
  {'LOWER': 'to'},
  {'LOWER': 'be'},
  {'LOWER': 'tokenized'}]]

然而,此时将其写成一行并没有太多好处,但在可读性方面会失去很多。我建议您不要将其写成一行。


把它写成一个推导式而不是一个for循环,对性能来说会更好,不是吗? - Alejandro Alcalde
1
只是勉强能实现,通常不足以引起注意。如果你在任何阶段放弃可读性,普通循环更可取。 - Paritosh Singh
好的,那我就按照你在回答中提到的方式来做。谢谢! - Alejandro Alcalde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接