Python Enhancement Proposals

  • Python »
  • PEP Index »

PEP 572 – Assignment Expressions

The importance of real code, exceptional cases, scope of the target, relative precedence of :=, change to evaluation order, differences between assignment expressions and assignment statements, specification changes during implementation, _pydecimal.py, datetime.py, sysconfig.py, simplifying list comprehensions, capturing condition values, changing the scope rules for comprehensions, alternative spellings, special-casing conditional statements, special-casing comprehensions, lowering operator precedence, allowing commas to the right, always requiring parentheses, why not just turn existing assignment into an expression, with assignment expressions, why bother with assignment statements, why not use a sublocal scope and prevent namespace pollution, style guide recommendations, acknowledgements, a numeric example, appendix b: rough code translations for comprehensions, appendix c: no changes to scope semantics.

This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr .

As part of this change, there is also an update to dictionary comprehension evaluation order to ensure key expressions are executed before value expressions (allowing the key to be bound to a name and then re-used as part of calculating the corresponding value).

During discussion of this PEP, the operator became informally known as “the walrus operator”. The construct’s formal name is “Assignment Expressions” (as per the PEP title), but they may also be referred to as “Named Expressions” (e.g. the CPython reference implementation uses that name internally).

Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.

Additionally, naming sub-parts of a large expression can assist an interactive debugger, providing useful display hooks and partial results. Without a way to capture sub-expressions inline, this would require refactoring of the original code; with assignment expressions, this merely requires the insertion of a few name := markers. Removing the need to refactor reduces the likelihood that the code be inadvertently changed as part of debugging (a common cause of Heisenbugs), and is easier to dictate to another programmer.

During the development of this PEP many people (supporters and critics both) have had a tendency to focus on toy examples on the one hand, and on overly complex examples on the other.

The danger of toy examples is twofold: they are often too abstract to make anyone go “ooh, that’s compelling”, and they are easily refuted with “I would never write it that way anyway”.

The danger of overly complex examples is that they provide a convenient strawman for critics of the proposal to shoot down (“that’s obfuscated”).

Yet there is some use for both extremely simple and extremely complex examples: they are helpful to clarify the intended semantics. Therefore, there will be some of each below.

However, in order to be compelling , examples should be rooted in real code, i.e. code that was written without any thought of this PEP, as part of a useful application, however large or small. Tim Peters has been extremely helpful by going over his own personal code repository and picking examples of code he had written that (in his view) would have been clearer if rewritten with (sparing) use of assignment expressions. His conclusion: the current proposal would have allowed a modest but clear improvement in quite a few bits of code.

Another use of real code is to observe indirectly how much value programmers place on compactness. Guido van Rossum searched through a Dropbox code base and discovered some evidence that programmers value writing fewer lines over shorter lines.

Case in point: Guido found several examples where a programmer repeated a subexpression, slowing down the program, in order to save one line of code, e.g. instead of writing:

they would write:

Another example illustrates that programmers sometimes do more work to save an extra level of indentation:

This code tries to match pattern2 even if pattern1 has a match (in which case the match on pattern2 is never used). The more efficient rewrite would have been:

Syntax and semantics

In most contexts where arbitrary Python expressions can be used, a named expression can appear. This is of the form NAME := expr where expr is any valid Python expression other than an unparenthesized tuple, and NAME is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value:

There are a few places where assignment expressions are not allowed, in order to avoid ambiguities or user confusion:

This rule is included to simplify the choice for the user between an assignment statement and an assignment expression – there is no syntactic position where both are valid.

Again, this rule is included to avoid two visually similar ways of saying the same thing.

This rule is included to disallow excessively confusing code, and because parsing keyword arguments is complex enough already.

This rule is included to discourage side effects in a position whose exact semantics are already confusing to many users (cf. the common style recommendation against mutable default values), and also to echo the similar prohibition in calls (the previous bullet).

The reasoning here is similar to the two previous cases; this ungrouped assortment of symbols and operators composed of : and = is hard to read correctly.

This allows lambda to always bind less tightly than := ; having a name binding at the top level inside a lambda function is unlikely to be of value, as there is no way to make use of it. In cases where the name will be used more than once, the expression is likely to need parenthesizing anyway, so this prohibition will rarely affect code.

This shows that what looks like an assignment operator in an f-string is not always an assignment operator. The f-string parser uses : to indicate formatting options. To preserve backwards compatibility, assignment operator usage inside of f-strings must be parenthesized. As noted above, this usage of the assignment operator is not recommended.

An assignment expression does not introduce a new scope. In most cases the scope in which the target will be bound is self-explanatory: it is the current scope. If this scope contains a nonlocal or global declaration for the target, the assignment expression honors that. A lambda (being an explicit, if anonymous, function definition) counts as a scope for this purpose.

There is one special case: an assignment expression occurring in a list, set or dict comprehension or in a generator expression (below collectively referred to as “comprehensions”) binds the target in the containing scope, honoring a nonlocal or global declaration for the target in that scope, if one exists. For the purpose of this rule the containing scope of a nested comprehension is the scope that contains the outermost comprehension. A lambda counts as a containing scope.

The motivation for this special case is twofold. First, it allows us to conveniently capture a “witness” for an any() expression, or a counterexample for all() , for example:

Second, it allows a compact way of updating mutable state from a comprehension, for example:

However, an assignment expression target name cannot be the same as a for -target name appearing in any comprehension containing the assignment expression. The latter names are local to the comprehension in which they appear, so it would be contradictory for a contained use of the same name to refer to the scope containing the outermost comprehension instead.

For example, [i := i+1 for i in range(5)] is invalid: the for i part establishes that i is local to the comprehension, but the i := part insists that i is not local to the comprehension. The same reason makes these examples invalid too:

While it’s technically possible to assign consistent semantics to these cases, it’s difficult to determine whether those semantics actually make sense in the absence of real use cases. Accordingly, the reference implementation [1] will ensure that such cases raise SyntaxError , rather than executing with implementation defined behaviour.

This restriction applies even if the assignment expression is never executed:

For the comprehension body (the part before the first “for” keyword) and the filter expression (the part after “if” and before any nested “for”), this restriction applies solely to target names that are also used as iteration variables in the comprehension. Lambda expressions appearing in these positions introduce a new explicit function scope, and hence may use assignment expressions with no additional restrictions.

Due to design constraints in the reference implementation (the symbol table analyser cannot easily detect when names are re-used between the leftmost comprehension iterable expression and the rest of the comprehension), named expressions are disallowed entirely as part of comprehension iterable expressions (the part after each “in”, and before any subsequent “if” or “for” keyword):

A further exception applies when an assignment expression occurs in a comprehension whose containing scope is a class scope. If the rules above were to result in the target being assigned in that class’s scope, the assignment expression is expressly invalid. This case also raises SyntaxError :

(The reason for the latter exception is the implicit function scope created for comprehensions – there is currently no runtime mechanism for a function to refer to a variable in the containing class scope, and we do not want to add such a mechanism. If this issue ever gets resolved this special case may be removed from the specification of assignment expressions. Note that the problem already exists for using a variable defined in the class scope from a comprehension.)

See Appendix B for some examples of how the rules for targets in comprehensions translate to equivalent code.

The := operator groups more tightly than a comma in all syntactic positions where it is legal, but less tightly than all other operators, including or , and , not , and conditional expressions ( A if C else B ). As follows from section “Exceptional cases” above, it is never allowed at the same level as = . In case a different grouping is desired, parentheses should be used.

The := operator may be used directly in a positional function call argument; however it is invalid directly in a keyword argument.

Some examples to clarify what’s technically valid or invalid:

Most of the “valid” examples above are not recommended, since human readers of Python source code who are quickly glancing at some code may miss the distinction. But simple cases are not objectionable:

This PEP recommends always putting spaces around := , similar to PEP 8 ’s recommendation for = when used for assignment, whereas the latter disallows spaces around = used for keyword arguments.)

In order to have precisely defined semantics, the proposal requires evaluation order to be well-defined. This is technically not a new requirement, as function calls may already have side effects. Python already has a rule that subexpressions are generally evaluated from left to right. However, assignment expressions make these side effects more visible, and we propose a single change to the current evaluation order:

  • In a dict comprehension {X: Y for ...} , Y is currently evaluated before X . We propose to change this so that X is evaluated before Y . (In a dict display like {X: Y} this is already the case, and also in dict((X, Y) for ...) which should clearly be equivalent to the dict comprehension.)

Most importantly, since := is an expression, it can be used in contexts where statements are illegal, including lambda functions and comprehensions.

Conversely, assignment expressions don’t support the advanced features found in assignment statements:

  • Multiple targets are not directly supported: x = y = z = 0 # Equivalent: (z := (y := (x := 0)))
  • Single assignment targets other than a single NAME are not supported: # No equivalent a [ i ] = x self . rest = []
  • Priority around commas is different: x = 1 , 2 # Sets x to (1, 2) ( x := 1 , 2 ) # Sets x to 1
  • Iterable packing and unpacking (both regular or extended forms) are not supported: # Equivalent needs extra parentheses loc = x , y # Use (loc := (x, y)) info = name , phone , * rest # Use (info := (name, phone, *rest)) # No equivalent px , py , pz = position name , phone , email , * other_info = contact
  • Inline type annotations are not supported: # Closest equivalent is "p: Optional[int]" as a separate declaration p : Optional [ int ] = None
  • Augmented assignment is not supported: total += tax # Equivalent: (total := total + tax)

The following changes have been made based on implementation experience and additional review after the PEP was first accepted and before Python 3.8 was released:

  • for consistency with other similar exceptions, and to avoid locking in an exception name that is not necessarily going to improve clarity for end users, the originally proposed TargetScopeError subclass of SyntaxError was dropped in favour of just raising SyntaxError directly. [3]
  • due to a limitation in CPython’s symbol table analysis process, the reference implementation raises SyntaxError for all uses of named expressions inside comprehension iterable expressions, rather than only raising them when the named expression target conflicts with one of the iteration variables in the comprehension. This could be revisited given sufficiently compelling examples, but the extra complexity needed to implement the more selective restriction doesn’t seem worthwhile for purely hypothetical use cases.

Examples from the Python standard library

env_base is only used on these lines, putting its assignment on the if moves it as the “header” of the block.

  • Current: env_base = os . environ . get ( "PYTHONUSERBASE" , None ) if env_base : return env_base
  • Improved: if env_base := os . environ . get ( "PYTHONUSERBASE" , None ): return env_base

Avoid nested if and remove one indentation level.

  • Current: if self . _is_special : ans = self . _check_nans ( context = context ) if ans : return ans
  • Improved: if self . _is_special and ( ans := self . _check_nans ( context = context )): return ans

Code looks more regular and avoid multiple nested if. (See Appendix A for the origin of this example.)

  • Current: reductor = dispatch_table . get ( cls ) if reductor : rv = reductor ( x ) else : reductor = getattr ( x , "__reduce_ex__" , None ) if reductor : rv = reductor ( 4 ) else : reductor = getattr ( x , "__reduce__" , None ) if reductor : rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )
  • Improved: if reductor := dispatch_table . get ( cls ): rv = reductor ( x ) elif reductor := getattr ( x , "__reduce_ex__" , None ): rv = reductor ( 4 ) elif reductor := getattr ( x , "__reduce__" , None ): rv = reductor () else : raise Error ( "un(deep)copyable object of type %s " % cls )

tz is only used for s += tz , moving its assignment inside the if helps to show its scope.

  • Current: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) tz = self . _tzstr () if tz : s += tz return s
  • Improved: s = _format_time ( self . _hour , self . _minute , self . _second , self . _microsecond , timespec ) if tz := self . _tzstr (): s += tz return s

Calling fp.readline() in the while condition and calling .match() on the if lines make the code more compact without making it harder to understand.

  • Current: while True : line = fp . readline () if not line : break m = define_rx . match ( line ) if m : n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v else : m = undef_rx . match ( line ) if m : vars [ m . group ( 1 )] = 0
  • Improved: while line := fp . readline (): if m := define_rx . match ( line ): n , v = m . group ( 1 , 2 ) try : v = int ( v ) except ValueError : pass vars [ n ] = v elif m := undef_rx . match ( line ): vars [ m . group ( 1 )] = 0

A list comprehension can map and filter efficiently by capturing the condition:

Similarly, a subexpression can be reused within the main expression, by giving it a name on first use:

Note that in both cases the variable y is bound in the containing scope (i.e. at the same level as results or stuff ).

Assignment expressions can be used to good effect in the header of an if or while statement:

Particularly with the while loop, this can remove the need to have an infinite loop, an assignment, and a condition. It also creates a smooth parallel between a loop which simply uses a function call as its condition, and one which uses that as its condition but also uses the actual value.

An example from the low-level UNIX world:

Rejected alternative proposals

Proposals broadly similar to this one have come up frequently on python-ideas. Below are a number of alternative syntaxes, some of them specific to comprehensions, which have been rejected in favour of the one given above.

A previous version of this PEP proposed subtle changes to the scope rules for comprehensions, to make them more usable in class scope and to unify the scope of the “outermost iterable” and the rest of the comprehension. However, this part of the proposal would have caused backwards incompatibilities, and has been withdrawn so the PEP can focus on assignment expressions.

Broadly the same semantics as the current proposal, but spelled differently.

Since EXPR as NAME already has meaning in import , except and with statements (with different semantics), this would create unnecessary confusion or require special-casing (e.g. to forbid assignment within the headers of these statements).

(Note that with EXPR as VAR does not simply assign the value of EXPR to VAR – it calls EXPR.__enter__() and assigns the result of that to VAR .)

Additional reasons to prefer := over this spelling include:

  • In if f(x) as y the assignment target doesn’t jump out at you – it just reads like if f x blah blah and it is too similar visually to if f(x) and y .
  • import foo as bar
  • except Exc as var
  • with ctxmgr() as var

To the contrary, the assignment expression does not belong to the if or while that starts the line, and we intentionally allow assignment expressions in other contexts as well.

  • NAME = EXPR
  • if NAME := EXPR

reinforces the visual recognition of assignment expressions.

This syntax is inspired by languages such as R and Haskell, and some programmable calculators. (Note that a left-facing arrow y <- f(x) is not possible in Python, as it would be interpreted as less-than and unary minus.) This syntax has a slight advantage over ‘as’ in that it does not conflict with with , except and import , but otherwise is equivalent. But it is entirely unrelated to Python’s other use of -> (function return type annotations), and compared to := (which dates back to Algol-58) it has a much weaker tradition.

This has the advantage that leaked usage can be readily detected, removing some forms of syntactic ambiguity. However, this would be the only place in Python where a variable’s scope is encoded into its name, making refactoring harder.

