Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 851 – Add list.discard()

PEP 851 – Add list.discard()

Author:
Stan Ulbrych <stan at python.org>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Created:
27-Oct-2026
Python-Version:
3.16
Post-History:
12-Oct-2025, 27-Oct-2026

Table of Contents

Abstract

Add list.discard(x), which removes the first item equal to x if one is present and does nothing otherwise, returning whether an item was removed. It is the non-raising counterpart to list.remove(), as set.discard() is to set.remove().

Motivation

Removing a value that may not be present is usually written as:

if x in lst:
    lst.remove(x)

or:

try:
    lst.remove(x)
except ValueError:
    pass

The first form is clear but scans the list twice on a hit, and if the list is shared between threads another thread can remove the item between the two calls, turning a no-op into a ValueError. The second avoids both problems but obscures the intent. It is four lines for one operation, pays for raising and catching an exception on every miss, and except ValueError is broad enough to hide unrelated errors from a user-defined __eq__().

Both are common. A search of the 5,000 most downloaded PyPI projects [1] finds 1,062 matches for the first form across 411 projects and 331 for the second across 164 projects. Some of these are on other container types; in the standard library, excluding tests, two of the 29 matches are on deques and the remaining seven and 20 are on lists, for example in logging, warnings, tkinter, runpy and asyncio. Each becomes a single call that states the intent directly:

lst.discard(x)

The cost of providing this is small, as discard() reuses the existing search and removal implementation of remove().

Specification

list gains a positional-only method discard(value, /). It removes the first item equal to value and returns True, or returns False if there is no such item. Only the absence of a matching item is reported by the return value; any other error, including a ValueError raised during equality comparison, propagates as an exception. The implementation shares the search loop of remove(), so which item is removed, how values are compared and the thread-safety properties are identical to remove(). collections.UserList gains the same method for consistency.

Rationale

The name follows the remove() / discard() distinction already made by set, where discard() is the variant that tolerates a missing item. It has been raised that after s.discard(x) the test x not in s holds, while after lst.discard(x) it need not if x appears more than once. This is a real difference. discard() deliberately shares the search and removal semantics of list.remove() rather than those of set.discard(), and the documentation for list.discard() in the reference implementation states this explicitly. The name remains the least surprising choice as users who know sets can apply that knowledge directly.

Unlike set.discard(), which returns None, a bool is returned. This costs nothing, since the implementation already knows the outcome, and lets if lst.discard(x): replace the double scan for callers who care.

Backwards Compatibility

list subclasses defining their own discard keep it. Code using hasattr(obj, "discard") to tell sets from lists would be affected. None was found in the standard library or the top 5,000 PyPI packages.

Security Implications

None.

How to Teach This

Introduce it in the tutorial directly after list.remove(), with remove() for when the item must be present and discard() for when it may not be. The same pairing already exists for set.

Reference Implementation

A reference implementation can be found in python/cpython#160726.

Rejected Ideas

A parameter on remove()

A separate method is preferred over a parameter such as remove(x, missing_ok=True). The standard library has precedent for both shapes, Path.unlink(missing_ok=True) and os.makedirs(exist_ok=True) on one side and set.remove() / set.discard() on the other, but the closest precedent is set, and following it means the two container types share one vocabulary. A method also reads more clearly at the call site, since lst.discard(x) says what happens while a flag describes what does not.

Adding discard() to collections.abc.MutableSequence

collections.abc.MutableSequence is left unchanged, as is the case for list.copy(). Adding discard() to the ABC would change the interface of every third-party subclass, while virtual subclasses registered with register() would not gain it at all.

deque.discard()

The original discussion also proposed collections.deque gaining the same method. It is left out of this PEP to keep the proposal to the builtin list. It can follow as a routine change if this PEP is accepted.

Footnotes

Acknowledgements

Thanks to Guido van Rossum for suggesting that discard() return whether an item was removed.