这个有条件删除的更好的pythonic版本是什么?

时间:2016-02-16 15:09:59

标签: python indexing

我正在刷新我的python(2.7),我发现了迭代器和生成器。 据我所知,它们是一种有效的方法,可以在不消耗太多内存的情况下导航值。 所以下面的代码在列表上做了某种逻辑索引: 删除触发由函数f。

表示的False条件语句的列表L的值

我对我的代码不满意,因为我觉得这段代码不是最佳的,原因有三个:

  • 我在某处读到,使用for循环比使用while循环更好。 但是,在通常的for i in range(10)中,我无法修改'i'的值,因为迭代似乎并不关心。

  • 逻辑索引在面向矩阵的语言中非常强大,并且应该有一种方法可以在python中做同样的事情(手动授权,但可能比我的代码更好)。

  • 第三个原因就是我想在这个例子中使用generator / iterator来帮助我理解。

第三个原因就是我想在这个例子中使用generator / iterator来帮助我理解。

TL; DR:这段代码是一种很好的pythonic方式来进行逻辑索引吗?

#f string -> bool
def f(s):
    return 'c' in s

L=['','a','ab','abc','abcd','abcde','abde']  #example
length=len(L)
i=0
while i < length:
    if not f(L[i]): #f is a conditional statement (input string output bool)
        del L[i]
        length-=1 #cut and push leftwise

    else:
        i+=1
    print 'Updated list is :', L
    print length

5 个答案:

答案 0 :(得分:3)

此代码存在一些问题,但主要问题是您必须永远不要修改正在迭代的列表。而是从符合条件的元素创建新列表。这可以简单地在for循环中完成:

newlist = []
for item in L:
    if f(item):
        newlist.append(item)

可以缩短为简单的列表理解:

newlist = [item for item in L if f(item)]

答案 1 :(得分:1)

看起来filter()就是您所追求的目标:

android/app/build.gradle

filter()过滤器(...)是一个可迭代的,只保留谓词返回newlist = filter(lambda x: not f(x), L) 的项目。在您的情况下,True不是谓词,而是f(..)

简单:

not f(...)

请参阅:https://docs.python.org/2/library/functions.html#filter

答案 2 :(得分:0)

“python方式”是使用生成器表达式:

# list comprehension
L = [l for l in L if f(l)]
# alternative generator comprehension
L = (l for l in L if f(l))

如果列表或生成器“更好”,则取决于您的上下文(请参阅例如this so question)。因为您的源数据来自列表,所以在这里使用生成器没有任何实际好处。

答案 3 :(得分:0)

永远不要使用delpop或其他方法修改列表,以便在迭代时改变列表的长度。请阅读this以获取更多信息。

过滤列表的“pythonic”方法是使用重新分配以及列表推导或内置filter函数:

列表理解:

>>> [item for item in L if f(item)]
['abc', 'abcd', 'abcde']
  

我想在这个例子中使用generator / iterator来帮助我理解

for item in L部分 隐式使用迭代器协议。 Python列表是可迭代的,iter(somelist)返回迭代器。

>>> from collections import Iterable, Iterator
>>> isinstance([], Iterable)
True
>>> isinstance([], Iterator)
False
>>> isinstance(iter([]), Iterator)
True

__iter__不仅在使用传统的for循环时被调用,而且当你使用列表理解时也是如此:

>>> class mylist(list):
...     def __iter__(self):
...         print('iter has been called')
...         return super(mylist, self).__iter__()
... 
>>> m = mylist([1,2,3])
>>> [x for x in m]
iter has been called
[1, 2, 3]

过滤

>>> filter(f, L)
['abc', 'abcd', 'abcde']

在Python3中,使用list(filter(f, L))获取列表。

当然,要过滤列表,Python也需要迭代它:

>>> filter(None, mylist())
iter has been called
[]

答案 4 :(得分:0)

对于简单删除元素,特别是如果不再需要原始列表,只需向后迭代:

Python 2.x

for i in xrange(len(L) - 1, -1, -1):
    if not f(L[i]):
        del L[i]

Python 3.x

for i in range(len(L) - 1, -1, -1):
    if not f(L[i]):
        del L[i]

通过从末尾开始迭代,“next”索引在删除后不会改变,并且for循环是可能的。请注意,您应该使用Python 2中的xrange生成器或Python 3中的range生成器来节省内存*。

如果您必须向前迭代,请使用上面给出的解决方案。

*请注意,如果有&gt; = 2 ** 32 - 1个元素,Python 2的xrange将会中断。 Python 3的range以及效率较低的Python 2 range没有这个限制。