Execution order is inverted (the indented body is performed first, followed by the “header”). This requires a new keyword, unless an existing keyword is repurposed (most likely with: ). See PEP 3150 for prior discussion on this subject (with the proposed keyword being given: ).

This syntax has fewer conflicts than as does (conflicting only with the raise Exc from Exc notation), but is otherwise comparable to it. Instead of paralleling with expr as target: (which can be useful but can also be confusing), this has no parallels, but is evocative.

One of the most popular use-cases is if and while statements. Instead of a more general solution, this proposal enhances the syntax of these two statements to add a means of capturing the compared value:

This works beautifully if and ONLY if the desired condition is based on the truthiness of the captured value. It is thus effective for specific use-cases (regex matches, socket reads that return '' when done), and completely useless in more complicated cases (e.g. where the condition is f(x) < 0 and you want to capture the value of f(x) ). It also has no benefit to list comprehensions.

Advantages: No syntactic ambiguities. Disadvantages: Answers only a fraction of possible use-cases, even in if / while statements.

Another common use-case is comprehensions (list/set/dict, and genexps). As above, proposals have been made for comprehension-specific solutions.

This brings the subexpression to a location in between the ‘for’ loop and the expression. It introduces an additional language keyword, which creates conflicts. Of the three, where reads the most cleanly, but also has the greatest potential for conflict (e.g. SQLAlchemy and numpy have where methods, as does tkinter.dnd.Icon in the standard library).

As above, but reusing the with keyword. Doesn’t read too badly, and needs no additional language keyword. Is restricted to comprehensions, though, and cannot as easily be transformed into “longhand” for-loop syntax. Has the C problem that an equals sign in an expression can now create a name binding, rather than performing a comparison. Would raise the question of why “with NAME = EXPR:” cannot be used as a statement on its own.

As per option 2, but using as rather than an equals sign. Aligns syntactically with other uses of as for name binding, but a simple transformation to for-loop longhand would create drastically different semantics; the meaning of with inside a comprehension would be completely different from the meaning as a stand-alone statement, while retaining identical syntax.

Regardless of the spelling chosen, this introduces a stark difference between comprehensions and the equivalent unrolled long-hand form of the loop. It is no longer possible to unwrap the loop into statement form without reworking any name bindings. The only keyword that can be repurposed to this task is with , thus giving it sneakily different semantics in a comprehension than in a statement; alternatively, a new keyword is needed, with all the costs therein.

There are two logical precedences for the := operator. Either it should bind as loosely as possible, as does statement-assignment; or it should bind more tightly than comparison operators. Placing its precedence between the comparison and arithmetic operators (to be precise: just lower than bitwise OR) allows most uses inside while and if conditions to be spelled without parentheses, as it is most likely that you wish to capture the value of something, then perform a comparison on it:

Once find() returns -1, the loop terminates. If := binds as loosely as = does, this would capture the result of the comparison (generally either True or False ), which is less useful.

While this behaviour would be convenient in many situations, it is also harder to explain than “the := operator behaves just like the assignment statement”, and as such, the precedence for := has been made as close as possible to that of = (with the exception that it binds tighter than comma).

Some critics have claimed that the assignment expressions should allow unparenthesized tuples on the right, so that these two would be equivalent:

(With the current version of the proposal, the latter would be equivalent to ((point := x), y) .)

However, adopting this stance would logically lead to the conclusion that when used in a function call, assignment expressions also bind less tight than comma, so we’d have the following confusing equivalence:

The less confusing option is to make := bind more tightly than comma.

It’s been proposed to just always require parentheses around an assignment expression. This would resolve many ambiguities, and indeed parentheses will frequently be needed to extract the desired subexpression. But in the following cases the extra parentheses feel redundant:

Frequently Raised Objections

C and its derivatives define the = operator as an expression, rather than a statement as is Python’s way. This allows assignments in more contexts, including contexts where comparisons are more common. The syntactic similarity between if (x == y) and if (x = y) belies their drastically different semantics. Thus this proposal uses := to clarify the distinction.

The two forms have different flexibilities. The := operator can be used inside a larger expression; the = statement can be augmented to += and its friends, can be chained, and can assign to attributes and subscripts.

Previous revisions of this proposal involved sublocal scope (restricted to a single statement), preventing name leakage and namespace pollution. While a definite advantage in a number of situations, this increases complexity in many others, and the costs are not justified by the benefits. In the interests of language simplicity, the name bindings created here are exactly equivalent to any other name bindings, including that usage at class or module scope will create externally-visible names. This is no different from for loops or other constructs, and can be solved the same way: del the name once it is no longer needed, or prefix it with an underscore.

(The author wishes to thank Guido van Rossum and Christoph Groth for their suggestions to move the proposal in this direction. [2] )

As expression assignments can sometimes be used equivalently to statement assignments, the question of which should be preferred will arise. For the benefit of style guides such as PEP 8 , two recommendations are suggested.

  • If either assignment statements or assignment expressions can be used, prefer statements; they are a clear declaration of intent.
  • If using assignment expressions would lead to ambiguity about execution order, restructure it to use statements instead.

The authors wish to thank Alyssa Coghlan and Steven D’Aprano for their considerable contributions to this proposal, and members of the core-mentorship mailing list for assistance with implementation.

Appendix A: Tim Peters’s findings

Here’s a brief essay Tim Peters wrote on the topic.

I dislike “busy” lines of code, and also dislike putting conceptually unrelated logic on a single line. So, for example, instead of:

instead. So I suspected I’d find few places I’d want to use assignment expressions. I didn’t even consider them for lines already stretching halfway across the screen. In other cases, “unrelated” ruled:

is a vast improvement over the briefer:

The original two statements are doing entirely different conceptual things, and slamming them together is conceptually insane.

In other cases, combining related logic made it harder to understand, such as rewriting:

as the briefer:

The while test there is too subtle, crucially relying on strict left-to-right evaluation in a non-short-circuiting or method-chaining context. My brain isn’t wired that way.

But cases like that were rare. Name binding is very frequent, and “sparse is better than dense” does not mean “almost empty is better than sparse”. For example, I have many functions that return None or 0 to communicate “I have nothing useful to return in this case, but since that’s expected often I’m not going to annoy you with an exception”. This is essentially the same as regular expression search functions returning None when there is no match. So there was lots of code of the form:

I find that clearer, and certainly a bit less typing and pattern-matching reading, as:

It’s also nice to trade away a small amount of horizontal whitespace to get another _line_ of surrounding code on screen. I didn’t give much weight to this at first, but it was so very frequent it added up, and I soon enough became annoyed that I couldn’t actually run the briefer code. That surprised me!

There are other cases where assignment expressions really shine. Rather than pick another from my code, Kirill Balunov gave a lovely example from the standard library’s copy() function in copy.py :

The ever-increasing indentation is semantically misleading: the logic is conceptually flat, “the first test that succeeds wins”:

Using easy assignment expressions allows the visual structure of the code to emphasize the conceptual flatness of the logic; ever-increasing indentation obscured it.

A smaller example from my code delighted me, both allowing to put inherently related logic in a single line, and allowing to remove an annoying “artificial” indentation level:

That if is about as long as I want my lines to get, but remains easy to follow.

So, in all, in most lines binding a name, I wouldn’t use assignment expressions, but because that construct is so very frequent, that leaves many places I would. In most of the latter, I found a small win that adds up due to how often it occurs, and in the rest I found a moderate to major win. I’d certainly use it more often than ternary if , but significantly less often than augmented assignment.

I have another example that quite impressed me at the time.

Where all variables are positive integers, and a is at least as large as the n’th root of x, this algorithm returns the floor of the n’th root of x (and roughly doubling the number of accurate bits per iteration):

It’s not obvious why that works, but is no more obvious in the “loop and a half” form. It’s hard to prove correctness without building on the right insight (the “arithmetic mean - geometric mean inequality”), and knowing some non-trivial things about how nested floor functions behave. That is, the challenges are in the math, not really in the coding.

If you do know all that, then the assignment-expression form is easily read as “while the current guess is too large, get a smaller guess”, where the “too large?” test and the new guess share an expensive sub-expression.

To my eyes, the original form is harder to understand:

This appendix attempts to clarify (though not specify) the rules when a target occurs in a comprehension or in a generator expression. For a number of illustrative examples we show the original code, containing a comprehension, and the translation, where the comprehension has been replaced by an equivalent generator function plus some scaffolding.

Since [x for ...] is equivalent to list(x for ...) these examples all use list comprehensions without loss of generality. And since these examples are meant to clarify edge cases of the rules, they aren’t trying to look like real code.

Note: comprehensions are already implemented via synthesizing nested generator functions like those in this appendix. The new part is adding appropriate declarations to establish the intended scope of assignment expression targets (the same scope they resolve to as if the assignment were performed in the block containing the outermost comprehension). For type inference purposes, these illustrative expansions do not imply that assignment expression targets are always Optional (but they do indicate the target binding scope).

Let’s start with a reminder of what code is generated for a generator expression without assignment expression.

  • Original code (EXPR usually references VAR): def f (): a = [ EXPR for VAR in ITERABLE ]
  • Translation (let’s not worry about name conflicts): def f (): def genexpr ( iterator ): for VAR in iterator : yield EXPR a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a simple assignment expression.

  • Original code: def f (): a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): if False : TARGET = None # Dead code to ensure TARGET is a local variable def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Let’s add a global TARGET declaration in f() .

  • Original code: def f (): global TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def f (): global TARGET def genexpr ( iterator ): global TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Or instead let’s add a nonlocal TARGET declaration in f() .

  • Original code: def g (): TARGET = ... def f (): nonlocal TARGET a = [ TARGET := EXPR for VAR in ITERABLE ]
  • Translation: def g (): TARGET = ... def f (): nonlocal TARGET def genexpr ( iterator ): nonlocal TARGET for VAR in iterator : TARGET = EXPR yield TARGET a = list ( genexpr ( iter ( ITERABLE )))

Finally, let’s nest two comprehensions.

  • Original code: def f (): a = [[ TARGET := i for i in range ( 3 )] for j in range ( 2 )] # I.e., a = [[0, 1, 2], [0, 1, 2]] print ( TARGET ) # prints 2
  • Translation: def f (): if False : TARGET = None def outer_genexpr ( outer_iterator ): nonlocal TARGET def inner_generator ( inner_iterator ): nonlocal TARGET for i in inner_iterator : TARGET = i yield i for j in outer_iterator : yield list ( inner_generator ( range ( 3 ))) a = list ( outer_genexpr ( range ( 2 ))) print ( TARGET )

Because it has been a point of confusion, note that nothing about Python’s scoping semantics is changed. Function-local scopes continue to be resolved at compile time, and to have indefinite temporal extent at run time (“full closures”). Example:

This document has been placed in the public domain.

Source: https://github.com/python/peps/blob/main/peps/pep-0572.rst

Last modified: 2023-10-11 12:05:51 GMT

  • Turtle Graphics
  • Chapter 3: Branch
  • Chapter 4: Loop
  • Chapter 5: Function
  • Chapter 6 Collection

Expression and Statement ¶

Expression ¶.

An expression is a code construct that is evaluated to a value. A code construct is a piece of code. Following are some common expressions:

  • An object or a declared variable, such as: 3 , Hi , x , [1, 2, 3] .
  • A computation using operators, such as 3 + 5 , x < y < z .
  • A function call, such as len("hello") , math.pow(3, 2) .
  • Functions defined inside types are called methods. A method call is an expression. For example: "hello".upper() , [1, 2, 3].pop() .

Evaluation ¶

Evaluation refers to the process that Python interpreter generates the final object of an expression.

Expressions have a common characteristic: the evaluation of the code construct generates a single object .

An object may have a single value such as 3 or multiple values like a list or a tuple.

An expression may have nested expression. For example, a list or a tuple may have other tuple or list or dictionary as its element. There is no limit on the nested levels.

Python evaluates expressions from left to right.

Expression Examples ¶

  • 3 is an literal expression, evaluated to 3
  • 3 + 5 is an addition operation expression, evaluated to 8
  • len("hello") is a function call expression, evaluated to 5
  • math.pow(3, 2) is an method call expression. evaluated to 9
  • len("hello"), math.pow(3, 2) is an expression that has two nested evaluation, the final evaluation result is a tuple of (5, 3) .

There are other code constructs that are expressions. They will be introduced later.

Operator ¶

An operator is a special symbols that perform operations on values and variables.

A high-level programming language like Python has many built-in operators that make the language simple and easy to understand. For example, very few programming languages have built-in support for chained comparison like x < y < z or matrix multiplication operation with @ . Python allows both, though matrix data types are not provided by third party libraries.

The data that an on operator acts on are called operands . An operator may take one operand ( unary operator ), two operands ( binary operator ), or three operands ( ternary operator ).

Python has 7 types of operators.

Types of Operators ¶

1 Arithmetic Operators

2 Relational Operators

3 Logical (Boolean) Operators

4 Membership Operators

5 Identity Operators

6 Bitwise Operators

7 Assignment Operators

3 Logical Operators ¶

Also called Boolean operators. there are three of them: not , and , and or . There are four important details for boolean operators:

  • all Python objects can be Truthy or Falsy .
  • not is a unary boolean operator, it always returns a True or False .
  • The return value of and and or is determined by their operands.
  • and and or operations support short circuiting.

Truthy and Falsy ¶

All Python objects can be Truthy or Falsy , used as True or False in places where a boolean value is required. For example, logical operation and branching code.

You can find the Truthy or Falsy value using the built-in function bool() . Empty values such as 0 , None , empty list [] , empty tuple () and empty dictionary {} are Falsy . Most values are Truthy .

and and or Logical Operation ¶

The evaluation rules are:

  • x or y , if x is True or Truthy , then x , else y
  • x and y , if x is False or Falsy , then x , else y

If it is confusing, use if/else statement.

Short-circuiting ¶

Python supports short-circuiting in and / or operations: if it can determine the result from the left part, it doesn't evaluate the later part.

4 Membership Operator ¶

The membership operator in and not in are used to find if a value is a member of a sequence. A sequence can be a string, a list, a tuple, or even a dictionary.

5 Identity Operator ¶

is and is not are used to check if two variables refer to the same object, i.e., if two variables are aliases of the same object. Use == to check if two values are equal or not. It is not recommended to use identity operator on literal values.

There are some surprises in these two concepts. In Python, small integer values (from -5 to 256, inclusive) and strings are shared by all references for performance reason. Therefore, variables with the same value refer to the same object, so-called interning . But it is implementation-specific.

Practically, it is safe and simple to remember that only an assignment x = y or a function/method call create an alias.

6 Bitwise Operators ¶

Python has one unary and several binary bit operators that act on binary data. Their operands only have 0 s or 1 s.

