Python WebAcademy Blog

リストに「あれば消す」discard()を足す提案、PEP 851が公開。remove()のValueErrorに悩んだ初心者向けに解説

|

2026年10月27日、Pythonのリストに、値があれば消し、なければ何もしないdiscard()を加える提案PEP 851が公開されました。対象はPython 3.16で、まだ採用は決まっていない草案の段階です。remove()がValueErrorを出すときの今の書き方と、その落とし穴を、実際に動かしたコードとともに整理します。

2026年10月27日、Pythonのリストにdiscard()という新しいメソッドを加える提案、PEP 851が公開されました。値がリストにあれば消し、なければ何もしない、というメソッドです。

リストから値を消そうとして、ValueErrorに出会ったことはありませんか。今は、その値が本当にあるかを先に確かめるか、エラーを受け止める書き方が必要です。

ただし、PEP 851はまだ草案の段階で、採用されるかどうかは決まっていません。今回は、提案の中身と、今の書き方に潜む落とし穴を、実際に動かしながら見ていきます。

まずはPEPとは何か

最初に、PEPという言葉を確認しておきましょう。PEPは、Python Enhancement Proposalの略で、Pythonに新しい機能を加えるための提案書です。

PEPは、草案として公開されたあと、議論を経て採用されたり、見送られたりします。PEP 851の状態は、現時点ではDraft、つまり草案です。

項目 PEP 851の内容
タイトル Add list.discard()
作者 Stan Ulbrych
状態 Draft(草案)
作成日 2026年10月27日
対象のバージョン Python 3.16

PEPの仕組みや読み方は、こちらで紹介しています。【関連記事】PythonのPEPとは?

今のremove()は、ないとエラーになる

まずは、今のリストの動きを確かめておきましょう。リストのremove()は、指定した値のうち最初の1つを消すメソッドです。

ところが、その値がリストにないと、ValueErrorになります。手元のPython 3.15.2で確かめた結果です。

fruits = ["apple", "banana", "apple"]
try:
    fruits.remove("melon")
except ValueError as e:
    print("ValueError:", e)
ValueError: list.remove(x): x not in list

一方で、集合のsetには、ないときは何もしないdiscard()が、前からあります。{"apple", "banana"}.discard("melon")を実行すると、エラーにならずにNoneが返ってきました。

リストのメソッドの基本は、こちらでまとめています。【関連記事】Pythonのリストのメソッドとは?

今のよくある2つの書き方

そのため今は、ないかもしれない値を消すときに、次のどちらかの書き方をするのが一般的です。PEP 851でも、この2つが例に挙げられています。

書き方 コード PEPが挙げる弱点
先に確かめる if x in lst:のあとでlst.remove(x) リストを2回探す。スレッドの間で値が消えるとエラーになる
エラーを受け止める try:でremove()し、except ValueError:で何もしない 4行かかり、意図が読みにくい。関係ないエラーも隠してしまう

PEPによると、ダウンロード数の多いPyPIの5,000個のプロジェクトを調べたところ、1つ目の書き方が411個のプロジェクトで1,062か所、2つ目が164個のプロジェクトで331か所見つかったそうです。

1つ目の書き方は、リストを2回探す

1つ目の書き方は、読みやすいのが長所です。ただ、inで探したあとに、remove()がもう一度最初から探します。

tasks = ["メール", "会議", "資料"]
if "会議" in tasks:   # 1回目: 探す
    tasks.remove("会議")   # 2回目: もう一度探して消す
print(tasks)

結果は['メール', '資料']です。短いリストなら気になりませんが、大きなリストでは無駄な仕事になります。

PEPには、複数のスレッドで同じリストを使っている場合、2つの行の間にほかのスレッドが値を消すと、ValueErrorになりうるとも書かれています。

2つ目の書き方は、関係ないエラーも隠す

2つ目の書き方には、もう少しわかりにくい落とし穴があります。except ValueErrorが、探している値がないこと以外のValueErrorまで飲み込んでしまうのです。

PEPでは、自分で定義した__eq__、つまり==で比べるときに呼ばれるメソッドが出したエラーも隠してしまう、と指摘されています。実際に、比べ方にバグのあるクラスで試してみました。

class Price:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        # 比べ方のバグ: 数値に変換できない値が来るとValueErrorになる
        return self.value == int(other.value)


prices = [Price(100), Price(200)]
try:
    prices.remove(Price("abc"))
except ValueError as e:
    print("見つからなかったことにした:", e)

実行した結果です。

見つからなかったことにした: invalid literal for int() with base 10: 'abc'

本当は、int("abc")という比べ方のバグでエラーになっていました。それなのに、見つからなかったことにして先へ進んでしまったわけです。

例外処理の基本については、こちらで詳しく紹介しています。【関連記事】Pythonのtry・except・else・finallyとは?

PEP 851が提案するdiscard()

では、PEP 851は何を提案しているのでしょうか。中心は、lst.discard(x)という1つのメソッドです。

