是的。立即,您应该使用 for 循环。递归通常不是 Pythonic 的。其次,使用内置工具:
from itertools import dropwhile
def remove_leading_items(l, item):
return list(dropwhile (lambda x: x == item, l))
或者
return list(dropwhile(item.__eq__, l))
编辑
出于好奇,我用不同的方法对这个问题做了一些实验:
from itertools import dropwhile
from functools import partial
from operator import eq
def _eq_drop(l, e):
return dropwhile(e.__eq__, l)
def lam_drop(l, e):
return dropwhile(lambda x:x==e, l)
def partial_drop(l, e):
return dropwhile(partial(eq, e), l)
首先,使用完全删除的列表:即 (1, 1, 1, ...)
In [64]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(_eq_drop(t0, 1))
...:
1000 loops, best of 3: 389 µs per loop
In [65]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(lam_drop(t0, 1))
...:
1000 loops, best of 3: 1.19 ms per loop
In [66]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(partial_drop(t0, 1))
...:
1000 loops, best of 3: 893 µs per loop
所以 __eq__
在这种情况下显然是最快的。我喜欢它,但它直接使用了 dunder 方法,这有时会让人不悦。 dropwhile(partial(eq...
方法(冗长但明确)介于两者之间,而迟缓、笨拙的 lambda
方法排在最后。不足为奇。
现在,当一半被丢弃时,即 (1, 1, 1, ..., 0, 0, 0)
:
In [52]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(_eq_drop(t2, 1))
...:
1000 loops, best of 3: 245 µs per loop
In [53]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(lam_drop(t2, 1))
...:
1000 loops, best of 3: 652 µs per loop
In [54]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(partial_drop(t2, 1))
...:
1000 loops, best of 3: 487 µs per loop
差异并不明显。
至于为什么我说递归不是 Pythonic,请考虑以下几点:
In [6]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: remove_leading_items(t0, 1)
...:
1 loop, best of 3: 405 ms per loop
In [7]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: remove_leading_items(t1, 1)
...:
10000 loops, best of 3: 34.7 µs per loop
In [8]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: remove_leading_items(t2, 1)
...:
1 loop, best of 3: 280 ms per loop
除了丢弃 0(嗯,1 项)的退化情况外,它在所有情况下的表现都非常糟糕。
一种快速、最不灵活的方法
现在,如果您知道自己总是想要一个列表,请考虑一种高度迭代的非常方法:
def for_loop(l, e):
it = iter(l)
for x in it:
if x != e:
break
else:
return []
return [x, *it]
它比使用内置函数执行得更好!
In [33]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: for_loop(t0, 1)
...:
1000 loops, best of 3: 270 µs per loop
In [34]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: for_loop(t1, 1)
...:
10000 loops, best of 3: 50.7 µs per loop
In [35]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: for_loop(t2, 1)
...:
10000 loops, best of 3: 160 µs per loop
速度较慢,但更灵活!
也许保持灵 active 的一个好的折衷方案是使用基于生成器的方法:
In [5]: def gen_drop(l, e):
...: it = iter(l)
...: for x in it:
...: if x != e:
...: break
...: yield x
...: yield from it
...:
In [6]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(gen_drop(t0, 1))
...:
1000 loops, best of 3: 287 µs per loop
In [7]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(gen_drop(t1, 1))
...:
1000 loops, best of 3: 359 µs per loop
In [8]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: list(gen_drop(t2, 1))
...:
1000 loops, best of 3: 324 µs per loop
使用双端队列
最后,deque
方法:
In [1]: from collections import deque
...:
...: def noLeadingZero(l, e):
...: d = deque(l)
...: for x in l:
...: if e == x:
...: d.popleft()
...: else:
...: break
...: return list(d)
...:
In [2]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: noLeadingZero(t0, 1)
...:
1000 loops, best of 3: 873 µs per loop
In [3]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: noLeadingZero(t1, 1)
...:
10000 loops, best of 3: 121 µs per loop
In [4]: %%timeit n = 10000; t0 = (1,)*n; t1 = (1,) + (0,)*(n-1); t2 = (1,)*(n//2) + (0,)*(n//2);
...: noLeadingZero(t2, 1)
...:
1000 loops, best of 3: 502 µs per loop