7 Assignment Operator ¶

Assignment operator = binds a variable in LHS to the RHS expression . It creates the variable if it is the first time binding for the variable, otherwise, it re-binds it to a different object.

The = assignment operation is not an expression because it binds a variable to an object, but the operation generates no value, i.e., you cannot use an assignment in a place that requires a value. It is a syntax error to write code like (x = 3) = 5 or if (x = 3) .

Augmented Assignment ¶

It is common to use a variable in an arithmetic or bitwise operation and assign the result back to the variable. Augmented assignment makes it simple to write those operations. It works for all binary arithmetic or bitwise operators. The LHS variable is used as the first operand of the operator.

The Walrus Operator ¶

Python 3.8 (released in 2019) brings a walrus := operator that work as an assignment expression. It performs two tasks: assign the RHS expression value to the LHS variable and use the value as its expression value.

It combines two tasks into one operation. It makes code simpler in some cases.

Operator Precedence ¶

Operators have different precedences when they are used together in a single expression. For example, the expression 3 + 5 * 4 evaluates 5 * 4 before add them. Thus the result is 23 , not 32 .

The Python document gives a list of operator precedences. You don't need to remember it - you even don't need to read it. The best practices are:

  • break long expression into short expressions with only one or two operators
  • use parenthesis to make the precedence explicit.

Using the best practices, 3 + 5 * 4 should be written as 3 + (5 * 4) .

Conditional Expression ¶

Python provides a conditional expression syntax to make it easy to choose a value from two conditions.

For example, you want to set a message to odd or even based on oddness of a number. Without conditional expression, it needs a four-line statements. With conditional expression, it is one line.

Statement ¶

A Python program consists of instructions to be executed by the Python interpreter. Those instructions are called statements.

All expressions are executed by the Python interpreter and generate values. Expressions are statements. But a program needs more than expressions.

The assignment name = expression is not an expression. It is a statement that bind a variable with the value on the RHS.

There are two types of statements: simple statement and compound statement.

Simple Statements ¶

Statements that are short and usually written in one line are simple statements .

Simple statements include

  • assignment statement name = expression
  • return statement: return expression
  • import statement: import module_name

Compound Statements ¶

A compound statement contains multiple statements that usually span multiple lines.

Compound statements are used to control program flow or create new data types like functions and classes.

They are not expressions.

They give the "high level" abstractions that make Python more flexible and expressive. Conditional statements, loop statements, function definition are compound statements.

Why Statements? ¶

Typically a Python program consists of one or more statements.

All expressions are simple statements. Python has two types of expressions

  • those generate a value: all operators, except the assignment operator = , act on their operands and create a value.
  • those don't generate a value. They handle I/O or change program states. For example: print() , assignment.

Expressions that generate values are useless if the result is not used in a statement that stores the value in memory/disk, or sends it over network, or displays it in a terminal.

For example, you can write an expression x + 1 as a valid statement in a Python program. But it is a waste of CPU cycles because its result is discarded. A statement like y = x + 1 is useful because the result can be used later.

Similarly, a function call math.sqrt(x) is useless by itself. But print("Hi") is useful because it displays the string in a console.

Walrus Operator Syntax ¶

Walrus operator := is interesting because it highlights the difference between a value-generation expression and a statement. Because := generate a value, it is useless if its value is not used. You cannot write it in a single line.

For example, it is an SyntaxError to write the expression x := 5 in a line. Because the evaluated value 5 is not used. If you only need assignment, use an assignment statement.

The following code shows a valid use.

  • Python »
  • 3.8.19 Documentation »
  • The Python Language Reference »

6. Expressions ¶

This chapter explains the meaning of the elements of expressions in Python.

Syntax Notes: In this and the following chapters, extended BNF notation will be used to describe syntax, not lexical analysis. When (one alternative of) a syntax rule has the form

and no semantics are given, the semantics of this form of name are the same as for othername .

6.1. Arithmetic conversions ¶

When a description of an arithmetic operator below uses the phrase “the numeric arguments are converted to a common type”, this means that the operator implementation for built-in types works as follows:

If either argument is a complex number, the other is converted to complex;

otherwise, if either argument is a floating point number, the other is converted to floating point;

otherwise, both must be integers and no conversion is necessary.

Some additional rules apply for certain operators (e.g., a string as a left argument to the ‘%’ operator). Extensions must define their own conversion behavior.

6.2. Atoms ¶

Atoms are the most basic elements of expressions. The simplest atoms are identifiers or literals. Forms enclosed in parentheses, brackets or braces are also categorized syntactically as atoms. The syntax for atoms is:

6.2.1. Identifiers (Names) ¶

An identifier occurring as an atom is a name. See section Identifiers and keywords for lexical definition and section Naming and binding for documentation of naming and binding.

When the name is bound to an object, evaluation of the atom yields that object. When a name is not bound, an attempt to evaluate it raises a NameError exception.

Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam . This transformation is independent of the syntactical context in which the identifier is used. If the transformed name is extremely long (longer than 255 characters), implementation defined truncation may happen. If the class name consists only of underscores, no transformation is done.

6.2.2. Literals ¶

Python supports string and bytes literals and various numeric literals:

Evaluation of a literal yields an object of the given type (string, bytes, integer, floating point number, complex number) with the given value. The value may be approximated in the case of floating point and imaginary (complex) literals. See section Literals for details.

All literals correspond to immutable data types, and hence the object’s identity is less important than its value. Multiple evaluations of literals with the same value (either the same occurrence in the program text or a different occurrence) may obtain the same object or a different object with the same value.

6.2.3. Parenthesized forms ¶

A parenthesized form is an optional expression list enclosed in parentheses:

A parenthesized expression list yields whatever that expression list yields: if the list contains at least one comma, it yields a tuple; otherwise, it yields the single expression that makes up the expression list.

An empty pair of parentheses yields an empty tuple object. Since tuples are immutable, the same rules as for literals apply (i.e., two occurrences of the empty tuple may or may not yield the same object).

Note that tuples are not formed by the parentheses, but rather by use of the comma operator. The exception is the empty tuple, for which parentheses are required — allowing unparenthesized “nothing” in expressions would cause ambiguities and allow common typos to pass uncaught.

6.2.4. Displays for lists, sets and dictionaries ¶

For constructing a list, a set or a dictionary Python provides special syntax called “displays”, each of them in two flavors:

either the container contents are listed explicitly, or

they are computed via a set of looping and filtering instructions, called a comprehension .

Common syntax elements for comprehensions are:

The comprehension consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new container are those that would be produced by considering each of the for or if clauses a block, nesting from left to right, and evaluating the expression to produce an element each time the innermost block is reached.

However, aside from the iterable expression in the leftmost for clause, the comprehension is executed in a separate implicitly nested scope. This ensures that names assigned to in the target list don’t “leak” into the enclosing scope.

The iterable expression in the leftmost for clause is evaluated directly in the enclosing scope and then passed as an argument to the implicitly nested scope. Subsequent for clauses and any filter condition in the leftmost for clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: [x*y for x in range(10) for y in range(x, x+10)] .

To ensure the comprehension always results in a container of the appropriate type, yield and yield from expressions are prohibited in the implicitly nested scope.

Since Python 3.6, in an async def function, an async for clause may be used to iterate over a asynchronous iterator . A comprehension in an async def function may consist of either a for or async for clause following the leading expression, may contain additional for or async for clauses, and may also use await expressions. If a comprehension contains either async for clauses or await expressions it is called an asynchronous comprehension . An asynchronous comprehension may suspend the execution of the coroutine function in which it appears. See also PEP 530 .

New in version 3.6: Asynchronous comprehensions were introduced.

Changed in version 3.8: yield and yield from prohibited in the implicitly nested scope.

6.2.5. List displays ¶

A list display is a possibly empty series of expressions enclosed in square brackets:

A list display yields a new list object, the contents being specified by either a list of expressions or a comprehension. When a comma-separated list of expressions is supplied, its elements are evaluated from left to right and placed into the list object in that order. When a comprehension is supplied, the list is constructed from the elements resulting from the comprehension.

6.2.6. Set displays ¶

A set display is denoted by curly braces and distinguishable from dictionary displays by the lack of colons separating keys and values:

A set display yields a new mutable set object, the contents being specified by either a sequence of expressions or a comprehension. When a comma-separated list of expressions is supplied, its elements are evaluated from left to right and added to the set object. When a comprehension is supplied, the set is constructed from the elements resulting from the comprehension.

An empty set cannot be constructed with {} ; this literal constructs an empty dictionary.

6.2.7. Dictionary displays ¶

A dictionary display is a possibly empty series of key/datum pairs enclosed in curly braces:

A dictionary display yields a new dictionary object.

If a comma-separated sequence of key/datum pairs is given, they are evaluated from left to right to define the entries of the dictionary: each key object is used as a key into the dictionary to store the corresponding datum. This means that you can specify the same key multiple times in the key/datum list, and the final dictionary’s value for that key will be the last one given.

A double asterisk ** denotes dictionary unpacking . Its operand must be a mapping . Each mapping item is added to the new dictionary. Later values replace values already set by earlier key/datum pairs and earlier dictionary unpackings.

New in version 3.5: Unpacking into dictionary displays, originally proposed by PEP 448 .

A dict comprehension, in contrast to list and set comprehensions, needs two expressions separated with a colon followed by the usual “for” and “if” clauses. When the comprehension is run, the resulting key and value elements are inserted in the new dictionary in the order they are produced.

Restrictions on the types of the key values are listed earlier in section The standard type hierarchy . (To summarize, the key type should be hashable , which excludes all mutable objects.) Clashes between duplicate keys are not detected; the last datum (textually rightmost in the display) stored for a given key value prevails.

Changed in version 3.8: Prior to Python 3.8, in dict comprehensions, the evaluation order of key and value was not well-defined. In CPython, the value was evaluated before the key. Starting with 3.8, the key is evaluated before the value, as proposed by PEP 572 .

6.2.8. Generator expressions ¶

A generator expression is a compact generator notation in parentheses:

A generator expression yields a new generator object. Its syntax is the same as for comprehensions, except that it is enclosed in parentheses instead of brackets or curly braces.

Variables used in the generator expression are evaluated lazily when the __next__() method is called for the generator object (in the same fashion as normal generators). However, the iterable expression in the leftmost for clause is immediately evaluated, so that an error produced by it will be emitted at the point where the generator expression is defined, rather than at the point where the first value is retrieved. Subsequent for clauses and any filter condition in the leftmost for clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: (x*y for x in range(10) for y in range(x, x+10)) .

The parentheses can be omitted on calls with only one argument. See section Calls for details.

To avoid interfering with the expected operation of the generator expression itself, yield and yield from expressions are prohibited in the implicitly defined generator.

If a generator expression contains either async for clauses or await expressions it is called an asynchronous generator expression . An asynchronous generator expression returns a new asynchronous generator object, which is an asynchronous iterator (see Asynchronous Iterators ).

New in version 3.6: Asynchronous generator expressions were introduced.

Changed in version 3.7: Prior to Python 3.7, asynchronous generator expressions could only appear in async def coroutines. Starting with 3.7, any function can use asynchronous generator expressions.

6.2.9. Yield expressions ¶

The yield expression is used when defining a generator function or an asynchronous generator function and thus can only be used in the body of a function definition. Using a yield expression in a function’s body causes that function to be a generator, and using it in an async def function’s body causes that coroutine function to be an asynchronous generator. For example:

Due to their side effects on the containing scope, yield expressions are not permitted as part of the implicitly defined scopes used to implement comprehensions and generator expressions.

Changed in version 3.8: Yield expressions prohibited in the implicitly nested scopes used to implement comprehensions and generator expressions.

Generator functions are described below, while asynchronous generator functions are described separately in section Asynchronous generator functions .

When a generator function is called, it returns an iterator known as a generator. That generator then controls the execution of the generator function. The execution starts when one of the generator’s methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to the generator’s caller. By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by calling one of the generator’s methods, the function can proceed exactly as if the yield expression were just another external call. The value of the yield expression after resuming depends on the method which resumed the execution. If __next__() is used (typically via either a for or the next() builtin) then the result is None . Otherwise, if send() is used, then the result will be the value passed in to that method.

All of this makes generator functions quite similar to coroutines; they yield multiple times, they have more than one entry point and their execution can be suspended. The only difference is that a generator function cannot control where the execution should continue after it yields; the control is always transferred to the generator’s caller.

Yield expressions are allowed anywhere in a try construct. If the generator is not resumed before it is finalized (by reaching a zero reference count or by being garbage collected), the generator-iterator’s close() method will be called, allowing any pending finally clauses to execute.

When yield from <expr> is used, the supplied expression must be an iterable. The values produced by iterating that iterable are passed directly to the caller of the current generator’s methods. Any values passed in with send() and any exceptions passed in with throw() are passed to the underlying iterator if it has the appropriate methods. If this is not the case, then send() will raise AttributeError or TypeError , while throw() will just raise the passed in exception immediately.

When the underlying iterator is complete, the value attribute of the raised StopIteration instance becomes the value of the yield expression. It can be either set explicitly when raising StopIteration , or automatically when the subiterator is a generator (by returning a value from the subgenerator).

Changed in version 3.3: Added yield from <expr> to delegate control flow to a subiterator.

The parentheses may be omitted when the yield expression is the sole expression on the right hand side of an assignment statement.

The proposal for adding generators and the yield statement to Python.

The proposal to enhance the API and syntax of generators, making them usable as simple coroutines.

The proposal to introduce the yield_from syntax, making delegation to subgenerators easy.

The proposal that expanded on PEP 492 by adding generator capabilities to coroutine functions.

6.2.9.1. Generator-iterator methods ¶

This subsection describes the methods of a generator iterator. They can be used to control the execution of a generator function.

Note that calling any of the generator methods below when the generator is already executing raises a ValueError exception.

Starts the execution of a generator function or resumes it at the last executed yield expression. When a generator function is resumed with a __next__() method, the current yield expression always evaluates to None . The execution then continues to the next yield expression, where the generator is suspended again, and the value of the expression_list is returned to __next__() ’s caller. If the generator exits without yielding another value, a StopIteration exception is raised.

This method is normally called implicitly, e.g. by a for loop, or by the built-in next() function.

Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

Raises an exception of type type at the point where the generator was paused, and returns the next value yielded by the generator function. If the generator exits without yielding another value, a StopIteration exception is raised. If the generator function does not catch the passed-in exception, or raises a different exception, then that exception propagates to the caller.

Raises a GeneratorExit at the point where the generator function was paused. If the generator function then exits gracefully, is already closed, or raises GeneratorExit (by not catching the exception), close returns to its caller. If the generator yields a value, a RuntimeError is raised. If the generator raises any other exception, it is propagated to the caller. close() does nothing if the generator has already exited due to an exception or normal exit.

