2

我正在尝试遍历一个列表,检查列表中的每个字符串是否有一个字符。

test = [str(i) for i in range(100)]

for i in test:
    if '0' or '4' or '6' or '8' in str(i):
        test.remove(i)

我认为这会很好,但是列表如下:

[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

'2'被删除但'41'不是?我注意到它只有偶数,但不知道为什么。

4

6 回答 6

11

您的代码有两个问题。第一个是您在迭代列表时正在修改列表。第二个是您or以错误的方式使用运算符 -if语句中的条件将始终为True.

这是一个固定版本:

test = [i for i in range(100) if set("0468").isdisjoint(str(i))]
于 2012-06-15T17:58:26.740 回答
5

你有两个问题。首先,这个if陈述总是被采纳。

if '0' or '4' or '6' or '8' in str(i):

因为字符串0是非零长度字符串,因此是True. 0或是40因此True。声明的其余部分无关紧要。

我希望您实际上想测试这些数字中的每一个是否都在整数的字符串表示形式中。正如您现在编写的那样,您只是在测试是否8在字符串中,甚至没有得到测试,因为表达式True在它到达那里之前就计算了。像这样的东西会起作用:

if any(x in i for x in '0468'):

顺便说一句,str(i)是多余的,因为您的列表已经是字符串列表。

另一个问题是您正在从正在迭代的列表中删除项目。所以,这就是发生的事情:

  • 第一项,0,被删除,因为你的if陈述总是被采纳。
  • 第二项 ,1成为第一项,因为您删除了0
  • 循环继续到第二for项,即 now 2

换句话说,因为你删除了01所以永远不会被测试。因此,所有其他项目(所有偶数)都被删除。

在代码中避免这种情况的最简单方法是遍历列表的副本:

for i in test[:]:
于 2012-06-15T18:04:05.730 回答
2

考虑一下:

(Pdb) i=2
(Pdb) i='2'
(Pdb) '0' or 'beer' in str(i)
'0'
(Pdb) bool('0')
True

你明白为什么'0' or '4' or '6' or '8' in str(i)不总是布尔值吗?

现在,考虑您要删除的内容:

>>> l=[str(i) for i in range(10)]
>>> for i in l:
...   print i
...   print l
...   l.remove(i)
... 
0
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
2
['1', '2', '3', '4', '5', '6', '7', '8', '9']
4
['1', '3', '4', '5', '6', '7', '8', '9']
6
['1', '3', '5', '6', '7', '8', '9']
8
['1', '3', '5', '7', '8', '9']

当您在迭代列表时就地删除元素时,您正在缩短列表并跳过每个下一个值。

Sven 有正确的 pythonic 解决方案,但我觉得更冗长的解释可能是值得的。

于 2012-06-15T18:06:31.080 回答
1

当您执行类似'0' or '4'的操作时,or运算符将验证第一个参数 ( '0') 是否为假,如果不是假,则返回第一个值。由于'0'is not false (它是一个非空字符串,True当用作布尔值时产生),它将返回第一个值,甚至不会考虑以下值:

>>> '0' or '4'
'0'

如果你重复它,你会得到相同的结果:

>>> '0' or '4' or '6' or '8'
'0'

此外,in运算符具有更高的优先级,因此它将在 each 之前执行or

'0' 或 '4' 或 '6' 或 '8' 在 '88' '0'

您想要在您的条件下做的是验证结果中是否有任何值:

>>> '0' in str(i) or '4' in str(i) or '6' in str(i) or '8' in str(i)
True
>>> i = 17
>>> '0' in str(i) or '4' in str(i) or '6' in str(i) or '8' in str(i)
False

这不是最优雅的解决方案,但它是您意图的最佳翻译。

那么,什么是优雅的解决方案?正如@sven 所建议的那样,您可以创建一组(更多关于它)寻找的字符:

>>> sought = set("0468")
>>> sought
set(['0', '8', '4', '6'])

然后在您的号码中创建一组数字:

>>> i = 16
>>> set(str(i))
set(['1', '6'])

现在,看看它们是否不相交

>>> i = 16
>>> sought.isdisjoint(set(str(i)))
False
>>> i = 17
>>> sought.isdisjoint(set(str(i)))
True

在这种情况下,如果集合不是不相交的那么您要保留它:

>>> found = []
>>> for i in range(100):
...     if sought.isdisjoint(set(str(i))):
...         found.append(i)
... 
>>> found
[1, 2, 3, 5, 7, 9, 11, 12, 13, 15, 17, 19, 21, 22, 23, 25, 27, 29, 31, 32, 33, 35, 37, 39, 51, 52, 53, 55, 57, 59, 71, 72, 73, 75, 77, 79, 91, 92, 93, 95, 97, 99]

大多数时候,每次你让自己创建一个for用于过滤迭代器的循环时,你真正想要的是一个列表推导

>>> [i for i in range(100) if sought.isdisjoint(set(str(i)))]
[1, 2, 3, 5, 7, 9, 11, 12, 13, 15, 17, 19, 21, 22, 23, 25, 27, 29, 31, 32, 33, 35, 37, 39, 51, 52, 53, 55, 57, 59, 71, 72, 73, 75, 77, 79, 91, 92, 93, 95, 97, 99]

或者,使用更笨拙但对新手更友好的构造:

>>> [i for i in range(100) if not ( '0' in str(i) or '4' in str(i) or '6' in str(i) or '8' in str(i) )]
[1, 2, 3, 5, 7, 9, 11, 12, 13, 15, 17, 19, 21, 22, 23, 25, 27, 29, 31, 32, 33, 35, 37, 39, 51, 52, 53, 55, 57, 59, 71, 72, 73, 75, 77, 79, 91, 92, 93, 95, 97, 99]
于 2012-06-15T18:15:07.067 回答
0

问题出在这行:

if '0' or '4' or '6' or '8' in str(i):

这是不正确的。最好这样写:

if any(x in i for x in '0468'):

第二个问题(如 Sven 所述)是您在迭代列表时修改了列表。

最好的解决方案是:

[i for i in test if not any(x in i for x in '0468')]
于 2012-06-15T18:00:04.983 回答
0
itms = ['0','4','6','8']
test = [str(i) for i in range(100) if not  any([x in str(i) for x in itms])]

也许....那是未经测试的,但我认为它会起作用

于 2012-06-15T18:00:22.267 回答