Skip to main content
https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-comprehensions/main.yml.svg?branch=main&style=for-the-badge https://img.shields.io/pypi/v/flake8-comprehensions.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

A flake8 plugin that helps you write better list/set/dict comprehensions.


Linting a Django project? Check out my book Boost Your Django DX which covers Flake8 and many other code quality tools.


Requirements

Python 3.9 to 3.14 supported.

Installation

First, install with pip:

python -m pip install flake8-comprehensions

Second, if you define Flake8’s select setting, add the C4 prefix to it. Otherwise, the plugin should be active by default.

Rules

C400-402: Unnecessary generator - rewrite as a <list/set/dict> comprehension.

Rules:

  • C400 Unnecessary generator - rewrite as a list comprehension.

  • C401 Unnecessary generator - rewrite as a set comprehension.

  • C402 Unnecessary generator - rewrite as a dict comprehension.

It’s unnecessary to use list, set, or dict around a generator expression, since there are equivalent comprehensions for these types. For example:

  • Rewrite list(f(x) for x in foo) as [f(x) for x in foo]

  • Rewrite set(f(x) for x in foo) as {f(x) for x in foo}

  • Rewrite dict((x, f(x)) for x in foo) as {x: f(x) for x in foo}

C403-404: Unnecessary list comprehension - rewrite as a <set/dict> comprehension.

Rules:

  • C403 Unnecessary list comprehension - rewrite as a set comprehension.

  • C404 Unnecessary list comprehension - rewrite as a dict comprehension.

It’s unnecessary to use a list comprehension inside a call to set or dict, since there are equivalent comprehensions for these types. For example:

  • Rewrite set([f(x) for x in foo]) as {f(x) for x in foo}

  • Rewrite dict([(x, f(x)) for x in foo]) as {x: f(x) for x in foo}

C405-406: Unnecessary <list/tuple> literal - rewrite as a <set/dict> literal.

  • C405 Unnecessary <list/tuple> literal - rewrite as a set literal.

  • C406 Unnecessary <list/tuple> literal - rewrite as a dict literal.

It’s unnecessary to use a list or tuple literal within a call to set or dict. For example:

  • Rewrite set([1, 2]) as {1, 2}

  • Rewrite set((1, 2)) as {1, 2}

  • Rewrite set([]) as set()

  • Rewrite dict([(1, 2)]) as {1: 2}

  • Rewrite dict(((1, 2),)) as {1: 2}

  • Rewrite dict([]) as {}

C407: Unnecessary <dict/list> comprehension - <builtin> can take a generator

This rule was dropped in version 3.4.0, because it promoted an increase in laziness which could lead to bugs.

C408: Unnecessary <dict/list/tuple> call - rewrite as a literal.

It’s slower to call e.g. dict() than using the empty literal, because the name dict must be looked up in the global scope in case it has been rebound. Same for the other two basic types here. For example:

  • Rewrite dict() as {}

  • Rewrite dict(a=1, b=2) as {"a": 1, "b": 2}

  • Rewrite list() as []

  • Rewrite tuple() as ()

C409-410: Unnecessary <list/tuple> passed to <list/tuple>() - <advice>.

Rules:

  • C409 Unnecessary <list/tuple> passed to tuple() - <advice>.

  • C410 Unnecessary list passed to list() - <advice>.

Where <advice> is either:

  • remove the outer call to <list/tuple>()

  • rewrite as a <list/tuple> literal

It’s unnecessary to use a list or tuple literal within a call to list or tuple, since there is literal syntax for these types. For example:

  • Rewrite tuple([1, 2]) as (1, 2)

  • Rewrite tuple((1, 2)) as (1, 2)

  • Rewrite tuple([]) as ()

  • Rewrite list([1, 2]) as [1, 2]

  • Rewrite list((1, 2)) as [1, 2]

  • Rewrite list([]) as []

C411: Unnecessary list call - remove the outer call to list().

It’s unnecessary to use a list around a list comprehension, since it is equivalent without it. For example:

  • Rewrite list([f(x) for x in foo]) as [f(x) for x in foo]

C412: Unnecessary <dict/list/set> comprehension - ‘in’ can take a generator.

This rule was dropped in version 3.4.0, because it promoted an increase in laziness which could lead to bugs.

C413: Unnecessary <list/reversed> call around sorted().

It’s unnecessary to use list() around sorted() as it already returns a list. It is also unnecessary to use reversed() around sorted() as the latter has a reverse argument. For example:

  • Rewrite list(sorted([2, 3, 1])) as sorted([2, 3, 1])

  • Rewrite reversed(sorted([2, 3, 1])) as sorted([2, 3, 1], reverse=True)

  • Rewrite reversed(sorted([2, 3, 1], reverse=True)) as sorted([2, 3, 1])

C414: Unnecessary <list/reversed/set/sorted/tuple> call within <list/set/sorted/tuple>().