6.2.9.2. Examples ¶

Here is a simple example that demonstrates the behavior of generators and generator functions:

For examples using yield from , see PEP 380: Syntax for Delegating to a Subgenerator in “What’s New in Python.”

6.2.9.3. Asynchronous generator functions ¶

The presence of a yield expression in a function or method defined using async def further defines the function as an asynchronous generator function.

When an asynchronous generator function is called, it returns an asynchronous iterator known as an asynchronous generator object. That object then controls the execution of the generator function. An asynchronous generator object is typically used in an async for statement in a coroutine function analogously to how a generator object would be used in a for statement.

Calling one of the asynchronous generator’s methods returns an awaitable object, and the execution starts when this object is awaited on. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to the awaiting coroutine. As with a generator, suspension means that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by awaiting on the next object returned by the asynchronous generator’s methods, the function can proceed exactly as if the yield expression were just another external call. The value of the yield expression after resuming depends on the method which resumed the execution. If __anext__() is used then the result is None . Otherwise, if asend() is used, then the result will be the value passed in to that method.

In an asynchronous generator function, yield expressions are allowed anywhere in a try construct. However, if an asynchronous generator is not resumed before it is finalized (by reaching a zero reference count or by being garbage collected), then a yield expression within a try construct could result in a failure to execute pending finally clauses. In this case, it is the responsibility of the event loop or scheduler running the asynchronous generator to call the asynchronous generator-iterator’s aclose() method and run the resulting coroutine object, thus allowing any pending finally clauses to execute.

To take care of finalization, an event loop should define a finalizer function which takes an asynchronous generator-iterator and presumably calls aclose() and executes the coroutine. This finalizer may be registered by calling sys.set_asyncgen_hooks() . When first iterated over, an asynchronous generator-iterator will store the registered finalizer to be called upon finalization. For a reference example of a finalizer method see the implementation of asyncio.Loop.shutdown_asyncgens in Lib/asyncio/base_events.py .

The expression yield from <expr> is a syntax error when used in an asynchronous generator function.

6.2.9.4. Asynchronous generator-iterator methods ¶

This subsection describes the methods of an asynchronous generator iterator, which are used to control the execution of a generator function.

Returns an awaitable which when run starts to execute the asynchronous generator or resumes it at the last executed yield expression. When an asynchronous generator function is resumed with an __anext__() method, the current yield expression always evaluates to None in the returned awaitable, which when run will continue to the next yield expression. The value of the expression_list of the yield expression is the value of the StopIteration exception raised by the completing coroutine. If the asynchronous generator exits without yielding another value, the awaitable instead raises a StopAsyncIteration exception, signalling that the asynchronous iteration has completed.

This method is normally called implicitly by a async for loop.

Returns an awaitable which when run resumes the execution of the asynchronous generator. As with the send() method for a generator, this “sends” a value into the asynchronous generator function, and the value argument becomes the result of the current yield expression. The awaitable returned by the asend() method will return the next value yielded by the generator as the value of the raised StopIteration , or raises StopAsyncIteration if the asynchronous generator exits without yielding another value. When asend() is called to start the asynchronous generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

Returns an awaitable that raises an exception of type type at the point where the asynchronous generator was paused, and returns the next value yielded by the generator function as the value of the raised StopIteration exception. If the asynchronous generator exits without yielding another value, a StopAsyncIteration exception is raised by the awaitable. If the generator function does not catch the passed-in exception, or raises a different exception, then when the awaitable is run that exception propagates to the caller of the awaitable.

Returns an awaitable that when run will throw a GeneratorExit into the asynchronous generator function at the point where it was paused. If the asynchronous generator function then exits gracefully, is already closed, or raises GeneratorExit (by not catching the exception), then the returned awaitable will raise a StopIteration exception. Any further awaitables returned by subsequent calls to the asynchronous generator will raise a StopAsyncIteration exception. If the asynchronous generator yields a value, a RuntimeError is raised by the awaitable. If the asynchronous generator raises any other exception, it is propagated to the caller of the awaitable. If the asynchronous generator has already exited due to an exception or normal exit, then further calls to aclose() will return an awaitable that does nothing.

6.3. Primaries ¶

Primaries represent the most tightly bound operations of the language. Their syntax is:

6.3.1. Attribute references ¶

An attribute reference is a primary followed by a period and a name:

The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the attribute whose name is the identifier. This production can be customized by overriding the __getattr__() method. If this attribute is not available, the exception AttributeError is raised. Otherwise, the type and value of the object produced is determined by the object. Multiple evaluations of the same attribute reference may yield different objects.

6.3.2. Subscriptions ¶

A subscription selects an item of a sequence (string, tuple or list) or mapping (dictionary) object:

The primary must evaluate to an object that supports subscription (lists or dictionaries for example). User-defined objects can support subscription by defining a __getitem__() method.

For built-in objects, there are two types of objects that support subscription:

If the primary is a mapping, the expression list must evaluate to an object whose value is one of the keys of the mapping, and the subscription selects the value in the mapping that corresponds to that key. (The expression list is a tuple except if it has exactly one item.)

If the primary is a sequence, the expression list must evaluate to an integer or a slice (as discussed in the following section).

The formal syntax makes no special provision for negative indices in sequences; however, built-in sequences all provide a __getitem__() method that interprets negative indices by adding the length of the sequence to the index (so that x[-1] selects the last item of x ). The resulting value must be a nonnegative integer less than the number of items in the sequence, and the subscription selects the item whose index is that value (counting from zero). Since the support for negative indices and slicing occurs in the object’s __getitem__() method, subclasses overriding this method will need to explicitly add that support.

A string’s items are characters. A character is not a separate data type but a string of exactly one character.

6.3.3. Slicings ¶

A slicing selects a range of items in a sequence object (e.g., a string, tuple or list). Slicings may be used as expressions or as targets in assignment or del statements. The syntax for a slicing:

There is ambiguity in the formal syntax here: anything that looks like an expression list also looks like a slice list, so any subscription can be interpreted as a slicing. Rather than further complicating the syntax, this is disambiguated by defining that in this case the interpretation as a subscription takes priority over the interpretation as a slicing (this is the case if the slice list contains no proper slice).

The semantics for a slicing are as follows. The primary is indexed (using the same __getitem__() method as normal subscription) with a key that is constructed from the slice list, as follows. If the slice list contains at least one comma, the key is a tuple containing the conversion of the slice items; otherwise, the conversion of the lone slice item is the key. The conversion of a slice item that is an expression is that expression. The conversion of a proper slice is a slice object (see section The standard type hierarchy ) whose start , stop and step attributes are the values of the expressions given as lower bound, upper bound and stride, respectively, substituting None for missing expressions.

6.3.4. Calls ¶

A call calls a callable object (e.g., a function ) with a possibly empty series of arguments :

An optional trailing comma may be present after the positional and keyword arguments but does not affect the semantics.

The primary must evaluate to a callable object (user-defined functions, built-in functions, methods of built-in objects, class objects, methods of class instances, and all objects having a __call__() method are callable). All argument expressions are evaluated before the call is attempted. Please refer to section Function definitions for the syntax of formal parameter lists.

If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None , it fills the slot). When all arguments have been processed, the slots that are still unfilled are filled with the corresponding default value from the function definition. (Default values are calculated, once, when the function is defined; thus, a mutable object such as a list or dictionary used as default value will be shared by all calls that don’t specify an argument value for the corresponding slot; this should usually be avoided.) If there are any unfilled slots for which no default value is specified, a TypeError exception is raised. Otherwise, the list of filled slots is used as the argument list for the call.

CPython implementation detail: An implementation may provide built-in functions whose positional parameters do not have names, even if they are ‘named’ for the purpose of documentation, and which therefore cannot be supplied by keyword. In CPython, this is the case for functions implemented in C that use PyArg_ParseTuple() to parse their arguments.

If there are more positional arguments than there are formal parameter slots, a TypeError exception is raised, unless a formal parameter using the syntax *identifier is present; in this case, that formal parameter receives a tuple containing the excess positional arguments (or an empty tuple if there were no excess positional arguments).

If any keyword argument does not correspond to a formal parameter name, a TypeError exception is raised, unless a formal parameter using the syntax **identifier is present; in this case, that formal parameter receives a dictionary containing the excess keyword arguments (using the keywords as keys and the argument values as corresponding values), or a (new) empty dictionary if there were no excess keyword arguments.

If the syntax *expression appears in the function call, expression must evaluate to an iterable . Elements from these iterables are treated as if they were additional positional arguments. For the call f(x1, x2, *y, x3, x4) , if y evaluates to a sequence y1 , …, yM , this is equivalent to a call with M+4 positional arguments x1 , x2 , y1 , …, yM , x3 , x4 .

A consequence of this is that although the *expression syntax may appear after explicit keyword arguments, it is processed before the keyword arguments (and any **expression arguments – see below). So:

It is unusual for both keyword arguments and the *expression syntax to be used in the same call, so in practice this confusion does not arise.

If the syntax **expression appears in the function call, expression must evaluate to a mapping , the contents of which are treated as additional keyword arguments. If a keyword is already present (as an explicit keyword argument, or from another unpacking), a TypeError exception is raised.

Formal parameters using the syntax *identifier or **identifier cannot be used as positional argument slots or as keyword argument names.

Changed in version 3.5: Function calls accept any number of * and ** unpackings, positional arguments may follow iterable unpackings ( * ), and keyword arguments may follow dictionary unpackings ( ** ). Originally proposed by PEP 448 .

A call always returns some value, possibly None , unless it raises an exception. How this value is computed depends on the type of the callable object.

The code block for the function is executed, passing it the argument list. The first thing the code block will do is bind the formal parameters to the arguments; this is described in section Function definitions . When the code block executes a return statement, this specifies the return value of the function call.

The result is up to the interpreter; see Built-in Functions for the descriptions of built-in functions and methods.

A new instance of that class is returned.

The corresponding user-defined function is called, with an argument list that is one longer than the argument list of the call: the instance becomes the first argument.

The class must define a __call__() method; the effect is then the same as if that method was called.

6.4. Await expression ¶

Suspend the execution of coroutine on an awaitable object. Can only be used inside a coroutine function .

New in version 3.5.

6.5. The power operator ¶

The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right. The syntax is:

Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1 .

The power operator has the same semantics as the built-in pow() function, when called with two arguments: it yields its left argument raised to the power of its right argument. The numeric arguments are first converted to a common type, and the result is of that type.

For int operands, the result has the same type as the operands unless the second argument is negative; in that case, all arguments are converted to float and a float result is delivered. For example, 10**2 returns 100 , but 10**-2 returns 0.01 .

Raising 0.0 to a negative power results in a ZeroDivisionError . Raising a negative number to a fractional power results in a complex number. (In earlier versions it raised a ValueError .)

6.6. Unary arithmetic and bitwise operations ¶

All unary arithmetic and bitwise operations have the same priority:

The unary - (minus) operator yields the negation of its numeric argument.

The unary + (plus) operator yields its numeric argument unchanged.

The unary ~ (invert) operator yields the bitwise inversion of its integer argument. The bitwise inversion of x is defined as -(x+1) . It only applies to integral numbers.

In all three cases, if the argument does not have the proper type, a TypeError exception is raised.

6.7. Binary arithmetic operations ¶

The binary arithmetic operations have the conventional priority levels. Note that some of these operations also apply to certain non-numeric types. Apart from the power operator, there are only two levels, one for multiplicative operators and one for additive operators:

The * (multiplication) operator yields the product of its arguments. The arguments must either both be numbers, or one argument must be an integer and the other must be a sequence. In the former case, the numbers are converted to a common type and then multiplied together. In the latter case, sequence repetition is performed; a negative repetition factor yields an empty sequence.

The @ (at) operator is intended to be used for matrix multiplication. No builtin Python types implement this operator.

The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result. Division by zero raises the ZeroDivisionError exception.

