autopep8 vs Black: I Formatted the Same Messy File With Both
Black reformats your whole file into one fixed, opinionated style — it normalizes quotes to double, spaces every operator, explodes long collections, and guarantees the code's behavior is unchanged. autopep8 does the opposite: it makes only the minimum edits needed to satisfy PEP 8 (pycodestyle) and leaves the rest of your style alone. Choose Black for consistency across a team; choose autopep8 when you want small, reviewable diffs on an existing codebase.
"autopep8 vs Black" gets argued about a lot, usually in the abstract. So I stopped arguing and ran both — same messy file, default settings, current versions (Black 26.5.1 and autopep8 2.3.2 on pycodestyle 2.14.0) — and looked at exactly what each one changed.
The results make the difference obvious in about thirty seconds. Here's the file, both outputs, and the concrete rules that explain why they diverge.
The one-line difference
autopep8 is a linter's fixer; Black is a rewriter. autopep8 runs the same checks as pycodestyle (the PEP 8 checker) and edits only what's needed to make those warnings go away — so a file can pass and still keep most of your personal style. Black ignores your style entirely and re-emits the file in *its* single, non-configurable format. That's the whole story; everything below is a consequence of it.
The test: one messy file, both formatters
This is the input I gave both tools — deliberately sloppy: mixed quotes, no operator spacing, a cramped signature, a multi-import line:
import os, sys
x = {'name':'alice','role':'admin','tags':['a','b','c']}
def greet( name,greeting = 'hello' ):
msg=greeting+', '+name+'!'
return msgHere's what autopep8 did (default, no flags):
import os
import sys
x = {'name': 'alice', 'role': 'admin', 'tags': ['a', 'b', 'c']}
def greet(name, greeting='hello'):
msg = greeting+', '+name+'!'
return msgAnd here's Black on the same input:
import os, sys
x = {"name": "alice", "role": "admin", "tags": ["a", "b", "c"]}
def greet(name, greeting="hello"):
msg = greeting + ", " + name + "!"
return msgLook closely — they disagree on almost every line, and each disagreement is a real rule.
Five differences I actually saw
| Thing | autopep8 (default) | Black |
|---|---|---|
| Quotes | Left 'single' untouched | Normalized to "double" |
msg=a+b | Spaced = but not + (msg = a+b) | Spaced everything (a + b) |
import os, sys | Split into two lines | Left on one line |
| Long list | Wrapped compactly to fit width | Exploded one item per line + trailing comma |
| Default line length | 79 (PEP 8) | 88 |
The operator one surprised people I showed this to. autopep8 spaced the = but left greeting+', '+name alone — because missing whitespace around an *arithmetic* operator is check E226, which autopep8 ignores by default. It only fixes it if you opt in:
orig: z=a+b*c
autopep8 default: z = a+b*c
autopep8 -aa: z = a + b * c
black: z = a + b * cAnd the line-length default is easy to trip over. I fed both an 81-character line. autopep8 (max 79) wrapped it; Black (max 88) left it alone — same code, different result, purely because the defaults differ.
Black's list "explosion" is its magic trailing comma rule: once a collection is split, it puts every element on its own line and adds a trailing comma so future diffs stay one-line-per-change. autopep8 just wraps to fit the width.
Is it safe? Does the code still run?
Formatting should never change what your program *does*. I ran the original file and both formatted versions and compared the results — all three produced identical output:
orig: hello, world! admin
autopep8: hello, world! admin
black: hello, world! adminBoth preserved behavior here — but there's an important asymmetry. Black guarantees it: after formatting, it re-parses the output and checks the AST is equivalent to the input, and bails if it isn't. autopep8 in its default mode is safe too, but its --aggressive fixes (the ones that finally space your arithmetic operators, rewrite comparisons, etc.) are *not* guaranteed semantics-preserving and can, in edge cases, change meaning.
If you reach for autopep8 --aggressive --aggressive, run your test suite afterward. Default autopep8 and Black you can trust blindly; aggressive autopep8 you should verify.
So which should you use?
There's no universally "better" one — they're built for different goals:
- Use Black for new projects and teams. It's deterministic and non-configurable, so it ends every style argument and produces byte-identical output for everyone. It's the de-facto standard for new Python code, and editors/CI integrate it everywhere.
- Use autopep8 when you're improving an existing codebase and want *small, reviewable diffs*. It won't touch your quotes or re-explode your data structures — it just clears the PEP 8 warnings. Great for a gradual cleanup where a full Black reformat would bury real changes under thousands of style edits.
- Worth knowing:
ruff formatis a newer, Rust-based formatter that's essentially Black-compatible but dramatically faster, and it also lints. If you're starting fresh, it's worth a look alongside Black.
You don't have to install anything to try either one. Our free Python Formatter runs Black, autopep8, and YAPF in your browser — paste the same messy snippet, switch the engine, and watch the exact differences above happen live. If you'd rather lint *and* format with the fast modern option, the Ruff linter & formatter is there too.
Try Black and autopep8 in your browser — free
Paste a messy snippet and format it with Black, autopep8, or YAPF instantly. No install, nothing uploaded.
Open the Python FormatterFree tools mentioned here
Frequently asked questions
Is Black or autopep8 better?
Neither is universally better. Black reformats everything into one fixed, opinionated style and is the standard for new projects and teams. autopep8 makes only the minimal changes needed to satisfy PEP 8, which is better when you want small, reviewable diffs on an existing codebase.
Does Black follow PEP 8?
Mostly, with deliberate exceptions. Black is PEP 8-compliant in spirit but sets its own defaults — notably an 88-character line length instead of PEP 8's 79, and it normalizes all strings to double quotes. autopep8 sticks closer to literal PEP 8 (79 columns, quotes left alone).
Can autopep8 change my code behavior?
In default mode, no — it makes safe, whitespace-level fixes. But autopep8's --aggressive options are not guaranteed to preserve semantics and can change meaning in edge cases, so run your tests after using them. Black re-checks the AST after formatting and guarantees behavior is unchanged.
Why did autopep8 not add spaces around my + operator?
Because missing whitespace around an arithmetic operator is pycodestyle check E226, which autopep8 ignores by default. Enable it with --aggressive. Black always adds the spaces.
What is the default line length for Black vs autopep8?
Black defaults to 88 characters; autopep8 defaults to 79 (PEP 8's limit). So the same line can be wrapped by autopep8 but left untouched by Black.