ユーニックス総合研究所

  • home
  • archives
  • python-list-clear-pop-remove-del

Pythonでリスト(配列)の要素を削除する方法【clear, pop, remove, del】

  • 作成日: 2023-12-28
  • 更新日: 2024-01-01
  • カテゴリ: Python

Pythonでリスト(配列)の要素を削除するにはどうしたらいいでしょうか?
その場合はリストのメソッドであるclear, pop, remove, そしてdel文を使います。
またリスト内包表記を使うことでフィルタリングをすることもできます。

この記事ではこれらの使い方について解説していきます。

5. データ構造 — Python 3.12.1 ドキュメント
GitHub - python/cpython: The Python programming language

clear ... リスト内の要素をクリアする

list.clear()はリストのメソッドで、リスト内の要素をすべて削除するメソッドです。
すべて削除することを「クリアする」と表現します。

l = [1, 2, 3]  
print(l)  # [1, 2, 3]  

l.clear()  
print(l)  # []  

このメソッドはpop()を使った場合は以下と同じです。

l = [1, 2, 3]  

while len(l):  
    l.pop(0)  

print(l)  # []  

またdel l[:]とも同じになります。

l = [1, 2, 3]  
print(l)  # [1, 2, 3]  

del l[:]  
print(l)  # []  

リスト内包表記を使った場合は以下と同じになります。

l = [1, 2, 3]  
print(l)  # [1, 2, 3]  

l = [el for el in l if False]  
print(l)  # []  

clear()のCPythonの実装

CPythonではリストのclear()cpython/Objects/clinic/listobject.c.hlist_clear()関数が登録されています。
list_clear()関数内ではcpython/Objects/listobject.clist_clear_impl()が呼び出されています。
list_clear_impl()内ではさらに_list_clear()関数が呼ばれており、clear()の詳細な実装はこの_list_clear()に実装されています。
_list_clear()int型の整数0を返します。この値はlist_clear_impl()内で無視されています。そしてlist_clear_impl()Noneを返します。
よってPythonのclear()が返す値はNoneです。

l = [1, 2, 3]  

print(l.clear())  # None  

pop ... 末尾か、添え字の要素をポップする

このメソッドはリストの特定の添え字の要素をポップ(取り出して削除)するメソッドです。
引数には添え字を指定します。

l = [1, 2, 3]  

print(l.pop(1))  # 2  
print(l)  # [1, 3]  

print(l.pop(0))  # 1  
print(l)  # [3]  

添え字が負数の場合は後方から参照されます。

l = [1, 2, 3]  

print(l.pop(-1))  # 3  
print(l)  # [1, 2]  

print(l.pop(-2))  # 1  
print(l)  # [2]  

添え字がリストのサイズより大きい場合はIndexError例外が送出されます。

l = [1, 2, 3]  

try:  
    print(l.pop(100))  
except IndexError as e:  
    print(e)  
    # pop index out of range  

添え字を指定していない場合はリストの末尾の要素がポップされます。
リストが空の状態でpop()した時はIndexError例外が送出されます。

l = [1, 2, 3]  

print(l.pop())  # 3  
print(l.pop())  # 2  
print(l.pop())  # 1  

try:  
    print(l.pop())  
except IndexError as e:  
    print(e)  
    # pop from empty list  

このメソッドはdel文の場合は以下のコードと同じです。

l = [1, 2, 3]  

del l[1:2]  # del 2  
print(l)  # [1, 3]  

del l[0:1]  # del 1  
print(l)  # [3]  

リスト内包表記を使う場合は以下のコードと同じです。

l = [1, 2, 3]  
l = [el for i, el in enumerate(l) if i != 1]  
print(l)  # [1, 3]  

pop()のCPythonの実装

このメソッドはcpython/Objects/clinic/listobject.c.hlist_pop()関数が登録されています。
list_pop()内ではインデックスを引数から取り出して、cpython/Objects/listobject.clist_pop_impl()に処理を任せています。

list_pop_impl()では実際に要素を削除する処理をしていますが、indexの添え字の値が負数の場合は単純にそのindexにリストのサイズを加算しているだけです。
つまり加算してもリストのサイズに収まらない場合、以下のようなコードはエラーになります。

l = [1, 2, 3]  
print(len(l))  # 3  

try:  
    l.pop(-6)  
except IndexError as e:  
    print(e)  
    # pop index out of range  

上記のコードではlは長さが3のリストです。pop()の添え字は-6なので-6 + 3で加算結果は-3になります。
-3はリストの添え字の範囲外になるのでエラーになります。

pop()の実装ですが、要素が取り出された後の残りの要素の配列内での移動はC言語のmemmove()関数を使っています。