The % (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the ZeroDivisionError exception. The arguments may be floating point numbers, e.g., 3.14%0.7 equals 0.34 (since 3.14 equals 4*0.7 + 0.34 .) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand 1 .

The floor division and modulo operators are connected by the following identity: x == (x//y)*y + (x%y) . Floor division and modulo are also connected with the built-in function divmod() : divmod(x, y) == (x//y, x%y) . 2 .

In addition to performing the modulo operation on numbers, the % operator is also overloaded by string objects to perform old-style string formatting (also known as interpolation). The syntax for string formatting is described in the Python Library Reference, section printf-style String Formatting .

The floor division operator, the modulo operator, and the divmod() function are not defined for complex numbers. Instead, convert to a floating point number using the abs() function if appropriate.

The + (addition) operator yields the sum of its arguments. The arguments must either both be numbers or both be sequences of the same type. In the former case, the numbers are converted to a common type and then added together. In the latter case, the sequences are concatenated.

The - (subtraction) operator yields the difference of its arguments. The numeric arguments are first converted to a common type.

6.8. Shifting operations ¶

The shifting operations have lower priority than the arithmetic operations:

These operators accept integers as arguments. They shift the first argument to the left or right by the number of bits given by the second argument.

A right shift by n bits is defined as floor division by pow(2,n) . A left shift by n bits is defined as multiplication with pow(2,n) .

6.9. Binary bitwise operations ¶

Each of the three bitwise operations has a different priority level:

The & operator yields the bitwise AND of its arguments, which must be integers.

The ^ operator yields the bitwise XOR (exclusive OR) of its arguments, which must be integers.

The | operator yields the bitwise (inclusive) OR of its arguments, which must be integers.

6.10. Comparisons ¶

Unlike C, all comparison operations in Python have the same priority, which is lower than that of any arithmetic, shifting or bitwise operation. Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics:

Comparisons yield boolean values: True or False .

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z , except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

Formally, if a , b , c , …, y , z are expressions and op1 , op2 , …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z , except that each expression is evaluated at most once.

Note that a op1 b op2 c doesn’t imply any kind of comparison between a and c , so that, e.g., x < y > z is perfectly legal (though perhaps not pretty).

6.10.1. Value comparisons ¶

The operators < , > , == , >= , <= , and != compare the values of two objects. The objects do not need to have the same type.

Chapter Objects, values and types states that objects have a value (in addition to type and identity). The value of an object is a rather abstract notion in Python: For example, there is no canonical access method for an object’s value. Also, there is no requirement that the value of an object should be constructed in a particular way, e.g. comprised of all its data attributes. Comparison operators implement a particular notion of what the value of an object is. One can think of them as defining the value of an object indirectly, by means of their comparison implementation.

Because all types are (direct or indirect) subtypes of object , they inherit the default comparison behavior from object . Types can customize their comparison behavior by implementing rich comparison methods like __lt__() , described in Basic customization .

The default behavior for equality comparison ( == and != ) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y ).

A default order comparison ( < , > , <= , and >= ) is not provided; an attempt raises TypeError . A motivation for this default behavior is the lack of a similar invariant as for equality.

The behavior of the default equality comparison, that instances with different identities are always unequal, may be in contrast to what types will need that have a sensible definition of object value and value-based equality. Such types will need to customize their comparison behavior, and in fact, a number of built-in types have done that.

The following list describes the comparison behavior of the most important built-in types.

Numbers of built-in numeric types ( Numeric Types — int, float, complex ) and of the standard library types fractions.Fraction and decimal.Decimal can be compared within and across their types, with the restriction that complex numbers do not support order comparison. Within the limits of the types involved, they compare mathematically (algorithmically) correct without loss of precision.

The not-a-number values float('NaN') and decimal.Decimal('NaN') are special. Any ordered comparison of a number to a not-a-number value is false. A counter-intuitive implication is that not-a-number values are not equal to themselves. For example, if x = float('NaN') , 3 < x , x < 3 and x == x are all false, while x != x is true. This behavior is compliant with IEEE 754.

None and NotImplemented are singletons. PEP 8 advises that comparisons for singletons should always be done with is or is not , never the equality operators.

Binary sequences (instances of bytes or bytearray ) can be compared within and across their types. They compare lexicographically using the numeric values of their elements.

Strings (instances of str ) compare lexicographically using the numerical Unicode code points (the result of the built-in function ord() ) of their characters. 3

Strings and binary sequences cannot be directly compared.

Sequences (instances of tuple , list , or range ) can be compared only within each of their types, with the restriction that ranges do not support order comparison. Equality comparison across these types results in inequality, and ordering comparison across these types raises TypeError .

Sequences compare lexicographically using comparison of corresponding elements. The built-in containers typically assume identical objects are equal to themselves. That lets them bypass equality tests for identical objects to improve performance and to maintain their internal invariants.

Lexicographical comparison between built-in collections works as follows:

For two collections to compare equal, they must be of the same type, have the same length, and each pair of corresponding elements must compare equal (for example, [1,2] == (1,2) is false because the type is not the same).

Collections that support order comparison are ordered the same as their first unequal elements (for example, [1,2,x] <= [1,2,y] has the same value as x <= y ). If a corresponding element does not exist, the shorter collection is ordered first (for example, [1,2] < [1,2,3] is true).

Mappings (instances of dict ) compare equal if and only if they have equal (key, value) pairs. Equality comparison of the keys and values enforces reflexivity.

Order comparisons ( < , > , <= , and >= ) raise TypeError .

Sets (instances of set or frozenset ) can be compared within and across their types.

They define order comparison operators to mean subset and superset tests. Those relations do not define total orderings (for example, the two sets {1,2} and {2,3} are not equal, nor subsets of one another, nor supersets of one another). Accordingly, sets are not appropriate arguments for functions which depend on total ordering (for example, min() , max() , and sorted() produce undefined results given a list of sets as inputs).

Comparison of sets enforces reflexivity of its elements.

Most other built-in types have no comparison methods implemented, so they inherit the default comparison behavior.

User-defined classes that customize their comparison behavior should follow some consistency rules, if possible:

Equality comparison should be reflexive. In other words, identical objects should compare equal:

x is y implies x == y

Comparison should be symmetric. In other words, the following expressions should have the same result:

x == y and y == x x != y and y != x x < y and y > x x <= y and y >= x

Comparison should be transitive. The following (non-exhaustive) examples illustrate that:

x > y and y > z implies x > z x < y and y <= z implies x < z

Inverse comparison should result in the boolean negation. In other words, the following expressions should have the same result:

x == y and not x != y x < y and not x >= y (for total ordering) x > y and not x <= y (for total ordering)

The last two expressions apply to totally ordered collections (e.g. to sequences, but not to sets or mappings). See also the total_ordering() decorator.

The hash() result should be consistent with equality. Objects that are equal should either have the same hash value, or be marked as unhashable.

Python does not enforce these consistency rules. In fact, the not-a-number values are an example for not following these rules.

6.10.2. Membership test operations ¶

The operators in and not in test for membership. x in s evaluates to True if x is a member of s , and False otherwise. x not in s returns the negation of x in s . All built-in sequences and set types support this as well as dictionary, for which in tests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y) .

For the string and bytes types, x in y is True if and only if x is a substring of y . An equivalent test is y.find(x) != -1 . Empty strings are always considered to be a substring of any other string, so "" in "abc" will return True .

For user-defined classes which define the __contains__() method, x in y returns True if y.__contains__(x) returns a true value, and False otherwise.

For user-defined classes which do not define __contains__() but do define __iter__() , x in y is True if some value z , for which the expression x is z or x == z is true, is produced while iterating over y . If an exception is raised during the iteration, it is as if in raised that exception.

Lastly, the old-style iteration protocol is tried: if a class defines __getitem__() , x in y is True if and only if there is a non-negative integer index i such that x is y[i] or x == y[i] , and no lower integer index raises the IndexError exception. (If any other exception is raised, it is as if in raised that exception).

The operator not in is defined to have the inverse truth value of in .

6.10.3. Identity comparisons ¶

The operators is and is not test for an object’s identity: x is y is true if and only if x and y are the same object. An Object’s identity is determined using the id() function. x is not y yields the inverse truth value. 4

6.11. Boolean operations ¶

In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False , None , numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. User-defined objects can customize their truth value by providing a __bool__() method.

The operator not yields True if its argument is false, False otherwise.

The expression x and y first evaluates x ; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

The expression x or y first evaluates x ; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

Note that neither and nor or restrict the value and type they return to False and True , but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value. Because not has to create a new value, it returns a boolean value regardless of the type of its argument (for example, not 'foo' produces False rather than '' .)

6.12. Assignment expressions ¶

An assignment expression (sometimes also called a “named expression” or “walrus”) assigns an expression to an identifier , while also returning the value of the expression .

One common use case is when handling matched regular expressions:

Or, when processing a file stream in chunks:

New in version 3.8: See PEP 572 for more details about assignment expressions.

6.13. Conditional expressions ¶

Conditional expressions (sometimes called a “ternary operator”) have the lowest priority of all Python operations.

The expression x if C else y first evaluates the condition, C rather than x . If C is true, x is evaluated and its value is returned; otherwise, y is evaluated and its value is returned.

See PEP 308 for more details about conditional expressions.

6.14. Lambdas ¶

Lambda expressions (sometimes called lambda forms) are used to create anonymous functions. The expression lambda parameters: expression yields a function object. The unnamed object behaves like a function object defined with:

See section Function definitions for the syntax of parameter lists. Note that functions created with lambda expressions cannot contain statements or annotations.

6.15. Expression lists ¶

Except when part of a list or set display, an expression list containing at least one comma yields a tuple. The length of the tuple is the number of expressions in the list. The expressions are evaluated from left to right.

An asterisk * denotes iterable unpacking . Its operand must be an iterable . The iterable is expanded into a sequence of items, which are included in the new tuple, list, or set, at the site of the unpacking.

New in version 3.5: Iterable unpacking in expression lists, originally proposed by PEP 448 .

The trailing comma is required only to create a single tuple (a.k.a. a singleton ); it is optional in all other cases. A single expression without a trailing comma doesn’t create a tuple, but rather yields the value of that expression. (To create an empty tuple, use an empty pair of parentheses: () .)

6.16. Evaluation order ¶

Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.

In the following lines, expressions will be evaluated in the arithmetic order of their suffixes:

6.17. Operator precedence ¶

The following table summarizes the operator precedence in Python, from lowest precedence (least binding) to highest precedence (most binding). Operators in the same box have the same precedence. Unless the syntax is explicitly given, operators are binary. Operators in the same box group left to right (except for exponentiation, which groups from right to left).

Note that comparisons, membership tests, and identity tests, all have the same precedence and have a left-to-right chaining feature as described in the Comparisons section.

While abs(x%y) < abs(y) is true mathematically, for floats it may not be true numerically due to roundoff. For example, and assuming a platform on which a Python float is an IEEE 754 double-precision number, in order that -1e-100 % 1e100 have the same sign as 1e100 , the computed result is -1e-100 + 1e100 , which is numerically exactly equal to 1e100 . The function math.fmod() returns a result whose sign matches the sign of the first argument instead, and so returns -1e-100 in this case. Which approach is more appropriate depends on the application.

If x is very close to an exact integer multiple of y, it’s possible for x//y to be one larger than (x-x%y)//y due to rounding. In such cases, Python returns the latter result, in order to preserve that divmod(x,y)[0] * y + x % y be very close to x .

The Unicode standard distinguishes between code points (e.g. U+0041) and abstract characters (e.g. “LATIN CAPITAL LETTER A”). While most abstract characters in Unicode are only represented using one code point, there is a number of abstract characters that can in addition be represented using a sequence of more than one code point. For example, the abstract character “LATIN CAPITAL LETTER C WITH CEDILLA” can be represented as a single precomposed character at code position U+00C7, or as a sequence of a base character at code position U+0043 (LATIN CAPITAL LETTER C), followed by a combining character at code position U+0327 (COMBINING CEDILLA).

The comparison operators on strings compare at the level of Unicode code points. This may be counter-intuitive to humans. For example, "\u00C7" == "\u0043\u0327" is False , even though both strings represent the same abstract character “LATIN CAPITAL LETTER C WITH CEDILLA”.

To compare strings at the level of abstract characters (that is, in a way intuitive to humans), use unicodedata.normalize() .

Due to automatic garbage-collection, free lists, and the dynamic nature of descriptors, you may notice seemingly unusual behaviour in certain uses of the is operator, like those involving comparisons between instance methods, or constants. Check their documentation for more info.

The % operator is also used for string formatting; the same precedence applies.

The power operator ** binds less tightly than an arithmetic or bitwise unary operator on its right, that is, 2**-1 is 0.5 .

Table of Contents

  • 6.1. Arithmetic conversions
  • 6.2.1. Identifiers (Names)
  • 6.2.2. Literals
  • 6.2.3. Parenthesized forms
  • 6.2.4. Displays for lists, sets and dictionaries
  • 6.2.5. List displays
  • 6.2.6. Set displays
  • 6.2.7. Dictionary displays
  • 6.2.8. Generator expressions
  • 6.2.9.1. Generator-iterator methods
  • 6.2.9.2. Examples
  • 6.2.9.3. Asynchronous generator functions
  • 6.2.9.4. Asynchronous generator-iterator methods
  • 6.3.1. Attribute references
  • 6.3.2. Subscriptions
  • 6.3.3. Slicings
  • 6.3.4. Calls
  • 6.4. Await expression
  • 6.5. The power operator
  • 6.6. Unary arithmetic and bitwise operations
  • 6.7. Binary arithmetic operations
  • 6.8. Shifting operations
  • 6.9. Binary bitwise operations
  • 6.10.1. Value comparisons
  • 6.10.2. Membership test operations
  • 6.10.3. Identity comparisons
  • 6.11. Boolean operations
  • 6.12. Assignment expressions
  • 6.13. Conditional expressions
  • 6.14. Lambdas
  • 6.15. Expression lists
  • 6.16. Evaluation order
  • 6.17. Operator precedence

Previous topic

5. The import system

7. Simple statements

  • Report a Bug
  • Show Source

logo

Learning Python by doing

  • suggest edit

Variables, Expressions, and Assignments

Variables, expressions, and assignments 1 #, introduction #.

In this chapter, we introduce some of the main building blocks needed to create programs–that is, variables, expressions, and assignments. Programming related variables can be intepret in the same way that we interpret mathematical variables, as elements that store values that can later be changed. Usually, variables and values are used within the so-called expressions. Once again, just as in mathematics, an expression is a construct of values and variables connected with operators that result in a new value. Lastly, an assignment is a language construct know as an statement that assign a value (either as a constant or expression) to a variable. The rest of this notebook will dive into the main concepts that we need to fully understand these three language constructs.

Values and Types #

A value is the basic unit used in a program. It may be, for instance, a number respresenting temperature. It may be a string representing a word. Some values are 42, 42.0, and ‘Hello, Data Scientists!’.

Each value has its own type : 42 is an integer ( int in Python), 42.0 is a floating-point number ( float in Python), and ‘Hello, Data Scientists!’ is a string ( str in Python).

The Python interpreter can tell you the type of a value: the function type takes a value as argument and returns its corresponding type.

Observe the difference between type(42) and type('42') !

Expressions and Statements #

On the one hand, an expression is a combination of values, variables, and operators.

A value all by itself is considered an expression, and so is a variable.

When you type an expression at the prompt, the interpreter evaluates it, which means that it calculates the value of the expression and displays it.

In boxes above, m has the value 27 and m + 25 has the value 52 . m + 25 is said to be an expression.

On the other hand, a statement is an instruction that has an effect, like creating a variable or displaying a value.

The first statement initializes the variable n with the value 17 , this is a so-called assignment statement .

The second statement is a print statement that prints the value of the variable n .

The effect is not always visible. Assigning a value to a variable is not visible, but printing the value of a variable is.

Assignment Statements #

We have already seen that Python allows you to evaluate expressions, for instance 40 + 2 . It is very convenient if we are able to store the calculated value in some variable for future use. The latter can be done via an assignment statement. An assignment statement creates a new variable with a given name and assigns it a value.

The example in the previous code contains three assignments. The first one assigns the value of the expression 40 + 2 to a new variable called magicnumber ; the second one assigns the value of π to the variable pi , and; the last assignment assigns the string value 'Data is eatig the world' to the variable message .

Programmers generally choose names for their variables that are meaningful. In this way, they document what the variable is used for.

Do It Yourself!

Let’s compute the volume of a cube with side \(s = 5\) . Remember that the volume of a cube is defined as \(v = s^3\) . Assign the value to a variable called volume .

Well done! Now, why don’t you print the result in a message? It can say something like “The volume of the cube with side 5 is \(volume\) ”.

Beware that there is no checking of types ( type checking ) in Python, so a variable to which you have assigned an integer may be re-used as a float, even if we provide type-hints .

Names and Keywords #

Names of variable and other language constructs such as functions (we will cover this topic later), should be meaningful and reflect the purpose of the construct.

In general, Python names should adhere to the following rules:

It should start with a letter or underscore.

It cannot start with a number.

It must only contain alpha-numeric (i.e., letters a-z A-Z and digits 0-9) characters and underscores.

They cannot share the name of a Python keyword.

If you use illegal variable names you will get a syntax error.

