Condition()がTrueを返すところで値をNoneに置き換えたいリストがあります。
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
たとえば、条件チェックがブール値(アイテム%2)の場合、次の値が返されます。
[None, 1, None, 3, None, 5, None, 7, None, 9, None]
これを行うための最も効率的な方法は何ですか?
リスト内包表記を使って新しいリストを作成します。
new_items = [x if x % 2 else None for x in items]
必要に応じて元のリストをその場で変更できますが、実際には時間を節約することはできません。
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for index, item in enumerate(items):
if not (item % 2):
items[index] = None
これが(Python 3.6.3)非タイムセーブを示すタイミングです。
In [1]: %%timeit
...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
...: for index, item in enumerate(items):
...: if not (item % 2):
...: items[index] = None
...:
1.06 µs ± 33.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [2]: %%timeit
...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
...: new_items = [x if x % 2 else None for x in items]
...:
891 ns ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
そしてPython 2.7.6のタイミング:
In [1]: %%timeit
...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
...: for index, item in enumerate(items):
...: if not (item % 2):
...: items[index] = None
...:
1000000 loops, best of 3: 1.27 µs per loop
In [2]: %%timeit
...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
...: new_items = [x if x % 2 else None for x in items]
...:
1000000 loops, best of 3: 1.14 µs per loop
ls = [x if (condition) else None for x in ls]
コメントでOPが尋ねた副次的な質問をリフティングする。
リストではなくrange(11)の値を返すジェネレータがあるとどうなりますか。ジェネレータの値を置き換えることは可能でしょうか。
もちろん、それは簡単なことです...:
def replaceiniter(it, predicate, replacement=None):
for item in it:
if predicate(item): yield replacement
else: yield item
最初の引数として(ジェネレータを呼び出した結果も含めて)反復可能なものを渡すだけで、2番目の引数として値を置き換える必要があるかどうかを判断する述語です。
例えば:
>>> list(replaceiniter(xrange(11), lambda x: x%2))
[0, None, 2, None, 4, None, 6, None, 8, None, 10]
これは別の方法です:
>>> L = range (11)
>>> map(lambda x: x if x%2 else None, L)
[None, 1, None, 3, None, 5, None, 7, None, 9, None]
>>> L = range (11)
>>> [ x if x%2 == 1 else None for x in L ]
[None, 1, None, 3, None, 5, None, 7, None, 9, None]
値を適切に置換する場合は、元のリスト全体をリスト内包からの値で更新して、元のリスト全体に割り当てることができます。
data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
id_before = id(data)
data[:] = [x if x % 2 else None for x in data]
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data) # check if list is still the same
# Out: True
元のリストを指す名前が複数ある場合、たとえば、リストを変更する前にdata2=data
を記述し、data
に割り当てるためのスライス表記をスキップした場合、data
はdata2
はまだ元の変更されていないリストを指していますが、新しく作成されたリスト。
data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
data2 = data
id_before = id(data)
data = [x if x % 2 else None for x in data] # no [:] here
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data) # check if list is still the same
# Out: False
data2
# Out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
注:これは、一般に一方を他方よりも優先する(リストをその場で変更するかどうかは問わない)ための推奨事項ではありませんが、注意すべき動作です。