Free Python reference · JavaScript timeline

Python 3.8: Assign inside expressions when it helps

The walrus operator can reuse a computed value, positional-only parameters clarify APIs, and f-strings can label debugging values.

Released · Editorial review October 25, 2026

Introduced in Python 3.8 · Syntax

The := walrus operator

An assignment expression computes a value, binds it to a name, and makes that value available to the surrounding expression. It is useful when a calculation is needed both to decide what to do and to perform the work. Ordinary = is an assignment statement; == compares values; := binds a name while producing a value.

Parentheses and precedence. In if (size := len(items)) > 2, size receives the length. Writing if size := len(items) > 2 instead assigns the comparison result, a boolean. Parentheses make the intended grouping explicit. A standalone assignment expression also needs parentheses, although ordinary assignment is usually clearer there. The target must be a simple name, not an attribute or subscript.

Truthiness is a separate choice. if (position := text.find("x")): is a bug: index zero is falsey, while the missing marker -1 is truthy. Compare the saved result with the actual success condition. Similarly, the loop below stops on the explicit None sentinel while accepting a numeric zero.

Comprehensions and scope. In the final example, clean is bound in the containing scope rather than being isolated like the comprehension's iteration variable. Do not rely on its last value after an empty comprehension: the assignment might never have happened. Choose an ordinary loop when that scope behavior makes the code harder to follow.

Use := when it removes a repeated operation or a bookkeeping step. If an assignment on its own line tells the story more clearly, keep it. Shorter code is not automatically easier code.

Earlier approach

size = len(items)
if size > 2:
    print(size)

Context fragment; read it with the example below.

Worked example

Minimum Python 3.8

items = ["a", "b", "c"]
if (size := len(items)) > 2:
    print(size)

text = "xray"
if (position := text.find("x")) != -1:
    print(position)

values = iter([0, 5, None])
while (value := next(values)) is not None:
    print(value)

names = [" Ada ", "   ", "Mina"]
cleaned = [clean for name in names if (clean := name.strip())]
print(cleaned)

Expected output

3
0
0
5
['Ada', 'Mina']

Example checked with CPython 3.14.8, CPython 3.15.0, Pyodide Python 3.13.2.

Common mistake

Do not confuse saving a value with testing its truthiness. In comprehensions, walrus bindings can outlive the comprehension; they cannot rebind its iteration variable.

Try it yourself

Change the comparison to if (size := len(items) > 2): and predict the type and value of size. Then rewrite the name-cleaning comprehension as an ordinary loop.

Read more: Python 3.8 release notes · PEP 572

Introduced in Python 3.8 · Syntax

Positional-only parameters with /

A slash in a function signature marks the preceding parameters as positional-only. Callers must supply those arguments by position. Parameters after / can still accept keywords unless another marker changes their calling convention.

The * marker introduces keyword-only parameters. Combining both markers lets an API distinguish its main input from named options. This can also let a library rename an internal parameter without breaking callers that would otherwise use that name.

The example permits render("status", suffix="!") but deliberately rejects render(text="status"). The exception is caught so you can see the failure without depending on an exact error message.

Worked example

Minimum Python 3.8

def render(text, /, *, suffix="."):
    return text + suffix

print(render("status", suffix="!"))
try:
    render(text="status")
except TypeError:
    print("text must be positional")

Expected output

status!
text must be positional

Example checked with CPython 3.14.8, CPython 3.15.0, Pyodide Python 3.13.2.

Common mistake

The slash is a signature marker, not an argument. Do not require positional-only arguments unless the API benefits from that restriction.

Try it yourself

Add a normal optional parameter between / and *. Which arguments can now be supplied either way?

Read more: Python 3.8 release notes · PEP 570

Introduced in Python 3.8 · Syntax

Self-labeling f-strings with =

Put = after an f-string expression to include its source text and value. This is useful for a quick local diagnostic when several values are printed together.

Without a format specification, the debug form uses a representation suitable for inspecting values, so strings include quotes. You can still add a numeric format such as .1f after the equals sign.

This is a display feature rather than assignment. Spaces in the expression can appear in the label, so keep the source spelling consistent when comparing expected output.

Worked example

Minimum Python 3.8

name = "Ada"
ratio = 2 / 3
print(f"{name=}")
print(f"{ratio=:.1f}")

Expected output

name='Ada'
ratio=0.7

Example checked with CPython 3.14.8, CPython 3.15.0, Pyodide Python 3.13.2.

Common mistake

Debug output may expose sensitive values. Use deliberate logging in an application and choose which fields to include.

Try it yourself

Print the length of name with the debug form. Predict the entire label as well as the numeric result.

Read more: Python 3.8 release notes

Recap

The walrus operator can reuse a computed value, positional-only parameters clarify APIs, and f-strings can label debugging values. Prefer the clearest form that works on your project's supported Python versions.

Read the full Python 3.8 release notes →