By choosing the right variables names you make the code self-documenting, what is better the variable v or velocity ?

The following are examples of invalid variable names.

These basic development principles are sometimes called architectural rules . By defining and agreeing upon architectural rules you make it easier for you and your fellow developers to understand and modify your code.

If you want to read more on this, please have a look at Code complete a book by Steven McConnell [ McC04 ] .

Every programming language has a collection of reserved keywords . They are used in predefined language constructs, such as loops and conditionals . These language concepts and their usage will be explained later.

The interpreter uses keywords to recognize these language constructs in a program. Python 3 has the following keywords:

False class finally is return

None continue for lambda try

True def from nonlocal while

and del global not with

as elif if or yield

assert else import pass break

except in raise

Reassignments #

It is allowed to assign a new value to an existing variable. This process is called reassignment . As soon as you assign a value to a variable, the old value is lost.

The assignment of a variable to another variable, for instance b = a does not imply that if a is reassigned then b changes as well.

You have a variable salary that shows the weekly salary of an employee. However, you want to compute the monthly salary. Can you reassign the value to the salary variable according to the instruction?

Updating Variables #

A frequently used reassignment is for updating puposes: the value of a variable depends on the previous value of the variable.

This statement expresses “get the current value of x , add one, and then update x with the new value.”

Beware, that the variable should be initialized first, usually with a simple assignment.

Do you remember the salary excercise of the previous section (cf. 13. Reassignments)? Well, if you have not done it yet, update the salary variable by using its previous value.

Updating a variable by adding 1 is called an increment ; subtracting 1 is called a decrement . A shorthand way of doing is using += and -= , which stands for x = x + ... and x = x - ... respectively.

Order of Operations #

Expressions may contain multiple operators. The order of evaluation depends on the priorities of the operators also known as rules of precedence .

For mathematical operators, Python follows mathematical convention. The acronym PEMDAS is a useful way to remember the rules:

Parentheses have the highest precedence and can be used to force an expression to evaluate in the order you want. Since expressions in parentheses are evaluated first, 2 * (3 - 1) is 4 , and (1 + 1)**(5 - 2) is 8 . You can also use parentheses to make an expression easier to read, even if it does not change the result.

Exponentiation has the next highest precedence, so 1 + 2**3 is 9 , not 27 , and 2 * 3**2 is 18 , not 36 .

Multiplication and division have higher precedence than addition and subtraction . So 2 * 3 - 1 is 5 , not 4 , and 6 + 4 / 2 is 8 , not 5 .

Operators with the same precedence are evaluated from left to right (except exponentiation). So in the expression degrees / 2 * pi , the division happens first and the result is multiplied by pi . To divide by 2π, you can use parentheses or write: degrees / 2 / pi .

In case of doubt, use parentheses!

Let’s see what happens when we evaluate the following expressions. Just run the cell to check the resulting value.

Floor Division and Modulus Operators #

The floor division operator // divides two numbers and rounds down to an integer.

For example, suppose that driving to the south of France takes 555 minutes. You might want to know how long that is in hours.

Conventional division returns a floating-point number.

Hours are normally not represented with decimal points. Floor division returns the integer number of hours, dropping the fraction part.