決められている動き

PEPの仕様をまとめると、次のようになります。

項目 提案されている動き
値があるとき 最初の1つだけを消して、Trueを返す
値がないとき 何もせず、Falseを返す
比べている途中のエラー 隠さずに、そのまま例外として伝える
引数の渡し方 位置引数だけ。discard(value, /)
collections.UserList 同じメソッドを加える

3つ目が、先ほどの落とし穴への答えです。値がないことだけを戻り値で知らせ、__eq__が出したValueErrorなどは、そのまま例外として伝えると明記されています。

setのdiscard()との違い

名前は、setのdiscard()から取られています。ただし、setのdiscard()がNoneを返すのに対して、リストのdiscard()は、消したかどうかをTrueかFalseで返します。

PEPによると、これはGuido van Rossumさんの提案によるものです。戻り値があるので、if lst.discard(x):と書けば、消せたときだけ処理を続けることもできます。

setの使い方は、こちらで紹介しています。【関連記事】Pythonのsetとは?

同じ値が複数あるときは

PEPでは、同じ値が複数あるときの注意も書かれています。setでは、discard()のあとにx not in sが必ず成り立ちますが、リストでは成り立たないことがあります。

discard()は、remove()と同じく、最初の1つだけを消すからです。今のPythonで、提案と同じ動きをする関数を作って確かめてみました。

def discard(items, value):
    """PEP 851のlist.discard()と同じ動きをまねた関数"""
    try:
        items.remove(value)
    except ValueError:
        return False
    return True


fruits = ["apple", "banana", "apple"]
print(discard(fruits, "apple"), fruits)
print(discard(fruits, "melon"), fruits)
print("apple" in fruits)
True ['banana', 'apple']
False ['banana', 'apple']
True

1つ目のappleだけが消え、2つ目は残っています。なお、この関数は説明のためのもので、__eq__のエラーを隠してしまう弱点はそのままです。

見送られた案

PEPには、検討したうえで見送った案も書かれています。考え方の違いがわかって面白いので、表にまとめます。

見送られた案 見送った理由
remove(x, missing_ok=True)のような引数 setと同じ言葉づかいにそろえるほうがよい。メソッドのほうが読みやすい
MutableSequenceへの追加 ほかのライブラリの、すべてのサブクラスの形が変わってしまう
deque.discard()も同時に追加 提案を組み込みのlistだけにしぼるため。採用されたら続けて検討できる

pathlibのunlink(missing_ok=True)のように、引数で切り替える形も標準ライブラリにはあります。それでも、setという一番近い前例に合わせることを選んだ、と説明されています。

今のところ使えるのか

気になるのは、いつから使えるのかですよね。結論から言うと、今のPythonではまだ使えません。

手元で、Python 3.15.2と、10月13日に出た3.16の最初のアルファ版3.16.0a1で、hasattr(["a"], "discard")を確かめました。どちらもFalseで、リストにdiscardはまだありません。

CPythonのGitHubには、参考実装のプルリクエスト、python/cpython#160726が公開されています。ただし、PEPが採用されるまでは、本体に取り込まれることは決まっていません。

3.16のアルファ版については、こちらで紹介しています。【関連記事】Python 3.16の最初のアルファ版が公開

初心者が今からできること

PEP 851が採用されるかどうかにかかわらず、今回の話から学べることがあります。

場面 今のおすすめ
値が必ずあるはず remove()をそのまま使い、なければエラーで気づく
ないこともある、読みやすさ重視 if x in lst:で確かめてからremove()
ないこともある、エラーで受け止める tryの中はremove()の1行だけにする

私は10年ほどエンジニアとして開発に関わってきましたが、except ValueError: passが、比べ方のバグや型の間違いを何か月も隠していた現場を見たことがあります。エラーを受け止めるときは、tryの範囲をできるだけ狭くするのが基本です。

remove()とdiscard()を、必ずあるときとないかもしれないときで使い分けるのは、setではすでにおなじみの考え方です。リストでも同じ言葉で書けるようになるのか、今後の議論に注目しましょう。

まとめ

2026年10月27日、リストにdiscard()を加える提案、PEP 851が公開されました。値があれば最初の1つを消してTrueを、なければ何もせずFalseを返すメソッドで、Python 3.16を対象にしています。

まだ草案の段階なので、採用されるかどうかは今後の議論しだいです。今のうちは、remove()とValueErrorの付き合い方を見直すきっかけにしてみてください。

ここまでお読みいただきありがとうございました。

参考情報

次のアクション

記事で学んだ内容を実際に動かしてみよう

Python WebAcademyでは、ブラウザ上でコードを書きながら基礎から実践まで体系的に学べます。

Python WebAcademyの学習画面

あわせて読む

関連記事

ブログ一覧へ

Python学習ロードマップ

まずはこの3講座から

記事で気になったテーマを、順番に手を動かしながら学べます。

ロードマップを見る