Python -O: When It Speeds Scripts—and When It Won’t
Python’s -O command-line flag runs code with optimization enabled, chiefly by removing assert statements and setting __debug__ to False. It can make a script a little faster when those features add meaningful work, but it usually does not make ordinary Python code noticeably faster—and it can change behavior if assertions are being used for essential checks.
What Python’s -O flag does
Run a script with the flag before its filename:
python -O script.py
On systems where the Python 3 command is named python3, use python3 -O script.py. The option affects that interpreter process and the code it runs; it is not a source-code rewrite or a general-purpose speed mode.
Python also accepts -OO. It applies the same optimization level as -O and additionally removes docstrings from code objects. That can reduce the memory used by docstrings, but programs that inspect or display them may behave differently.
How optimization changes execution
The main, dependable effect of -O is that Python removes assert statements during compilation. Consider:
assert total >= 0, "total must not be negative"
Without optimization, Python evaluates the condition and raises AssertionError if it is false. With -O, the assertion statement is omitted, so neither the condition nor its optional message is evaluated.
The flag also changes the built-in constant __debug__ from True to False. Code can test it, although explicit checks like if __debug__: are uncommon in everyday application code. Python’s compiler can remove code guarded by a constant-false debug condition.
For example, a developer might put diagnostic work behind if __debug__:. In an optimized run, that work can be skipped. However, logging controlled by a normal configuration setting, expensive calculations, network requests, and most ordinary function calls are not automatically eliminated just because -O is present.
When -O can speed a script
Optimization helps only when it avoids work that would otherwise take time. The clearest case is a program that executes a large number of assertions, especially if their conditions involve costly calculations. Removing those checks can reduce runtime, though it also removes their protection against invalid assumptions.
A program may also benefit if it deliberately places substantial diagnostic work inside if __debug__: blocks. With optimization, those blocks can be omitted. The speed difference depends on how frequently the code runs and how expensive the skipped work is.
- Likely to matter: a tight loop with many expensive assertions or debug-only calculations.
- Unlikely to matter: a script dominated by waiting for a network response, reading files, database work, or calls into libraries.
- Impossible to assume: any workload without measurement. A benchmark is needed to establish whether the flag changes total runtime.
Even in the favorable cases, -O is not a substitute for profiling. First identify the part of the program that consumes time, then benchmark representative runs both with and without the flag. Keep inputs, environment, and measurement method consistent, and repeat runs to reduce the influence of startup and system noise.
When -O will not help
For most scripts, assertions are sparse and debug-only code is absent. Removing a few checks saves little work, so the result may be too small to notice or may disappear within normal timing variation. The flag does not generally turn interpreted Python into compiled machine code, optimize every expression, or make a slow algorithm efficient.
If a script spends most of its time waiting on I/O, skipping a small amount of Python-level checking will not shorten that wait. Likewise, an inefficient algorithm remains inefficient: reducing the cost of a few statements does not change how much work the algorithm performs as input grows.
Do not expect -O to speed up a program simply because its name says “optimization.” The exact benefit is workload-dependent, and in many real programs there is no practically measurable improvement.
Assertions are not input validation
A common and important mistake is using assert to enforce rules that must hold in production. Assertions are intended mainly for detecting programming errors and checking internal assumptions during development. Since -O removes them, a command-line option, user input, file, or network request must not be validated only with an assertion.
For example, this is unsafe as essential validation:
assert user_age >= 0
Use an ordinary conditional and raise or handle an appropriate error instead:
if user_age < 0:
raise ValueError("user_age must not be negative")
That condition still runs with -O. In general, if the program must reject bad data, protect a resource, or preserve a required behavior, use normal control flow rather than an assertion.
Optimization levels and bytecode files
Python’s optimization setting is reflected in generated bytecode-cache filenames, which use an optimization tag such as .opt-1.pyc for -O and .opt-2.pyc for -OO. These files are caches; they do not make the original source permanently optimized. Running the script normally later uses the normal optimization setting.
The same behavior can be requested through the PYTHONOPTIMIZE environment variable. A value of 1 corresponds to -O; 2 corresponds to -OO. This is useful when a launch environment controls interpreter settings, but remember that it applies to Python processes started in that environment.
Optimization settings are not a replacement for choosing a suitable Python implementation, improving an algorithm, or using a faster library for a measured bottleneck. Those choices can have much larger effects, but they require their own compatibility and maintenance considerations.
How to decide whether to use it
For routine development, run without optimization so assertions remain active. That makes internal mistakes easier to catch. If you are considering -O for a deployed script, first confirm that no essential behavior relies on assertions or __debug__, then measure the actual workload.
- Search the codebase for
assertand__debug__. - Check whether any assertion performs work with side effects or validates external data. Such code needs correction regardless of the flag.
- Benchmark normal and optimized runs using representative inputs.
- Use the flag only if the improvement is repeatable and worth the changed behavior.
For -OO, also check whether the application or its dependencies rely on docstrings at runtime, for example through introspection or documentation-generation features. If that reliance is unclear, avoid the extra optimization level.
Frequently Asked Questions
Does Python -O make every script faster?
No. It removes assertions and can skip debug-only code, but most scripts have little such work. Any speed gain depends on the program and should be measured.
Does -O disable assert statements?
Yes. Python omits assertions when running with -O, including evaluation of their conditions. Do not use assertions for checks that must run in production.
What is the difference between -O and -OO?-O removes assertions and makes __debug__ false. -OO does the same and also removes docstrings from code objects, which may affect runtime introspection.
Can I enable optimization inside a Python script?
The optimization level is selected when the interpreter starts, through options such as -O or the PYTHONOPTIMIZE environment variable. It is not an ordinary runtime switch that restores removed assertions later.
Should I use -O in production?
Only if you have checked that required behavior does not depend on assertions or debug-only code and benchmarks show a useful benefit. Production input validation should use regular conditionals and error handling.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.