関連記事
C言語の配列を簡単にコピーする方法

リストの実装は配列になってますが、この配列の複数の要素をmemmove()でずりっとずらしているだけです。単純な実装ですね。
pop()はリストのリサイズに失敗した場合はポップ前のリストの状態を復元するようになってます。
リストのリサイズはlist_resize()関数で行っていますが、内部ではPyMem_Realloc()関数を使っています。
この関数はC言語のrealloc()関数を呼び出す関数で、realloc()はすでに確保されている動的メモリを伸縮する関数です。
C言語の動的配列の実装ではrealloc()で配列を伸縮することがよく行われます。

関連記事
C言語の動的配列のリサイズ方法

remove ... 引数の値の要素を削除する

remove()は引数の値を持つリスト内の要素を削除するメソッドです。
要素が見つからなかった場合はValueErrorを送出します。

l = [10, 20, 30, 20]  

print(l.remove(20))  # None  
print(l)  # [10, 30]  

try:  
    l.remove(100)  
except ValueError as e:  
    print(e)  
    # list.remove(x): x not in list  

リストの先頭から走査して最初に見つけた値の要素を削除しています。
特定の値の要素をすべて削除したい場合はリスト内包表記を使って以下のようなコードを書きます。

l = [10, 20, 30, 20]  
l = [el for el in l if el != 20]  
print(l)  # [10, 30]  

あるいはfilter()を使っても以下のように書けます。

l = [10, 20, 30, 20]  
l = list(filter(lambda x: x != 20, l))  
print(l)  # [10, 30]  

removeのCPythonの実装

remove()のCPythonでの実装はcpython/Objects/listobject.clist_remove()関数が登録されています。
この関数内ではlist_ass_slice()というリストのスライスの処理で使われる関数が流用されています。
リストのremove()とリストのスライス処理は友達ということになります。

この関数内ではfor文でリストの要素を先頭から参照していますが、カウント変数の型はPy_ssize_t型になってます。
この型はCPytohn/PC/pyconfig.hで定義されていて、プラットフォームごとに中身が変わります。
Windows64ビットの場合は__int64になっており、これはMicrosoft C/C++が独自拡張した予約語で64ビット長の符号付き整数を表しています。
この型のバイト数は8バイトで、値の範囲は最小値-9,223,372,036,854,775,808から 最大値9,223,372,036,854,775,807になります。

参考
データ型の範囲 | Microsoft Learn

Windows64ビット以外ではint型になっています。
int型のサイズは処理系によって変わりますが一般的な処理系では4バイトであることが多いみたいです。
値の範囲は最小値-2147483648から最大値2147483647、になります。

del ... 特定の要素を削除する

リストの特定の要素を削除するのにdel文が使えます。
del文はオブジェクトを削除する文です。

l = [1, 2, 3]  

del l[0]  
print(l)  # [2, 3]  

del l[1]  
print(l)  # [2]  

del文の対象をスライスにすると、特定の範囲の要素をまとめて削除できます。

l = [1, 2, 3, 4, 5]  

del l[1:4]  
print(l)  # [1, 5]  

全ての要素を削除するには以下のようにコードを書きます。

l = [1, 2, 3]  

del l[:]  
print(l)  # []  

del文のCPythonの実装

del文はcpython/Python/compile.ccompiler_visit_stmt()関数で実装されています。
Pythonでは文ごとに定数が振られていてdel文にはDelete_kindという定数が振られています。
C言語のswitch文で分岐してDelete_kindだった場合はVISIT_SEQ()というマクロ関数を呼び出しています。
このマクロ関数は内部で識別子をプリプロセッサ時に連結し、compiler_visit_で始まる関数を呼び分けています。

リスト内包表記 ... if文でフィルタリングする

リスト内包表記を使うと特定の要素を残して残りを削除することができます。
たとえば特定の要素を残したい場合は以下のようなコードを書きます。

l = [1, 2, 3, 2]  
l = [el for el in l if el == 2]  # 2だけ残す  
print(l)  # [2, 2]  

特定の要素以外を残したい場合は以下のようなコードを書きます。

l = [1, 2, 3, 2]  
l = [el for el in l if el != 2]  # 2以外を残す  
print(l)  # [1, 3]  

このようなフィルタリング処理はfilter()を使っても行えます。
先ほどのコードは以下のコードと同じです。

l = [1, 2, 3, 2]  
l = list(filter(lambda el: el == 2, l))  # 2だけ残す  
print(l)  # [2, 2]  

l = [1, 2, 3, 2]  
l = list(filter(lambda el: el != 2, l))  # 2以外を残す  
print(l)  # [1, 3]