You spend around 225 minutes every week on programming activities. You want to know around how many hours you invest to this activity during a month. Use the \(//\) operator to give the answer.

The modulus operator % works on integer values. It computes the remainder when dividing the first integer by the second one.

The modulus operator is more useful than it seems.

For example, you can check whether one number is divisible by another—if x % y is zero, then x is divisible by y .

String Operations #

In general, you cannot perform mathematical operations on strings, even if the strings look like numbers, so the following operations are illegal: '2'-'1' 'eggs'/'easy' 'third'*'a charm'

But there are two exceptions, + and * .

The + operator performs string concatenation, which means it joins the strings by linking them end-to-end.

The * operator also works on strings; it performs repetition.

Speedy Gonzales is a cartoon known to be the fastest mouse in all Mexico . He is also famous for saying “Arriba Arriba Andale Arriba Arriba Yepa”. Can you use the following variables, namely arriba , andale and yepa to print the mentioned expression? Don’t forget to use the string operators.

Asking the User for Input #

The programs we have written so far accept no input from the user.

To get data from the user through the Python prompt, we can use the built-in function input .

When input is called your whole program stops and waits for the user to enter the required data. Once the user types the value and presses Return or Enter , the function returns the input value as a string and the program continues with its execution.

Try it out!

You can also print a message to clarify the purpose of the required input as follows.

The resulting string can later be translated to a different type, like an integer or a float. To do so, you use the functions int and float , respectively. But be careful, the user might introduce a value that cannot be converted to the type you required.

We want to know the name of a user so we can display a welcome message in our program. The message should say something like “Hello \(name\) , welcome to our hello world program!”.

Script Mode #

So far we have run Python in interactive mode in these Jupyter notebooks, which means that you interact directly with the interpreter in the code cells . The interactive mode is a good way to get started, but if you are working with more than a few lines of code, it can be clumsy. The alternative is to save code in a file called a script and then run the interpreter in script mode to execute the script. By convention, Python scripts have names that end with .py .

Use the PyCharm icon in Anaconda Navigator to create and execute stand-alone Python scripts. Later in the course, you will have to work with Python projects for the assignments, in order to get acquainted with another way of interacing with Python code.

This Jupyter Notebook is based on Chapter 2 of the books Python for Everybody [ Sev16 ] and Think Python (Sections 5.1, 7.1, 7.2, and 5.12) [ Dow15 ] .

5. Assignment Expressions

By Bernd Klein . Last modified: 30 Nov 2023.

On this page ➤

Introduction

walrus

This section in our Python tutorial explores the "assignment expression operator" affectionately known to many as "the walrus operator. So, you can call it either the "walrus operator" or the "assignment expression operator," and people in the Python programming community will generally understand what you mean. Despite being introduced in Python version 3.8, we can still view it as a relatively recent addition to the language. The assignment expressions have been discussed in PEP 572 and this is what was written about the naming:

The purpose of this feature is not a new way to assign objects to variables, but it gives programmers a convenient way to assign variables in the middle of expressions.

You might still be curious about the origin of the term "walrus operator." It's affectionately named this way because, with a touch of imagination, the operator's characters resemble the eyes and tusks of a walrus.

We will introduce the assignment expression by comparing it first with a simple assignment statement. A simple assignment statement can also be replaced by an assignment expression, even though it looks clumsy and is definitely not the intended use case of it:

First you may wonder about the brackets, when you look at the assignment expression. It is not meant as a replacement for the simple "assignment statement". Its primary role is to be utilized within expressions

The following code shows a proper usecase:

Though you may argue that the following code might be even clearer:

Here's another Python code example that demonstrates a similar scenario. First with the walrus operator:

Now without the walrus operator:

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

See: Live Python courses overview

Beneficial applications

Now, let's explore some examples where the usage of the assignment expression is really beneficial compared to code not using it.

You can observe that if we choose to avoid using the assignment expression in the list comprehension, we would need to call the function twice. So using the assgment expression makes the code in this context more efficient.

Use Case: Regular Expressions

There is also a great advantage when we use regular expressions. Like in our previous examples we present again two similiar code snippets, the first one without and the second one with the walrus operator:

Now, the code using assignment expressions:

Assignment Expression in File Reading

Fixed-width files.

Files in which each line has the same length are often referred to as "fixed-width" or "fixed-length" files. In these files, each line (or record) is structured so that it contains a predefined number of characters, and the fields within the lines have fixed positions. The files can be read by using the read -method and the walrus operator:

Now the same in traditional coding style:

The benefits for the most recent code snippet are

  • It uses a more traditional coding style, which may be more familiar to some developers.
  • It assigns and uses the data variable explicitly, making the code easier to understand for those not familiar with assignment expressions.

However, a significant drawback of this approach is that it requires the explicit assignment and reassignment of the data variable, which can be seen as less concise and somewhat less elegant.

Using readline

Most people use a for loop to iterator over a text file line by line. With the walrus operator, we can also elegantly go through a text using the method readline :

Code snippet using readline but not the assignment expression:

Now with the walrus operator:

Another Use Case

In the chapter on while loops of our Python Tutorial, we had a little number guessing game:

As you can see, we had to initialize guess to zero to be able to enter the loop. We can do the initialization directly in the loop condition with an assignment expression and simplify the whole code by this:

Critiques of the Walrus Operator in Python

the Python walrus

We said in the beginning of this page that some Python programmers longed for this construct for quite a while. One reason why it was not introduced earlier was the fact that it can also be used to write code which is less readable. In fact, the application of the walrus operator contradicts several principles highlighted in the Zen of Python. To grasp these contraventions, let's delve into the ensuing scenarios.

The Zen of Python says "Explicit is better than implicit". The walrus operator violates this requirement of the Zen of Python. This means that explicit operations are always better than implicit operations. The walrus operator assigns a value to a variable and implicitly returns the value as well. Therefore, it does not align with the concept of "Explicit is better than implicit."

The following code snippet is shows an extreme example which is not recommended to use:

What are your thoughts on the following piece of code using the Python assignment operator inside of a print function call?

These Python code snipets certainly violate two other demands of the Zen of Python:

  • Beautiful is Better Than Ugly
  • Complex is Better Than Complicated

The principle "There should be one and preferably only one obvious way to do it" from the Zen of Python emphasizes the importance of having a clear and singular approach. When we have the option to separate assignment and return operations into distinct statements, opting for the less readable and more complex walrus operator contradicts this principle.

Upcoming online Courses

On this page

  • Hands-on Python Tutorial »
  • 1. Beginning With Python »

1.6. Variables and Assignment ¶

Each set-off line in this section should be tried in the Shell.

Nothing is displayed by the interpreter after this entry, so it is not clear anything happened. Something has happened. This is an assignment statement , with a variable , width , on the left. A variable is a name for a value. An assignment statement associates a variable name on the left of the equal sign with the value of an expression calculated from the right of the equal sign. Enter

Once a variable is assigned a value, the variable can be used in place of that value. The response to the expression width is the same as if its value had been entered.

The interpreter does not print a value after an assignment statement because the value of the expression on the right is not lost. It can be recovered if you like, by entering the variable name and we did above.

Try each of the following lines:

The equal sign is an unfortunate choice of symbol for assignment, since Python’s usage is not the mathematical usage of the equal sign. If the symbol ↤ had appeared on keyboards in the early 1990’s, it would probably have been used for assignment instead of =, emphasizing the asymmetry of assignment. In mathematics an equation is an assertion that both sides of the equal sign are already, in fact, equal . A Python assignment statement forces the variable on the left hand side to become associated with the value of the expression on the right side. The difference from the mathematical usage can be illustrated. Try:

so this is not equivalent in Python to width = 10 . The left hand side must be a variable, to which the assignment is made. Reversed, we get a syntax error . Try

This is, of course, nonsensical as mathematics, but it makes perfectly good sense as an assignment, with the right-hand side calculated first. Can you figure out the value that is now associated with width? Check by entering

In the assignment statement, the expression on the right is evaluated first . At that point width was associated with its original value 10, so width + 5 had the value of 10 + 5 which is 15. That value was then assigned to the variable on the left ( width again) to give it a new value. We will modify the value of variables in a similar way routinely.

Assignment and variables work equally well with strings. Try:

Try entering:

Note the different form of the error message. The earlier errors in these tutorials were syntax errors: errors in translation of the instruction. In this last case the syntax was legal, so the interpreter went on to execute the instruction. Only then did it find the error described. There are no quotes around fred , so the interpreter assumed fred was an identifier, but the name fred was not defined at the time the line was executed.

It is both easy to forget quotes where you need them for a literal string and to mistakenly put them around a variable name that should not have them!

Try in the Shell :

There fred , without the quotes, makes sense.

There are more subtleties to assignment and the idea of a variable being a “name for” a value, but we will worry about them later, in Issues with Mutable Objects . They do not come up if our variables are just numbers and strings.

Autocompletion: A handy short cut. Idle remembers all the variables you have defined at any moment. This is handy when editing. Without pressing Enter, type into the Shell just

Assuming you are following on the earlier variable entries to the Shell, you should see f autocompleted to be

This is particularly useful if you have long identifiers! You can press Alt-/ several times if more than one identifier starts with the initial sequence of characters you typed. If you press Alt-/ again you should see fred . Backspace and edit so you have fi , and then and press Alt-/ again. You should not see fred this time, since it does not start with fi .

1.6.1. Literals and Identifiers ¶

Expressions like 27 or 'hello' are called literals , coming from the fact that they literally mean exactly what they say. They are distinguished from variables, whose value is not directly determined by their name.

The sequence of characters used to form a variable name (and names for other Python entities later) is called an identifier . It identifies a Python variable or other entity.

There are some restrictions on the character sequence that make up an identifier:

The characters must all be letters, digits, or underscores _ , and must start with a letter. In particular, punctuation and blanks are not allowed.

There are some words that are reserved for special use in Python. You may not use these words as your own identifiers. They are easy to recognize in Idle, because they are automatically colored orange. For the curious, you may read the full list:

There are also identifiers that are automatically defined in Python, and that you could redefine, but you probably should not unless you really know what you are doing! When you start the editor, we will see how Idle uses color to help you know what identifies are predefined.

Python is case sensitive: The identifiers last , LAST , and LaSt are all different. Be sure to be consistent. Using the Alt-/ auto-completion shortcut in Idle helps ensure you are consistent.

What is legal is distinct from what is conventional or good practice or recommended. Meaningful names for variables are important for the humans who are looking at programs, understanding them, and revising them. That sometimes means you would like to use a name that is more than one word long, like price at opening , but blanks are illegal! One poor option is just leaving out the blanks, like priceatopening . Then it may be hard to figure out where words split. Two practical options are

  • underscore separated: putting underscores (which are legal) in place of the blanks, like price_at_opening .
  • using camel-case : omitting spaces and using all lowercase, except capitalizing all words after the first, like priceAtOpening

Use the choice that fits your taste (or the taste or convention of the people you are working with).

Table Of Contents

  • 1.6.1. Literals and Identifiers

Previous topic

1.5. Strings, Part I

1.7. Print Function, Part I

  • Show Source

Quick search

Enter search terms or a module, class or function name.

Mouse Vs Python

Python 101 – Assignment Expressions

Assignment expressions were added to Python in version 3.8 . The general idea is that an assignment expression allows you to assign to variables within an expression.

The syntax for doing this is:

This operator has been called the “walrus operator”, although their real name is “assignment expression”. Interestingly, the CPython internals also refer to them as “named expressions”.

You can read all about assignment expressions in PEP 572 . Let’s find out how to use assignment expressions!

Using Assignment Expressions

Assignment expressions are still relatively rare. However, you need to know about assignment expressions because you will probably come across them from time to time. PEP 572 has some good examples of assignment expressions.

In these 3 examples, you are creating a variable in the expression statement itself. The first example creates the variable match by assigning it the result of the regex pattern search. The second example assigns the variable value to the result of calling a function in the while loop’s expression. Finally, you assign the result of calling f(x) to the variable y inside of a list comprehension.

It would probably help to see the difference between code that doesn’t use an assignment expression and one that does. Here’s an example of reading a file in chunks:

This code will open up a file of indeterminate size and process it 1024 bytes at a time. You will find this useful when working with very large files as it prevents you from loading the entire file into memory. If you do, you can run out of memory and cause your application or even the computer to crash.

You can shorten this code up a bit by using an assignment expression:

Here you assign the result of the read() to data within the while loop’s expression. This allows you to then use that variable inside of the while loop’s code block. It also checks that some data was returned so you don’t have to have the if not data: break stanza.

Another good example that is mentioned in PEP 572 is taken from Python’s own site.py . Here’s how the code was originally:

And this is how it could be simplified by using an assignment expression:

You move the assignment into the conditional statement’s expression, which shortens the code up nicely.

Now let’s discover some of the situations where assignment expressions can’t be used.

What You Cannot Do With Assignment Expressions

There are several cases where assignment expressions cannot be used.

One of the most interesting features of assignment expressions is that they can be used in contexts that an assignment statement cannot, such as in a lambda or the previously mentioned comprehension. However, they do NOT support some things that assignment statements can do. For example, you cannot do multiple target assignment:

Another prohibited use case is using an assignment expression at the top level of an expression statement. Here is an example from PEP 572:

There is a detailed list of other cases where assignment expressions are prohibited or discouraged in the PEP. You should check that document out if you plan to use assignment expressions often.

Wrapping Up

Assignment expressions are an elegant way to clean up certain parts of your code. The feature’s syntax is kind of similar to type hinting a variable. Once you have the hang of one, the other should become easier to do as well.

In this article, you saw some real-world examples of using the “walrus operator”. You also learned when assignment expressions shouldn’t be used. This syntax is only available in Python 3.8 or newer, so if you happen to be forced to use an older version of Python, this feature won’t be of much use to you.

Related Reading

This article is based on a chapter from Python 101, 2nd Edition , which you can purchase on Leanpub or Amazon .

If you’d like to learn more Python, then check out these tutorials:

Python 101 – How to Work with Images

Python 101 – Documenting Your Code

Python 101: An Intro to Working with JSON

Python 101 – Creating Multiple Processes

Welcome! This site is currently in beta. Get 10% off everything with promo code BETA10.

Python Expressions vs. Python Statements

  • Introduction

Python Expressions Example

Python statement example, expressions and statements in a program, summary: expressions and statements.

You've heard about expressions and statements before, maybe without paying that much attention to them. When you're reading through online programming tutorials and blog posts, you might keep encountering those two terms frequently.

In this lesson, you'll learn about the difference between an expression and a statement.

Python Expressions

Expressions are a combination of programming logic that evaluates to a result . Adding two numbers together is an expression because it results in the sum of the two numbers.

In the code snippet above, you can see that 2 + 3 has a result, 5 . When you execute the expression 2 + 3 , you'll hold a value in your digital hand. This makes 2 + 3 an expression.

Python Statements

  • Statements are a more general term for all sorts of pieces that make up your code. In programming, statements are the building blocks that constitute a program.

Programs are built of consecutive statements that are ultimately combined to produce a result. A statement is made up of components that can be other statements as well as expressions.

An example of a statement is the variable assignment:

In this variable assignment, you're creating a reference between the variable x and the value 2 .

This statement doesn't have a computed result, contrary to an expression like the one shown further up. All you do is assign a value to a variable.

In any complex program that you'll write, you'll use both expressions and statements together. A basic example of a statement that comes in combination with an expression is:

If you pick this short code snippet apart, you'll see that while the complete line of code is a statement, it also contains an expression:

  • Expression : On the right side of the statement, there is an expression, 2 + 3 , that evaluates to the integer 5 .
  • Statement : The statement x = expression saves the value of the expression 2 + 3 in the variable x .

Your code will be made up of both, but sometimes, it's helpful to be able to make a distinction between the two. When?

Colorful illustration of a light bulb

Info: Keep training in the habit of researching any questions you might have. It's extremely important for your continuous learning journey. The skill of answering your own questions through effective online research will stay relevant throughout your work as a software developer.

One example of where the distinction between statements and expressions is important is when passing arguments to functions. You can pass an expression as an argument to a function, but you can't pass a statement as an argument.

Additional Resources

  • StackOverflow: What is the Difference Between an Expression and a Statement in Python
  • Expressions are a combination of programming logic that evaluates to a result.
  • As a rule of thumb, you can remember that expressions evaluate to a result and statements don't.

Statement vs Expression – What's the Difference in Programming?

Ogundiran Ayobami

Learning the syntax of a programming language is key if you want to use that language effectively. This is true for both new and experienced developers.

And one of the most important things to pay attention to while learning a programming language is whether the code you're dealing with is a statement or an expression.

It can sometimes be confusing to differentiate between statements and expressions in programming. So this article is meant to simplify the differences so that you can improve your programming skills and become a better developer.

What is an Expression in Programming?

Senior caucasian man holding blank empty banner covering mouth with hand, shocked and afraid for mistake. surprised expression

An expression is any word or group of words or symbols that is a value. In programming, an expression is a value, or anything that executes and ends up being a value.

It is necessary to understand that a value is unique. For example, const , let , 2 , 4 , s , a , true , false , and world are values because each of them is unique in meaning or character.

Let's look at some code as an example:

Judging from the code above, const , price , = , and 500 are expressions because each of them has a definite and unique meaning or value. But if we take all of them together const price = 500 - then we have a statement.

Let's look at another example:

Looking at the code above, you can see an anonymous function is assigned to a variable. Oh, wait! You might know that any function is a statement. Can it also be an expression?

Yes! A "function" and a "class" are both statements and expressions because they can perform actions (do or not do tasks) and still execute to a value.

This brings us to statements – so what are they?

What is a Statement in Programming?

A statement is a group of expressions and/or statements that you design to carry out a task or an action.

Statements are two-sided – that is, they either do tasks or don't do them. Any statement that can return a value is automatically qualified to be used as an expression. That is why a function or class is a statement and also an expression in JavaScript.

If you look at the example of the function under the section on expressions, you can see it is assigned and execute to a value passed to a variable. That is why it is an expression in that case.

Examples of Statements in Programming

Inline statements.

The whole of the code above is a statement because it carries out the task of assigning $2000 to amount . It is safe to say a line of code is a statement because most compilers or interpreters don't execute any standalone expression.

Happy man portraits

Block statements

Look at the below if statement:

The if statement is a statement because it helps us check whether I love you or not. As I have said before, it is two-sided: this code finds out whether "I love you" or not, and that is why it is a statement. Also, it doesn't return any value but it can create side effects.

Here's a loop statement:

In short, any loop is a statement because if it can only do the tasks it is meant to do or not – does loop and doesn't loop. But a loop can't execute to a value in the end. They can only have side effects in JavaScript. Once they can execute to a value in a programming language, then they can also be used as an expression.

For example, you can use forloop and if statement as expressions in Python.

There is also an "IF" expression in Python. That means that something that is a statement in one language can be an expression (or both statement and expression) in another.

Look at the below function statement:

We declare the function add(firstNumber, secondNumber) and it returns a value. The function is called with two arguments as in add(2, 3) by declaration and so it is a statement. If you pay close attention, you will realize that calling the function as a statement is useless since it has no side effect.

Hey, stop! How can we turn it into an expression? Oh yeah, we can do it like this:

Though the function is now an expression the way it is called above, the whole of the code is still a statement.

Check out this class statement:

You can see that we declare the class "Person" and instantiate and assign it to "User" immediately. So, it is used as an expression.

Now, let's use it as a statement:

A class is similar to a function in the sense that it can be declared, assigned, or used as an operand just like a class. So, a class is a statement and/or an expression.

The Main Differences Between an Expression and a Statement in Programming

Expressions can be assigned or used as operands, while statements can only be declared.

Statements create side effects to be useful, while expressions are values or execute to values.

Expressions are unique in meaning, while statements are two-sided in execution. For example, 1 has a certain value while go( ) may be executed or not.

Statements are the whole structure, while expressions are the building blocks. For example, a line or a block of code is a statement.

Why You Should Know the Difference

First of all, understanding the difference between statements and expressions should make learning new programming languages less surprising. If you're used to JavaScript, you may be surprised by Python's ability to assign an if statement as a variable which is not possible in JavaScript.

Second, it makes it easy to use programming paradigms across different programming languages.

For example, a JavaScript "if statement" cannot be used as an expression because it can't execute to a value – it can only create side effects. Yet, you can use the ternary operator if you want to avoid the side effects of using an if statement in JavaScript.

For this reason, you can understand why some programmers avoid if statements by using the ternary operator in JavaScript. It is because they want to avoid side effects .

It also makes your realize why you have to be always careful about the scope of your variables whenever you use a statement. This is true because statements mostly have side effects to be useful, and it is reasonable to understand the scope of your variables and operations. For example,

Hey wait! What would be logged in the console if you ran the code above?

Tell yourself the answer first and then paste the code in the console to confirm. If you you're wrong, you need to learn more about scope and side effects. But if you're right, try to make those functions a bit better to avoid the confusion they may generate.

Knowing the difference also helps you to easily identify non-composable and composable syntaxes (functions, classes, modules, and so on) of a programming language. This makes porting your experience from one programming language to another more interesting and direct.

Wrapping Up

Now that you understand the difference between expressions and statements in programming, and you know why understanding the differences is important, you can identify pieces of code as expressions or statements while coding.

Next time, we'll go even further and help make learning a second programming language easier.

Go and get things done now! See you soon.

I am planning to share a lot about programming tips and tutorials in 2023. If you're struggling to build projects or you want to stay connected with my write-ups and videos, please join my list at YouTooCanCode or subscribe to my YouTube channel at You Too Can Code on YouTube .

Ayobami loves writing history with JavaScript(React) and PHP(Laravel). He has been making programming fun to learn for learners. Check him out on YouTube: https://bit.ly/3usOu3s

If you read this far, thank the author to show them you care. Say Thanks

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started

Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity, Membership, Bitwise

Operators are special symbols that perform some operation on operands and returns the result. For example, 5 + 6 is an expression where + is an operator that performs arithmetic add operation on numeric left operand 5 and the right side operand 6 and returns a sum of two operands as a result.

Python includes the operator module that includes underlying methods for each operator. For example, the + operator calls the operator.add(a,b) method.

Above, expression 5 + 6 is equivalent to the expression operator.add(5, 6) and operator.__add__(5, 6) . Many function names are those used for special methods, without the double underscores (dunder methods). For backward compatibility, many of these have functions with the double underscores kept.

Python includes the following categories of operators:

Arithmetic Operators

Assignment operators, comparison operators, logical operators, identity operators, membership test operators, bitwise operators.

Arithmetic operators perform the common mathematical operation on the numeric operands.

The arithmetic operators return the type of result depends on the type of operands, as below.

  • If either operand is a complex number, the result is converted to complex;
  • If either operand is a floating point number, the result is converted to floating point;
  • If both operands are integers, then the result is an integer and no conversion is needed.

The following table lists all the arithmetic operators in Python:

The assignment operators are used to assign values to variables. The following table lists all the arithmetic operators in Python:

The comparison operators compare two operands and return a boolean either True or False. The following table lists comparison operators in Python.

The logical operators are used to combine two boolean expressions. The logical operations are generally applicable to all objects, and support truth tests, identity tests, and boolean operations.

The identity operators check whether the two objects have the same id value e.i. both the objects point to the same memory location.

The membership test operators in and not in test whether the sequence has a given item or not. For the string and bytes types, x in y is True if and only if x is a substring of y .

Bitwise operators perform operations on binary operands.

  • Compare strings in Python
  • Convert file data to list
  • Convert User Input to a Number
  • Convert String to Datetime in Python
  • How to call external commands in Python?
  • How to count the occurrences of a list item?
  • How to flatten list in Python?
  • How to merge dictionaries in Python?
  • How to pass value by reference in Python?
  • Remove duplicate items from list in Python
  • More Python articles

python expression vs assignment statement

We are a team of passionate developers, educators, and technology enthusiasts who, with their combined expertise and experience, create in -depth, comprehensive, and easy to understand tutorials.We focus on a blend of theoretical explanations and practical examples to encourages hands - on learning. Visit About Us page for more information.

  • Python Questions & Answers
  • Python Skill Test
  • Python Latest Articles
  • Python Basics
  • Interview Questions
  • Python Quiz
  • Popular Packages
  • Python Projects
  • Practice Python
  • AI With Python
  • Learn Python3
  • Python Automation
  • Python Web Dev
  • DSA with Python
  • Python OOPs
  • Dictionaries

Python Operators

Precedence and associativity of operators in python.

  • Python Arithmetic Operators
  • Difference between / vs. // operator in Python
  • Python - Star or Asterisk operator ( * )
  • What does the Double Star operator mean in Python?
  • Division Operators in Python
  • Modulo operator (%) in Python
  • Python Logical Operators
  • Python OR Operator
  • Difference between 'and' and '&' in Python
  • not Operator in Python | Boolean Logic

Ternary Operator in Python

  • Python Bitwise Operators

Python Assignment Operators

Assignment operators in python.

  • Walrus Operator in Python 3.8
  • Increment += and Decrement -= Assignment Operators in Python
  • Merging and Updating Dictionary Operators in Python 3.9
  • New '=' Operator in Python3.8 f-string

Python Relational Operators

  • Comparison Operators in Python
  • Python NOT EQUAL operator
  • Difference between == and is operator in Python
  • Chaining comparison operators in Python
  • Python Membership and Identity Operators
  • Difference between != and is not operator in Python

In Python programming, Operators in general are used to perform operations on values and variables. These are standard symbols used for logical and arithmetic operations. In this article, we will look into different types of Python operators. 

  • OPERATORS: These are the special symbols. Eg- + , * , /, etc.
  • OPERAND: It is the value on which the operator is applied.

Types of Operators in Python

  • Arithmetic Operators
  • Comparison Operators
  • Logical Operators
  • Bitwise Operators
  • Assignment Operators
  • Identity Operators and Membership Operators

Python Operators

Arithmetic Operators in Python

Python Arithmetic operators are used to perform basic mathematical operations like addition, subtraction, multiplication , and division .

In Python 3.x the result of division is a floating-point while in Python 2.x division of 2 integers was an integer. To obtain an integer result in Python 3.x floored (// integer) is used.

Example of Arithmetic Operators in Python

Division operators.

In Python programming language Division Operators allow you to divide two numbers and return a quotient, i.e., the first number or number at the left is divided by the second number or number at the right and returns the quotient. 

There are two types of division operators: 

Float division

  • Floor division

The quotient returned by this operator is always a float number, no matter if two numbers are integers. For example:

Example: The code performs division operations and prints the results. It demonstrates that both integer and floating-point divisions return accurate results. For example, ’10/2′ results in ‘5.0’ , and ‘-10/2’ results in ‘-5.0’ .

Integer division( Floor division)

The quotient returned by this operator is dependent on the argument being passed. If any of the numbers is float, it returns output in float. It is also known as Floor division because, if any number is negative, then the output will be floored. For example:

Example: The code demonstrates integer (floor) division operations using the // in Python operators . It provides results as follows: ’10//3′ equals ‘3’ , ‘-5//2’ equals ‘-3’ , ‘ 5.0//2′ equals ‘2.0’ , and ‘-5.0//2’ equals ‘-3.0’ . Integer division returns the largest integer less than or equal to the division result.

Precedence of Arithmetic Operators in Python

The precedence of Arithmetic Operators in Python is as follows:

  • P – Parentheses
  • E – Exponentiation
  • M – Multiplication (Multiplication and division have the same precedence)
  • D – Division
  • A – Addition (Addition and subtraction have the same precedence)
  • S – Subtraction

The modulus of Python operators helps us extract the last digit/s of a number. For example:

  • x % 10 -> yields the last digit
  • x % 100 -> yield last two digits

Arithmetic Operators With Addition, Subtraction, Multiplication, Modulo and Power

Here is an example showing how different Arithmetic Operators in Python work:

Example: The code performs basic arithmetic operations with the values of ‘a’ and ‘b’ . It adds (‘+’) , subtracts (‘-‘) , multiplies (‘*’) , computes the remainder (‘%’) , and raises a to the power of ‘b (**)’ . The results of these operations are printed.

Note: Refer to Differences between / and // for some interesting facts about these two Python operators.

Comparison of Python Operators

In Python Comparison of Relational operators compares the values. It either returns True or False according to the condition.

= is an assignment operator and == comparison operator.

Precedence of Comparison Operators in Python

In Python, the comparison operators have lower precedence than the arithmetic operators. All the operators within comparison operators have the same precedence order.

Example of Comparison Operators in Python

Let’s see an example of Comparison Operators in Python.

Example: The code compares the values of ‘a’ and ‘b’ using various comparison Python operators and prints the results. It checks if ‘a’ is greater than, less than, equal to, not equal to, greater than, or equal to, and less than or equal to ‘b’ .

Logical Operators in Python

Python Logical operators perform Logical AND , Logical OR , and Logical NOT operations. It is used to combine conditional statements.

Precedence of Logical Operators in Python

The precedence of Logical Operators in Python is as follows:

  • Logical not
  • logical and

Example of Logical Operators in Python

The following code shows how to implement Logical Operators in Python:

Example: The code performs logical operations with Boolean values. It checks if both ‘a’ and ‘b’ are true ( ‘and’ ), if at least one of them is true ( ‘or’ ), and negates the value of ‘a’ using ‘not’ . The results are printed accordingly.

Bitwise Operators in Python

Python Bitwise operators act on bits and perform bit-by-bit operations. These are used to operate on binary numbers.

Precedence of Bitwise Operators in Python

The precedence of Bitwise Operators in Python is as follows:

  • Bitwise NOT
  • Bitwise Shift
  • Bitwise AND
  • Bitwise XOR

Here is an example showing how Bitwise Operators in Python work:

Example: The code demonstrates various bitwise operations with the values of ‘a’ and ‘b’ . It performs bitwise AND (&) , OR (|) , NOT (~) , XOR (^) , right shift (>>) , and left shift (<<) operations and prints the results. These operations manipulate the binary representations of the numbers.

Python Assignment operators are used to assign values to the variables.

Let’s see an example of Assignment Operators in Python.

Example: The code starts with ‘a’ and ‘b’ both having the value 10. It then performs a series of operations: addition, subtraction, multiplication, and a left shift operation on ‘b’ . The results of each operation are printed, showing the impact of these operations on the value of ‘b’ .

Identity Operators in Python

In Python, is and is not are the identity operators both are used to check if two values are located on the same part of the memory. Two variables that are equal do not imply that they are identical. 

Example Identity Operators in Python

Let’s see an example of Identity Operators in Python.

Example: The code uses identity operators to compare variables in Python. It checks if ‘a’ is not the same object as ‘b’ (which is true because they have different values) and if ‘a’ is the same object as ‘c’ (which is true because ‘c’ was assigned the value of ‘a’ ).

Membership Operators in Python

In Python, in and not in are the membership operators that are used to test whether a value or variable is in a sequence.

Examples of Membership Operators in Python

The following code shows how to implement Membership Operators in Python:

Example: The code checks for the presence of values ‘x’ and ‘y’ in the list. It prints whether or not each value is present in the list. ‘x’ is not in the list, and ‘y’ is present, as indicated by the printed messages. The code uses the ‘in’ and ‘not in’ Python operators to perform these checks.

in Python, Ternary operators also known as conditional expressions are operators that evaluate something based on a condition being true or false. It was added to Python in version 2.5. 

It simply allows testing a condition in a single line replacing the multiline if-else making the code compact.

Syntax :   [on_true] if [expression] else [on_false] 

Examples of Ternary Operator in Python

The code assigns values to variables ‘a’ and ‘b’ (10 and 20, respectively). It then uses a conditional assignment to determine the smaller of the two values and assigns it to the variable ‘min’ . Finally, it prints the value of ‘min’ , which is 10 in this case.

In Python, Operator precedence and associativity determine the priorities of the operator.

Operator Precedence in Python

This is used in an expression with more than one operator with different precedence to determine which operation to perform first.

Let’s see an example of how Operator Precedence in Python works:

Example: The code first calculates and prints the value of the expression 10 + 20 * 30 , which is 610. Then, it checks a condition based on the values of the ‘name’ and ‘age’ variables. Since the name is “ Alex” and the condition is satisfied using the or operator, it prints “Hello! Welcome.”

Operator Associativity in Python

If an expression contains two or more operators with the same precedence then Operator Associativity is used to determine. It can either be Left to Right or from Right to Left.

The following code shows how Operator Associativity in Python works:

Example: The code showcases various mathematical operations. It calculates and prints the results of division and multiplication, addition and subtraction, subtraction within parentheses, and exponentiation. The code illustrates different mathematical calculations and their outcomes.

To try your knowledge of Python Operators, you can take out the quiz on Operators in Python . 

Python Operator Exercise Questions

Below are two Exercise Questions on Python Operators. We have covered arithmetic operators and comparison operators in these exercise questions. For more exercises on Python Operators visit the page mentioned below.

Q1. Code to implement basic arithmetic operations on integers

Q2. Code to implement Comparison operations on integers

Explore more Exercises: Practice Exercise on Operators in Python

Please Login to comment...

Similar reads.

  • python-basics
  • Python-Operators

Improve your Coding Skills with Practice

 alt=

What kind of Experience do you want to share?

IMAGES

  1. What are Python Assignment Expressions and Using the Walrus Operator

    python expression vs assignment statement

  2. Assigning multiple variables in one line in Python

    python expression vs assignment statement

  3. 2.10. Statements and Expressions

    python expression vs assignment statement

  4. Python Assignment Statement and Types

    python expression vs assignment statement

  5. TYPES OF ASSIGNMENT STATEMENTS IN PYTHON

    python expression vs assignment statement

  6. Expressions and Statements Python

    python expression vs assignment statement

VIDEO

  1. Python Tutorial : Conditional Statement

  2. how to use the if statement in python.pt1 of python

  3. Topic 6: Python if else statement

  4. Python Statements and Python Comments💬 #ai #python #datascience #languagelearning #programming

  5. Python statement and expression #python #coding #programming #reels

  6. python expressions and statements |comments in python| escaping characters| python course|#part 2.1

COMMENTS

  1. What is the difference between an expression and a statement in Python

    An expression tells the interpreter that something needs to be evaluated, calculated, reduced, etc. for example: >>>>5 + 5. A statement does not. Think of the statement as the block of code, that instructs the interpreter to do something (besides evaluation). So as a simple example, x = 5 + 5.

  2. Is variable assignment a statement or expression?

    0. Roughly speaking, in C; A statement is a section of code that produces an observable effect; An expression is a code construct which accesses at least one data item, performs some operation of the result of that access (or those accesses), and produces at least one result. An expression may be composed of (smaller) expressions.

  3. Python's Assignment Operator: Write Robust Assignments

    Here, variable represents a generic Python variable, while expression represents any Python object that you can provide as a concrete value—also known as a literal—or an expression that evaluates to a value. To execute an assignment statement like the above, Python runs the following steps: Evaluate the right-hand expression to produce a concrete value or object.

  4. PEP 572

    Unparenthesized assignment expressions are prohibited at the top level of the right hand side of an assignment statement. Example: y0 = y1 := f(x) # INVALID y0 = (y1 := f(x)) # Valid, though discouraged. Again, this rule is included to avoid two visually similar ways of saying the same thing.

  5. Operators and Expressions in Python

    Even though all expressions are statements, not all statements are expressions. For example, pure assignment statements don't return any value, as you'll learn in a moment. Therefore, they're not expressions. The assignment operator is a special operator that doesn't create an expression but a statement.

  6. Expression and Statement

    Evaluation. Evaluation refers to the process that Python interpreter generates the final object of an expression. Expressions have a common characteristic: the evaluation of the code construct generates a single object. An object may have a single value such as 3 or multiple values like a list or a tuple. An expression may have nested expression.

  7. Assignment Expressions: The Walrus Operator

    In this lesson, you'll learn about the biggest change in Python 3.8: the introduction of assignment expressions.Assignment expression are written with a new notation (:=).This operator is often called the walrus operator as it resembles the eyes and tusks of a walrus on its side.. Assignment expressions allow you to assign and return a value in the same expression.

  8. How To Use Assignment Expressions in Python

    In this tutorial, you used assignment expressions to make compact sections of Python code that assign values to variables inside of if statements, while loops, and list comprehensions. For more information on other assignment expressions, you can view PEP 572—the document that initially proposed adding assignment expressions to Python.

  9. 6. Expressions

    Assignment expressions¶ assignment_expression::= [identifier ":="] expression. An assignment expression (sometimes also called a "named expression" or "walrus") assigns an expression to an identifier, while also returning the value of the expression. One common use case is when handling matched regular expressions:

  10. Variables, Expressions, and Assignments

    Assignment Statements# We have already seen that Python allows you to evaluate expressions, for instance 40 + 2. It is very convenient if we are able to store the calculated value in some variable for future use. The latter can be done via an assignment statement. An assignment statement creates a new variable with a given name and assigns it a ...

  11. 5. Assignment Expressions

    5. First you may wonder about the brackets, when you look at the assignment expression. It is not meant as a replacement for the simple "assignment statement". Its primary role is to be utilized within expressions. The following code shows a proper usecase: x = 4.56 z = (square := x**2) - (6.6 / square) print(z) OUTPUT:

  12. 1.6. Variables and Assignment

    A variable is a name for a value. An assignment statement associates a variable name on the left of the equal sign with the value of an expression calculated from the right of the equal sign. Enter. Once a variable is assigned a value, the variable can be used in place of that value. The response to the expression width is the same as if its ...

  13. Python 101

    Assignment expressions were added to Python in version 3.8. The general idea is that an assignment expression allows you to assign to variables within an expression. The syntax for doing this is: NAME := expr. This operator has been called the "walrus operator", although their real name is "assignment expression".

  14. Python Expressions vs Python Statements

    Expression: On the right side of the statement, there is an expression, 2 + 3, that evaluates to the integer 5. Statement: The statement x = expression saves the value of the expression 2 + 3 in the variable x. Your code will be made up of both, but sometimes, it's helpful to be able to make a distinction between the two.

  15. python

    This is of the form name := expr where expr is any valid Python expression, and name is an identifier. The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value. Differences from regular assignment statements

  16. Different Forms of Assignment Statements in Python

    Multiple- target assignment: x = y = 75. print(x, y) In this form, Python assigns a reference to the same object (the object which is rightmost) to all the target on the left. OUTPUT. 75 75. 7. Augmented assignment : The augmented assignment is a shorthand assignment that combines an expression and an assignment.

  17. Statement vs Expression

    Expressions are unique in meaning, while statements are two-sided in execution. For example, 1 has a certain value while go( ) may be executed or not. Statements are the whole structure, while expressions are the building blocks. For example, a line or a block of code is a statement.

  18. 2. Variables, expressions and statements

    assignment token = is Python's assignment token. Do not confuse it with equals, which is an operator for comparing values. composition The ability to combine simple expressions and statements into compound statements and expressions in order to represent complex computations concisely. concatenate To join two strings end-to-end. data type A ...

  19. Assignment Operators in Python

    Assignment Operator. Assignment Operators are used to assign values to variables. This operator is used to assign the value of the right side of the expression to the left side operand. Python. # Assigning values using # Assignment Operator a = 3 b = 5 c = a + b # Output print(c) Output. 8.

  20. Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity

    Python Operators: Arithmetic, Assignment, Comparison, Logical, Identity, Membership, Bitwise. Operators are special symbols that perform some operation on operands and returns the result. For example, 5 + 6 is an expression where + is an operator that performs arithmetic add operation on numeric left operand 5 and the right side operand 6 and ...

  21. python

    If one line code is definitely going to happen for you, Python 3.8 introduces assignment expressions affectionately known as "the walrus operator".:= ... single line if statement - Python. 5. One-line multiple variable value assignment with an "if" condition. 3. One line if assignment in python. 1.

  22. Python Operators

    Assignment Operators in Python. Let's see an example of Assignment Operators in Python. Example: The code starts with 'a' and 'b' both having the value 10. It then performs a series of operations: addition, subtraction, multiplication, and a left shift operation on 'b'.

  23. python

    yield is an expression. It used to be a statement, and it's most commonly used as an entire statement, but in Python 2.5, it was turned into an expression as part of new coroutine support. It's still commonly referred to as the "yield statement", partly due to outdated documentation and knowledge and partly because it's mostly used as a ...

  24. python

    1. In python and other languages like C, "=" is a assignment operator and is used to assign a value to a variable. Example: a=2 # the value of a is 2. whereas "==" is Comparison operator and is used to check whether 2 expressions give the same value .Equality check returns true if it succeeds and else return false. Example: a=2 b=3 c=2.