It’s unnecessary to double-cast or double-process iterables by wrapping the listed functions within list/set/sorted/tuple. For example:

  • Rewrite list(list(iterable)) as list(iterable)

  • Rewrite list(tuple(iterable)) as list(iterable)

  • Rewrite tuple(list(iterable)) as tuple(iterable)

  • Rewrite tuple(tuple(iterable)) as tuple(iterable)

  • Rewrite set(set(iterable)) as set(iterable)

  • Rewrite set(list(iterable)) as set(iterable)

  • Rewrite set(tuple(iterable)) as set(iterable)

  • Rewrite set(sorted(iterable)) as set(iterable)

  • Rewrite set(reversed(iterable)) as set(iterable)

  • Rewrite sorted(list(iterable)) as sorted(iterable)

  • Rewrite sorted(tuple(iterable)) as sorted(iterable)

  • Rewrite sorted(sorted(iterable)) as sorted(iterable)

  • Rewrite sorted(reversed(iterable)) as sorted(iterable)

C415: Unnecessary subscript reversal of iterable within <reversed/set/sorted>().

It’s unnecessary to reverse the order of an iterable when passing it into one of the listed functions will change the order again. For example:

  • Rewrite set(iterable[::-1]) as set(iterable)

  • Rewrite sorted(iterable)[::-1] as sorted(iterable, reverse=True)

  • Rewrite reversed(iterable[::-1]) as iterable

C416: Unnecessary <dict/list/set> comprehension - rewrite using <dict/list/set>().

It’s unnecessary to use a dict/list/set comprehension to build a data structure if the elements are unchanged. Wrap the iterable with dict(), list(), or set() instead. For example:

  • Rewrite {a: b for a, b in iterable} as dict(iterable)

  • Rewrite [x for x in iterable] as list(iterable)

  • Rewrite {x for x in iterable} as set(iterable)

C417: Unnecessary map usage - rewrite using a generator expression/<list/set/dict> comprehension.

map(func, iterable) has great performance when func is a built-in function, and it makes sense if your function already has a name. But if your func is a lambda, it’s faster to use a generator expression or a comprehension, as it avoids the function call overhead. For example:

  • Rewrite map(lambda x: x + 1, iterable) to (x + 1 for x in iterable)

  • Rewrite map(lambda item: get_id(item), items) to (get_id(item) for item in items)

  • Rewrite list(map(lambda num: num * 2, nums)) to [num * 2 for num in nums]

  • Rewrite set(map(lambda num: num % 2 == 0, nums)) to {num % 2 == 0 for num in nums}

  • Rewrite dict(map(lambda v: (v, v ** 2), values)) to {v : v ** 2 for v in values}

C418: Unnecessary <dict/dict comprehension> passed to dict() - remove the outer call to dict()

It’s unnecessary to use a dict around a dict literal or dict comprehension, since either syntax already constructs a dict. For example:

  • Rewrite dict({}) as {}

  • Rewrite dict({"a": 1}) as {"a": 1}

C419 Unnecessary list comprehension in <any/all>() prevents short-circuiting - rewrite as a generator.

Using a list comprehension inside a call to any()/all() prevents short-circuiting when a True / False value is found. The whole list will be constructed before calling any()/all(), potentially wasting work.part-way. Rewrite to use a generator expression, which can stop part way. For example:

  • Rewrite all([condition(x) for x in iterable]) as all(condition(x) for x in iterable)

  • Rewrite any([condition(x) for x in iterable]) as any(condition(x) for x in iterable)

C420: Unnecessary dict comprehension - rewrite using dict.fromkeys().

It’s unnecessary to use a dict comprehension to build a dict with all values set to the same constant. Use dict.fromkeys() instead, which is faster. For example:

  • Rewrite {x: 1 for x in iterable} as dict.fromkeys(iterable, 1)

  • Rewrite {x: None for x in iterable} as dict.fromkeys(iterable)

Release files for flake8-comprehensions 3.17.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flake8-comprehensions 3.17.0
File Size Uploaded
flake8_comprehensions-3.17.0.tar.gz 13.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flake8-comprehensions 3.17.0
File Interpreter ABI Platform
flake8_comprehensions-3.17.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.5 kB

Release files / flake8_comprehensions-3.17.0.tar.gz

Download URL flake8_comprehensions-3.17.0.tar.gz
Size 13.3 kB
Tags Source
SHA-256 checksum
How to use checksums
bf4fa102b2bf4d6c9e999e29e4b2724cbadffb70b937600fc782161be4dc0f4a
BLAKE2b-256 checksum
How to use checksums
9193da2d30987ce4c06404326e7c8e6636cae18c2b2f2d5542891a8ad64a34f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2025.

Transparency log

Release files / flake8_comprehensions-3.17.0-py3-none-any.whl

Download URL flake8_comprehensions-3.17.0-py3-none-any.whl
Size 8.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3943a9c6f2593c3bc5cc64106c2f89d63c6ecd49c8343597f8257b8fcfc8b0a2
BLAKE2b-256 checksum
How to use checksums
39bdd6739d685fdd79349aa51c37bdedc0d8eab6ae9c6e6ed2ca935b3f88210d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

3.17.0 This release

2 release files

3.16.0

2 release files

3.15.0

2 release files

3.14.0

2 release files

3.13.0

2 release files

3.12.0

2 release files

3.11.1

2 release files

3.11.0

2 release files

3.10.1

2 release files

3.10.0

2 release files

3.9.0

2 release files

3.8.0

2 release files

3.7.0

2 release files

3.6.1

2 release files

3.6.0

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.1

2 release files

3.3.0

2 release files

3.2.3

2 release files

3.2.2

2 release files

3.2.1

2 release files

3.2.0

2 release files

3.1.4

2 release files

3.1.3

2 release files

3.1.2

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page