Chapter 4. Control Flow Statements

4.1. Expression Statements
4.2. Assignment Statements
4.3. The assert Statement
4.4. The pass Statement
4.5. The del Statement
4.6. The return Statement
4.7. The yield Statement
4.8. The raise Statement
4.9. The break Statement
4.10. The continue Statement
4.11. The import Statement
4.12. The global Statement
4.13. The nonlocal Statement
4.14. Compound Statements
4.15. The if Statement
4.16. The while Statement
4.17. The for Statement
4.18. The try Statement
4.19. The with Statement
4.20. The match Statement

Statements and expressions form majority of any programming language. In this chapter we will study control flow related statements. We will not study async, functions or class related statements in this chapter. They will be prersented in their own chapters. It is of paramount importance to grasp these concepts in detail because they will form the major part of programs which you will write in future. We have already studied all operators in last chapter while we skipped some expressions which we will study later. A lot of it might not make sense to you but keep reading it and in the end it will all make sense.

Control flow statements form the majority of decision-making or as the name implies control flow of the logic of programs. Mostly this incldue branching using if-else or looping using a loop statement. These two types are core of control flow statements and you will do well to practice them as much as you can.

Statements can be categorized broadly in two categories: simple statements and compound statements. I will first present simple statement because they are easier to understand followed by compound statements.

A simple statement is comprised within a single logical line. Several simple statements may occur on a single line separated by semicolons. The syntax for simple statements is:

simple_stmt ::=  expression_stmt
                 | assert_stmt
                 | assignment_stmt
                 | augmented_assignment_stmt
                 | annotated_assignment_stmt
                 | pass_stmt
                 | del_stmt
                 | return_stmt
                 | yield_stmt
                 | raise_stmt
                 | break_stmt
                 | continue_stmt
                 | import_stmt
                 | future_stmt
                 | global_stmt
                 | nonlocal_stmt
  

4.1. Expression Statements

Expression statements are used (mostly interactively) to compute and write a value, or (usually) to call a procedure (a function that returns no meaningful result; in Python, procedures return the value None). Other uses of expression statements are allowed and occasionally useful. The syntax for an expression statement is:

expression_stmt        ::=  starred_expression
starred_expression     ::=  expression | (starred_item ",")* [starred_item]
starred_item           ::=  assignment_expression | "*" or_expr
    

An expression statement evaluates the expression list (which may be a single expression).

In interactive mode, if the value is not None, it is converted to a string using the built-in repr() function and the resulting string is written to standard output on a line by itself (except if the result is None, so that procedure calls do not cause any output.)

4.2. Assignment Statements

Assignment statements are used to (re)bind names to values and to modify attributes or items of mutable objects:

assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)
target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target
    

(See Section 3.3, “Primaries” for the syntax definitions for attributeref, subscription, and slicing.)

An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.

Assignment is defined recursively depending on the form of the target (list). When a target is part of a mutable object (an attribute reference, subscription or slicing), the mutable object must ultimately perform the assignment and decide about its validity, and may raise an exception if the assignment is unacceptable. The rules observed by various types and the exceptions raised are given with the definition of the object types (see ???).

Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows.

  • If the target list is a single target with no trailing comma, optionally in parentheses, the object is assigned to that target.
  • Else
    • If the target list contains one target prefixed with an asterisk, called a “starred” target: The object must be an iterable with at least as many items as there are targets in the target list, minus one. The first items of the iterable are assigned, from left to right, to the targets before the starred target. The final items of the iterable are assigned to the targets after the starred target. A list of the remaining items in the iterable is then assigned to the starred target (the list can be empty).
    • Else: The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

Assignment of an object to a single target is recursively defined as follows.

  • If the target is an identifier (name):
    • If the name does not occur in a The global Statement or The nonlocal Statement statement in the current code block: the name is bound to the object in the current local namespace.
    • Otherwise: the name is bound to the object in the global namespace or the outer namespace determined by nonlocal, respectively.
    The name is rebound if it was already bound. This may cause the reference count for the object previously bound to the name to reach zero, causing the object to be deallocated and its destructor (if it has one) to be called.
  • If the target is an attribute reference: The primary expression in the reference is evaluated. It should yield an object with assignable attributes; if this is not the case, TypeError is raised. That object is then asked to assign the assigned object to the given attribute; if it cannot perform the assignment, it raises an exception (usually but not necessarily AttributeError).

    Note: If the object is a class instance and the attribute reference occurs on both sides of the assignment operator, the right-hand side expression, a.x can access either an instance attribute or (if no instance attribute exists) a class attribute. The left-hand side target a.x is always set as an instance attribute, creating it if necessary. Thus, the two occurrences of a.x do not necessarily refer to the same attribute: if the right-hand side expression refers to a class attribute, the left-hand side creates a new instance attribute as the target of the assignment:

    class Cls:
        x = 3             # class variable
    inst = Cls()
    inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
              
    

    This description does not necessarily apply to descriptor attributes, such as properties created with property().

  • If the target is a subscription: The primary expression in the reference is evaluated. It should yield either a mutable sequence object (such as a list) or a mapping object (such as a dictionary). Next, the subscript expression is evaluated.

    If the primary is a mutable sequence object (such as a list), the subscript must yield an integer. If it is negative, the sequence’s length is added to it. The resulting value must be a nonnegative integer less than the sequence’s length, and the sequence is asked to assign the assigned object to its item with that index. If the index is out of range, IndexError is raised (assignment to a subscripted sequence cannot add new items to a list).

    If the primary is a mapping object (such as a dictionary), the subscript must have a type compatible with the mapping’s key type, and the mapping is then asked to create a key/datum pair which maps the subscript to the assigned object. This can either replace an existing key/value pair with the same key value, or insert a new key/value pair (if no key with the same value existed).

    For user-defined objects, the __setitem__() method is called with appropriate arguments.

  • If the target is a slicing: The primary expression in the reference is evaluated. It should yield a mutable sequence object (such as a list). The assigned object should be a sequence object of the same type. Next, the lower and upper bound expressions are evaluated, insofar they are present; defaults are zero and the sequence’s length. The bounds should evaluate to integers. If either bound is negative, the sequence’s length is added to it. The resulting bounds are clipped to lie between zero and the sequence’s length, inclusive. Finally, the sequence object is asked to replace the slice with the items of the assigned sequence. The length of the slice may be different from the length of the assigned sequence, thus changing the length of the target sequence, if the target sequence allows it.

CPython implementation detail: In the current implementation, the syntax for targets is taken to be the same as for expressions, and invalid syntax is rejected during the code generation phase, causing less detailed error messages.

Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are 'simultaneous' (for example a, b = b, a swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion. For instance, the following program prints [0, 2]:

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i is updated, then x[i] is updated
print(x)
    

See also PEP 3132 which details Extended Iterable Unpacking

4.2.1. Augmented assignment statements

Augmented assignment is the combination, in a single statement, of a binary operation and an assignment statement:

augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget                 ::=  identifier | attributeref | subscription | slicing
augop                     ::=  "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
                               | ">>=" | "<<=" | "&=" | "^=" | "|="
      

(See Section 3.3, “Primaries” for the syntax definotion of last three symbols.)

An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.

An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead. It is always preferred to use the former syntax for faster operation.

Unlike normal assignments, augmented assignments evaluate the left-hand side before evaluating the right-hand side. For example, a[i] += f(x) first looks-up a[i], then it evaluates f(x) and performs the addition, and lastly, it writes the result back to a[i].

With the exception of assigning to tuples and multiple targets in a single statement, the assignment done by augmented assignment statements is handled the same way as normal assignments. Similarly, with the exception of the possible in-place behavior, the binary operation performed by augmented assignment is the same as the normal binary operations.

For targets which are attribute references, see note above for class and instance attributes as for regular assignments.

4.2.2. Annotated assignment statements

Annotation(specifying the type of variable in this case) assignment is the combination, in a single statement, of a variable or attribute annotation and an optional assignment statement:

annotated_assignment_stmt ::=  augtarget ":" expression
                               ["=" (starred_expression | yield_expression)]
      

The difference from nromal Assignment Statements is that only single target is allowed.

For simple names as assignment targets, if in class or module scope, the annotations are evaluated and stored in a special class or module attribute __annotations__ that is a dictionary mapping from variable names (mangled if private) to evaluated annotations. This attribute is writable and is automatically created at the start of class or module body execution, if annotations are found statically.

For expressions as assignment targets, the annotations are evaluated if in class or module scope, but not stored.

If a name is annotated in a function scope, then this name is local for that scope. Annotations are never evaluated and stored in function scopes.

If the right hand side is present, an annotated assignment performs the actual assignment before evaluating annotations (where applicable). If the right hand side is not present for an expression target, then the interpreter evaluates the target except for the last __setitem__() or __setattr__() call.

See also, PEP 526 for Syntax for Variable Annotations and PEP 484 for Type Hints.

4.3. The assert Statement

Assert statements are a convenient way to insert debugging assertions into a program:

assert_stmt ::=  "assert" expression ["," expression]
    
For example,
assert 0 == 0
assert 0 != 1
    

The simple form, assert expression, is equivalent to

if __debug__:
    if not expression: raise AssertionError
    

The extended form, assert expression1, expression2 is equivalent to

if __debug__:
    if not expression1: raise AssertionError(expression2)
    

These equivalences assume that __debug__ and AssertionError refer to the built-in variables with those names. In the current implementation, the built-in variable __debug__ is True under normal circumstances, False when optimization is requested (command line option -O). The current code generator emits no code for an assert statement when optimization is requested at compile time. Note that it is unnecessary to include the source code for the expression that failed in the error message; it will be displayed as part of the stack trace.

Assignments to __debug__ are illegal. The value for the built-in variable is determined when the interpreter starts.

4.4. The pass Statement

pass_stmt ::=  "pass"

pass is a null operation — when it is executed, nothing happens. It is useful as a placeholder when a statement is required syntactically, but no code needs to be executed, for example:

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)
      

4.5. The del Statement

del_stmt ::=  "del" target_list
    

Deletion is recursively defined very similar to the way assignment is defined. Deletion of a target list recursively deletes each target, from left to right.

Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block. If the name is unbound, a NameError exception will be raised.

Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object).

An example is given below:

x = 5
del x
#print(x) will cause NameError exception if execute
l = [1, 2, 3]
del l[0]
print(l) # will print[2, 3]
d = {"k1": "v1", "k2": "v2"}
del d["k1"] # d is now {"k2": "v2"}
    

4.6. The return Statement

return_stmt ::=  "return" [expression_list]
    

return may only occur syntactically nested in a function definition, not within a nested class definition.

If an expression list is present, it is evaluated, else None is substituted.

return leaves the current function call with the expression list (or None) as return value.

When return passes control out of a try statement with a finally clause, that finally clause is executed before really leaving the function.

In a generator function, the return statement indicates that the generator is done and will cause StopIteration to be raised. The returned value (if any) is used as an argument to construct StopIteration and becomes the StopIteration.value attribute.

In an asynchronous generator function, an empty return statement indicates that the asynchronous generator is done and will cause StopAsyncIteration to be raised. A non-empty return statement is a syntax error in an asynchronous generator function.

We will see heavy usage of the return statement when we study functions.

4.7. The yield Statement

yield_stmt ::=  yield_expression

A yield statement is semantically equivalent to a yield expression. The yield statement can be used to omit the parentheses that would otherwise be required in the equivalent yield expression statement. For example, the yield statements

yield <expr>
yield from <expr>
    

are equivalent to the yield expression statements

(yield <expr>)
(yield from <expr>)
    

Yield expressions and statements are only used when defining a generator function, and are only used in the body of the generator function. Using yield in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.

As said in yield expression section we will study these after we have studied functions.

4.8. The raise Statement

raise_stmt ::=  "raise" [expression ["from" expression]]

If no expressions are present, y re-raises the exception that is currently being handled, which is also known as the active exception. If there isn't currently an active exception, a RuntimeError exception is raised indicating that this is an error.

Otherwise, raise evaluates the first expression as the exception object. It must be either a subclass or an instance of BaseException. If it is a class, the exception instance will be obtained when needed by instantiating the class with no arguments.

The type of the exception is the exception instance's class, the value is the instance itself.

A traceback object is normally created automatically when an exception is raised and attached to it as the __traceback__ attribute, which is writable. You can create an exception and set your own traceback in one step using the with_traceback() exception method (which returns the same exception instance, with its traceback set to its argument), like so:

raise Exception("foo occurred").with_traceback(tracebackobj)

The from clause is used for exception chaining: if given, the second expression must be another exception class or instance. If the second expression is an exception instance, it will be attached to the raised exception as the __cause__ attribute (which is writable). If the expression is an exception class, the class will be instantiated and the resulting exception instance will be attached to the raised exception as the __cause__ attribute. If the raised exception is not handled, both exceptions will be printed:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
    

A similar mechanism works implicitly if a new exception is raised when an exception is already being handled. An exception may be handled when an except or finally clause, or a with statement, is used. The previous exception is then attached as the new exception's __context__ attribute:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
    

Exception chaining can be explicitly suppressed by specifying None in the from clause:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
    

We will see more about exceptions when we study try statement and the execution model of Python.

4.9. The break Statement

break_stmt ::=  "break"

break may only occur syntactically nested in a for or while loop, but not nested in a function or class definition within that loop.

It terminates the nearest enclosing loop, skipping the optional else clause if the loop has one.

If a for loop is terminated by break, the loop control target keeps its current value.

When break passes control out of a try statement with a finally clause, that finally clause is executed before really leaving the loop.

4.10. The continue Statement

continue_stmt ::=  "continue"

continue may only occur syntactically nested in a for or while loop, but not nested in a function or class definition within that loop. It continues with the next cycle of the nearest enclosing loop.

When continue passes control out of a try statement with a finally clause, that finally clause is executed before really starting the next loop cycle.

4.11. The import Statement

import_stmt     ::=  "import" module ["as" identifier] ("," module ["as" identifier])*
                     | "from" relative_module "import" identifier ["as" identifier]
                     ("," identifier ["as" identifier])*
                     | "from" relative_module "import" "(" identifier ["as" identifier]
                     ("," identifier ["as" identifier])* [","] ")"
                     | "from" relative_module "import" "*"
module          ::=  (identifier ".")* identifier
relative_module ::=  "."* module | "."+
    

The basic import statement (no from clause) is executed in two steps:

  1. find a module, loading and initializing it if necessary
  2. define a name or names in the local namespace for the scope where the import statement occurs.

When the statement contains multiple clauses (separated by commas) the two steps are carried out separately for each clause, just as though the clauses had been separated out into individual import statements.

The steps in locating and initializing the module will be discussed later.

If the requested module is retrieved successfully, it will be made available in the local namespace in one of three ways:

  • If the module name is followed by as, then the name following as is bound directly to the imported module.
  • If no other name is specified, and the module being imported is a top level module, the module’s name is bound in the local namespace as a reference to the imported module
  • If the module being imported is not a top level module, then the name of the top level package that contains the module is bound in the local namespace as a reference to the top level package. The imported module must be accessed using its full qualified name rather than directly

The from form uses a slightly more complex process:

  • find the module specified in the from clause, loading and initializing it if necessary;
  • for each of the identifiers specified in the import clauses:
    • check if the imported module has an attribute by that name
    • if not, attempt to import a submodule with that name and then check the imported module again for that attribute
    • if the attribute is not found, ImportError is raised.
    • otherwise, a reference to that value is stored in the local namespace, using the name in the as clause if it is present, otherwise using the attribute name

Examples:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo, foo.bar, and foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb
from foo.bar import baz    # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz
from foo import attr       # foo imported and foo.attr bound as attr
    

If the list of identifiers is replaced by a star ('*'), all public names defined in the module are bound in the local namespace for the scope where the import statement occurs.

The public names defined by a module are determined by checking the module's namespace for a variable named __all__; if defined, it must be a sequence of strings which are names defined or imported by that module. The names given in __all__ are all considered public and are required to exist. If __all__ is not defined, the set of public names includes all names found in the module's namespace which do not begin with an underscore character. __all__ should contain the entire public API. It is intended to avoid accidentally exporting items that are not part of the API (such as library modules which were imported and used within the module).

The wild card form of import — from module import * — is only allowed at the module level. Attempting to use it in class or function definitions will raise a SyntaxError.

When specifying what module to import you do not have to specify the absolute name of the module. When a module or package is contained within another package it is possible to make a relative import within the same top package without having to mention the package name. By using leading dots in the specified module or package after from you can specify how high to traverse up the current package hierarchy without specifying exact names. One leading dot means the current package where the module making the import exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute from . import mod from a module in the pkg package then you will end up importing pkg.mod. If you execute from ..subpkg2 import mod from within pkg.subpkg1 you will import pkg.subpkg2.mod.

importlib.import_module() is provided to support applications that determine dynamically the modules to be loaded.

Raises an auditing event import with arguments module, filename, sys.path, sys.meta_path, sys.path_hooks.

4.11.1. Future statements

A future statement is a directive to the compiler that a particular module should be compiled using syntax or semantics that will be available in a specified future release of Python where the feature becomes standard.

The future statement is intended to ease migration to future versions of Python that introduce incompatible changes to the language. It allows use of the new features on a per-module basis before the release in which the feature becomes standard.

future_stmt ::=  "from" "__future__" "import" feature ["as" identifier]
                 ("," feature ["as" identifier])*
                 | "from" "__future__" "import" "(" feature ["as" identifier]
                 ("," feature ["as" identifier])* [","] ")"
feature     ::=  identifier
      

A future statement must appear near the top of the module. The only lines that can appear before a future statement are:

  • the module docstring (if any),
  • comments,
  • blank lines, and
  • other future statements.

The only feature that requires using the future statement is annotations (see PEP 563).

All historical features enabled by the future statement are still recognized by Python 3. The list includes absolute_import, division, generators, generator_stop, unicode_literals, print_function, nested_scopes and with_statement. They are all redundant because they are always enabled, and only kept for backwards compatibility.

A future statement is recognized and treated specially at compile time: Changes to the semantics of core constructs are often implemented by generating different code. It may even be the case that a new feature introduces new incompatible syntax (such as a new reserved word), in which case the compiler may need to parse the module differently. Such decisions cannot be pushed off until runtime.

For any given release, the compiler knows which feature names have been defined, and raises a compile-time error if a future statement contains a feature not known to it.

The direct runtime semantics are the same as for any import statement: there is a standard module __future__, described later, and it will be imported in the usual way at the time the future statement is executed.

The interesting runtime semantics depend on the specific feature enabled by the future statement.

Note that there is nothing special about the statement:

import __future__ [as name]
      

That is not a future statement; it’s an ordinary import statement with no special semantics or syntax restrictions.

Code compiled by calls to the built-in functions exec() and compile() that occur in a module M containing a future statement will, by default, use the new syntax or semantics associated with the future statement. This can be controlled by optional arguments to compile().

A future statement typed at an interactive interpreter prompt will take effect for the rest of the interpreter session. If an interpreter is started with the -i option, is passed a script name to execute, and the script includes a future statement, it will be in effect in the interactive session started after the script is executed.

See PEP 236 for more information about original proposal.

4.12. The global Statement

global_stmt ::=  "global" identifier ("," identifier)*
    

The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

Names listed in a global statement must not be used in the same code block textually preceding that global statement.

Names listed in a global statement must not be defined as formal parameters, or as targets in with statements or except clauses, or in a for target list, class definition, function definition, import statement, or variable annotation.

CPython implementation detail: The current implementation does not enforce some of these restrictions, but programs should not abuse this freedom, as future implementations may enforce them or silently change the meaning of the program.

Programmer's note: global is a directive to the parser. It applies only to code parsed at the same time as the global statement. In particular, a global statement contained in a string or code object supplied to the built-in exec() function does not affect the code block containing the function call, and code contained in such a string is unaffected by global statements in the code containing the function call. The same applies to the eval() and compile() functions.

4.13. The nonlocal Statement

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*
    

The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Names listed in a nonlocal statement must not collide with pre-existing bindings in the local scope.

See also PEP 3104 for full specification.

4.14. Compound Statements

As the name implies compound statements may consist of more than one statement. They can contain one or more other statements(these can be same or different statement) inside them; the containing statement have certain effects on the contained statements. Typically they always span more than one line although it is possible to write them in one line for simple cases.

A compound statement is usually formed with one of more 'clauses'. A clause is made up of a header and a 'suite'. The clause headers of a particular compound statement are all at the same indentation level. Each clause header starts with a uniquely identifying keyword(like if, while or for) and ends with a colon(:). A clause controls a group of statements which forms a suite. A suite can be one or more semicolon-separated simple statements on the same line as the header, following the header's colon, or it can be one or more indented statements on lines that follow. Only the latter form of a suite can contain nested compound statements; the following is illegal, mostly because it wouldn’t be clear to which if clause a following else clause would belong:

if test1: if test2: print(x)

Also note that the semicolon binds tighter than the colon in this context, so that in the following example, either all or none of the print() calls are executed:

if x < y < z: print(x); print(y); print(z)

Summary of grammar of compound statements is given below:

compound_stmt ::=  if_stmt
                   | while_stmt
                   | for_stmt
                   | try_stmt
                   | with_stmt
                   | match_stmt
                   | funcdef
                   | classdef
                   | async_with_stmt
                   | async_for_stmt
                   | async_funcdef
suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement     ::=  stmt_list NEWLINE | compound_stmt
stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]
    

Note that statements always end in a NEWLINE possibly followed by a DEDENT. Also note that optional continuation clauses always begin with a keyword that cannot start a statement, thus there are no ambiguities (the ‘dangling else’ problem is solved in Python by requiring nested if statements to be indented). See dangling else for an example.

The formatting of the grammar rules in the following sections places each clause on a separate line for clarity.

4.15. The if Statement

Section 4.15, “The if Statement” is used for conditional execution:

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]
    

It selects exactly one of the suites by evaluating the expressions one by one until one is found to be true (see Section 3.10, “Boolean Operators” for the definition of true and false); then that suite is executed (and no other part of the if d statement is executed or evaluated). If all expressions are false, the suite of the else clause, if present, is executed.

An example is given below:

a = 0

if a == 0:
    print('a is zero')

if a == False:
    print('a is False')

my_name = 'Shiv'

if my_name != 'Shiv':
    print('What is your name?')
else:
    print('Welcome Shiv')

rate_of_inflation = 5

if rate_of_inflation <= 4:
    print('Inflation is under control.')
elif (rate_of_inflation > 4) or (rate_of_inflation < 7):
    print('Inflation is bearable')
else:
    print('Inflation is concerning!')
    

And the output is:


a is zero
a is False
Welcome Shiv
Inflation is bearable
    

Notice how different clauses are executed in different cases. Also, notice not equal operator with string. Also, notice how parentheses are used for better readability in elif case. The print statements in the example form the suite of the if statement grammar.

4.16. The while Statement

The while statement is used for repeated execution as long as an expression is true:

while_stmt ::=  "while" assignment_expression ":" suite
                ["else" ":" suite]
    

This repeatedly tests the expression and, if it is true, executes the first suite; if the expression is false (which may be the first time it is tested) the suite of the else clause, if present, is executed and the loop terminates.

A break statement executed in the first suite terminates the loop without executing the else clause's suite. A continue statement executed in the first suite skips the rest of the suite and goes back to testing the expression.

Some examples are given below:

You can see in following example that x is initialized with 0 and while loop tests whether x is less that 11. If this test passes then x is printed and incremented by 1. Clearly, it will print 1 through 10 and when x becomes 11 the loop terminates.

>>> x = 0
>>> while x < 11:
...     print(x)
...     x += 1
...
0
1
2
3
4
5
6
7
8
9
10
>>> x = 0
    

Similar to previous example with added if statement which tests if x is greater than 5 and if true the break statement terminates the loop. The value of x will be 6 after the loop.

>>> while x < 11:
...     print(x)
...     x += 1
...     if x > 5:
...             break
...
0
1
2
3
4
5
    

Here we used continue to not execute print function. Also, at the end of this loop the value of x will be 11.

>>> x = 0
>>> while x < 11:
...     x += 1
...     if x > 5:
...             continue
...     print(x)
...
1
2
3
4
5
    

Here the initial test itself will fail so else clause is executed.

>>> x = 0
>>> while x > 0:
...     print(x)
... else:
...     print(x + 1)
...
1
    

Nested while loop example to print tables of 1 and 2. Notice how the loops are nested with indentation and usage of format string to print the tables.

>>> i = 1
>>> while i < 3:
...     j = 1
...     while j < 11:
...             print(f"{i}*{j} = {i * j}")
...             j += 1
...     i += 1
...
1*1 = 1
1*2 = 2
1*3 = 3
1*4 = 4
1*5 = 5
1*6 = 6
1*7 = 7
1*8 = 8
1*9 = 9
1*10 = 10
2*1 = 2
2*2 = 4
2*3 = 6
2*4 = 8
2*5 = 10
2*6 = 12
2*7 = 14
2*8 = 16
2*9 = 18
2*10 = 20
    

4.17. The for Statement

The for statement is used to iterate over the elements of a sequence (such as a string, tuple or list) or other iterable object:

for_stmt ::=  "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]
    

The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the expression_list. The suite is then executed once for each item provided by the iterator, in the order returned by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment Statements), and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty or an iterator raises a StopIteration exception), the suite in the else clause, if present, is executed, and the loop terminates.

A break statement executed in the first suite terminates the loop without executing the else clause's suite. A continue statement executed in the first suite skips the rest of the suite and continues with the next item, or with the else clause if there is no next item.

The for-loop makes assignments to the variables in the target list. This overwrites all previous assignments to those variables including those made in the suite of the for-loop:

for i in range(10):
    print(i)
    i = 5             # this will not affect the for-loop
                      # because i will be overwritten with the next
                      # index in the range
    

Names in the target list are not deleted when the loop is finished, but if the sequence is empty, they will not have been assigned to at all by the loop. Hint: the built-in function range() returns an iterator of integers suitable to emulate the effect of Pascal's for i := a to b do; e.g., list(range(3)) returns the list [0, 1, 2].

Since range is most commonly used function it, let us study it a bit. It comes in four forms: range(end), range(start,end) and range(start,end,step) where start, end, step are all integers. range(end) is an iterator with starting value 0 and ending with start - 1 i.e. it is a half-close half-open interval. The value of integers will increase by 1 for next value. For range(start, end) it start with start and ends with end - 1. If start is greater than end then it returns an empty iterable i.e. if used with a for loop then loop body will not execute. range(start, end, step) is like earlier but in this case the value increases/decreases by step value.

Let us go through some examples:

>>> for i in range(11):
...     print(i)
...
0
1
2
3
4
5
6
7
8
9
10
    

Like the while loop we can use continue to not execute print function.

>>> for x in range(0, 11):
...     if x > 5:
...             continue
...     print(x)
...
0
1
2
3
4
5
    

Similar to while example, we can use break statement to terminate the loop:

>>> for i in range(11):
...     print(i)
...     if i > 5:
...             break
...
0
1
2
3
4
5
6
    

The table example is rewritten below to demonstrate nested for loop:

>>> for i in range(1,3):
...     for j in range(1,11):
...         print(f'{i}*{j} = {i*j}')
...
1*1 = 1
1*2 = 2
1*3 = 3
1*4 = 4
1*5 = 5
1*6 = 6
1*7 = 7
1*8 = 8
1*9 = 9
1*10 = 10
2*1 = 2
2*2 = 4
2*3 = 6
2*4 = 8
2*5 = 10
2*6 = 12
2*7 = 14
2*8 = 16
2*9 = 18
2*10 = 20
    
The else clause example:
>>> for i in range(2,1):
...     print(i)
... else:
...     print('for loop did not execute')
...
for loop did not execute
    

Another example to demonstrate step of range.

>>> for i in range(0,11,2):
...     print(i)
...
0
2
4
6
8
10
    

4.18. The try Statement

We are going to study exceptions in their own chapter and will study this there.

4.19. The with Statement

The with statement is used to wrap the execution of a block with methods defined by a context manager (see section With Statement Context Managers). This allows common try…except…finally usage patterns to be encapsulated for convenient reuse.

with_stmt          ::=  "with" ( "(" with_stmt_contents ","? ")" | with_stmt_contents ) ":" suite
with_stmt_contents ::=  with_item ("," with_item)*
with_item          ::=  expression ["as" target]
    

The execution of the with statement with one "item" proceeds as follows:

  1. The context expression (the expression given in the with_item) is evaluated to obtain a context manager.
  2. The context manager's __enter__() is loaded for later use.
  3. The context manager's __exit__() is loaded for later use.
  4. The context manager's __enter__() method is invoked.
  5. If a target was included in the with statement, the return value from __enter__() is assigned to it.[1]
  6. The suite is executed.
  7. The context manager's __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.

    If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.

    If the suite was exited for any reason other than an exception, the return value from __exit__() is ignored, and execution proceeds at the normal location for the kind of exit that was taken.

The following code:

with EXPRESSION as TARGET:
    SUITE
    

is semantically equivalent to:

manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False

try:
    TARGET = value
    SUITE
except:
    hit_except = True
    if not exit(manager, *sys.exc_info()):
        raise
finally:
    if not hit_except:
        exit(manager, None, None, None)
    

With more than one item, the context managers are processed as if multiple with statements were nested:

with A() as a, B() as b:
    SUITE
    

is semantically equivalent to:

with A() as a:
    with B() as b:
        SUITE
    

You can also write multi-item context managers in multiple lines if the items are surrounded by parentheses. For example:

with (
    A() as a,
    B() as b,
):
    SUITE
    

See also PEP 343 for more on with statement.

4.20. The match Statement

This is new in version 3.10. The match statement is used for pattern matching. Syntax:

match_stmt   ::=  'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT
subject_expr ::=  star_named_expression "," star_named_expressions?
                  | named_expression
case_block   ::=  'case' patterns [guard] ":" block
    

The match and case keywords are soft keywords.

Pattern matching takes a pattern as input (following case) and a subject value (following match). The pattern (which may contain subpatterns) is matched against the subject value. The outcomes are:

  • A match success or failure (also termed a pattern success or failure).
  • Possible binding of matched values to a name. The prerequisites for this are further discussed below.

See PEP 634 for full specification.

4.20.1. Overview

Here’s an overview of the logical flow of a match statement:

  1. The subject expression subject_expr is evaluated and a resulting subject value obtained. If the subject expression contains a comma, a tuple is constructed using the standard rules.
  2. Each pattern in a case_block is attempted to match with the subject value. The specific rules for success or failure are described below. The match attempt can also bind some or all of the standalone names within the pattern. The precise pattern binding rules vary per pattern type and are specified below. Name bindings made during a successful pattern match outlive the executed block and can be used after the match statement.[2]
  3. If the pattern succeeds, the corresponding guard (if present) is evaluated. In this case all name bindings are guaranteed to have happened.
    • If the guard evaluates as true or is missing, the block inside case_block is executed.
    • Otherwise, the next case_block is attempted as described above.
    • If there are no further case blocks, the match statement is completed.

Important

Users should generally never rely on a pattern being evaluated. Depending on implementation, the interpreter may cache values or use other optimizations which skip repeated evaluations.

A simple match statement:

>>> flag = False
>>> match (100, 200):
...    case (100, 300):  # Mismatch: 200 != 300
...        print('Case 1')
...    case (100, 200) if flag:  # Successful match, but guard fails
...        print('Case 2')
...    case (100, y):  # Matches and binds y to 200
...        print(f'Case 3, y: {y}')
...    case _:  # Pattern not attempted
...        print('Case 4, I match anything!')
...
Case 3, y: 200
      

In this case, if flag is a guard. Read more about that in the next section.

4.20.2. Guards

guard ::=  "if" named_expression
      

A guard (which is part of the case) must succeed for code inside the case block to execute. It takes the form: if followed by an expression.

The logical flow of a case block with a guard follows:

  1. Check that the pattern in the case block succeeded. If the pattern failed, the guard is not evaluated and the next case block is checked.
  2. If the pattern succeeded, evaluate the guard.
    • If the guard condition evaluates as true, the case block is selected.
    • If the guard condition evaluates as false, the case block is not selected.
    • If the guard raises an exception during evaluation, the exception bubbles up.

Guards are allowed to have side effects as they are expressions. Guard evaluation must proceed from the first to the last case block, one at a time, skipping case blocks whose pattern(s) don't all succeed. (I.e., guard evaluation must happen in order.) Guard evaluation must stop once a case block is selected.

4.20.3. Irrefutable Case Blocks

An irrefutable case block is a match-all case block. A match statement may have at most one irrefutable case block, and it must be last.

A case block is considered irrefutable if it has no guard and its pattern is irrefutable. A pattern is considered irrefutable if we can prove from its syntax alone that it will always succeed. Only the following patterns are irrefutable:

  • AS Patterns whose left-hand side is irrefutable
  • OR Patterns containing at least one irrefutable pattern
  • Capture Patterns
  • Wildcard Patterns
  • parenthesized irrefutable patterns

4.20.4. Patterns

patterns       ::=  open_sequence_pattern | pattern
pattern        ::=  as_pattern | or_pattern
closed_pattern ::=  | literal_pattern
                    | capture_pattern
                    | wildcard_pattern
                    | value_pattern
                    | group_pattern
                    | sequence_pattern
                    | mapping_pattern
                    | class_pattern
      

The descriptions below will include a description "in simple terms" of what a pattern does for illustration purposes (credits to Raymond Hettinger for a document that inspired most of the descriptions). Note that these descriptions are purely for illustration purposes and may not reflect the underlying implementation. Furthermore, they do not cover all valid forms.

4.20.4.1. OR patterns

An OR pattern is two or more patterns separated by vertical bars |. Syntax:

or_pattern ::=  "|".closed_pattern+

Only the final subpattern may be irrefutable, and each subpattern must bind the same set of names to avoid ambiguity.

An OR pattern matches each of its subpatterns in turn to the subject value, until one succeeds. The OR pattern is then considered successful. Otherwise, if none of the subpatterns succeed, the OR pattern fails.

In simple terms, P1 | P2 | ... will try to match P1, if it fails it will try to match P2, succeeding immediately if any succeeds, failing otherwise.

4.20.4.2. AS Patterns

An AS pattern matches an OR pattern on the left of the as keyword against a subject. Syntax:

as_pattern ::=  or_pattern "as" capture_pattern

If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds the subject to the name on the right of the as keyword and succeeds. capture_pattern cannot be a a _.

In simple terms P as NAME will match with P, and on success it will set NAME = <subject>.

4.20.4.3. Literal Patterns

literal_pattern ::=  signed_number
                     | signed_number "+" NUMBER
                     | signed_number "-" NUMBER
                     | strings
                     | "None"
                     | "True"
                     | "False"
                     | signed_number: NUMBER | "-" NUMBER
        

The rule strings and the token NUMBER are defined in the standard Python grammar. Triple-quoted strings are supported. Raw strings and byte strings are supported. Formatted string literals are not supported.

The forms signed_number '+' NUMBER and signed_number '-' NUMBER are for expressing complex numbers; they require a real number on the left and an imaginary number on the right. E.g. 3 + 4j.

In simple terms, LITERAL will succeed only if <subject> == LITERAL. For the singletons None, True and False, the is operator is used.

4.20.4.4. Capture Patterns

A capture pattern binds the subject value to a name. Syntax:

capture_pattern ::=  !'_' NAME
        

A single underscore is not a capture pattern (this is what !'_' expresses). It is instead treated as a wildcard_pattern.

In a given pattern, a given name can only be bound once. E.g. case x, x: ... is invalid while case [x] | x: ... is allowed.

Capture patterns always succeed. The binding follows scoping rules established by the assignment expression operator in PEP 572; the name becomes a local variable in the closest containing function scope unless there's an applicable global or nonlocal statement.

In simple terms NAME will always succeed and it will set NAME = <subject>.

4.20.4.5. Wildcard Patterns

A wildcard pattern always succeeds (matches anything) and binds no name. Syntax:

wildcard_pattern ::=  '_'
        

_ is a soft keyword within any pattern, but only within patterns. It is an identifier, as usual, even within match subject expressions, guards, and case blocks.

In simple terms, _ will always succeed.

4.20.4.6. Value Patterns

A value pattern represents a named value in Python. Syntax:

value_pattern ::=  attr
attr          ::=  name_or_attr "." NAME
name_or_attr  ::=  attr | NAME
        

The dotted name in the pattern is looked up using standard Python name resolution rules. The pattern succeeds if the value found compares equal to the subject value (using the == equality operator).

In simple terms NAME1.NAME2 will succeed only if <subject> == NAME1.NAME2

Note

If the same value occurs multiple times in the same match statement, the interpreter may cache the first value found and reuse it rather than repeat the same lookup. This cache is strictly tied to a given execution of a given match statement.

4.20.4.7. Group Patterns

A group pattern allows users to add parentheses around patterns to emphasize the intended grouping. Otherwise, it has no additional syntax. Syntax:

group_pattern ::=  "(" pattern ")"

In simple terms (P) has the same effect as P.

4.20.4.8. Sequence Patterns

A sequence pattern contains several subpatterns to be matched against sequence elements. The syntax is similar to the unpacking of a list or tuple.

sequence_pattern       ::=  "[" [maybe_sequence_pattern] "]"
                            | "(" [open_sequence_pattern] ")"
open_sequence_pattern  ::=  maybe_star_pattern "," [maybe_sequence_pattern]
maybe_sequence_pattern ::=  ",".maybe_star_pattern+ ","?
maybe_star_pattern     ::=  star_pattern | pattern
star_pattern           ::=  "*" (capture_pattern | wildcard_pattern)
        

There is no difference if parentheses or square brackets are used for sequence patterns (i.e. (...) vs [...] ).

Note

A single pattern enclosed in parentheses without a trailing comma (e.g. (3 | 4)) is a group pattern. While a single pattern enclosed in square brackets (e.g. [3 | 4]) is still a sequence pattern.

At most one star subpattern may be in a sequence pattern. The star subpattern may occur in any position. If no star subpattern is present, the sequence pattern is a fixed-length sequence pattern; otherwise it is a variable-length sequence pattern.

The following is the logical flow for matching a sequence pattern against a subject value:

  1. If the subject value is not a sequence [3] , the sequence pattern fails.

    Note

    Subject values of type str, bytes and bytearray do not match sequence patterns.

  2. If the subject value is an instance of str, bytes or bytearray the sequence pattern fails.

    If the sequence pattern is fixed-length:

    1. If the length of the subject sequence is not equal to the number of subpatterns, the sequence pattern fails
    2. Subpatterns in the sequence pattern are matched to their corresponding items in the subject sequence from left to right. Matching stops as soon as a subpattern fails. If all subpatterns succeed in matching their corresponding item, the sequence pattern succeeds.

    Otherwise, if the sequence pattern is variable-length:

    1. If the length of the subject sequence is less than the number of non-star subpatterns, the sequence pattern fails.
    2. The leading non-star subpatterns are matched to their corresponding items as for fixed-length sequences.
    3. If the previous step succeeds, the star subpattern matches a list formed of the remaining subject items, excluding the remaining items corresponding to non-star subpatterns following the star subpattern.
    4. Remaining non-star subpatterns are matched to their corresponding subject items, as for a fixed-length sequence.

    Note

    The length of the subject sequence is obtained via len() (i.e. via the __len__() protocol). This length may be cached by the interpreter in a similar manner as value patterns.

In simple terms {KEY1: P1, KEY2: P2, ... } matches only if all the following happens:

  • check <subject> is a mapping
  • KEY1 in <subject>
  • P1 matches <subject>[KEY1]
  • … and so on for the corresponding KEY/pattern pair.

4.20.4.9. Class Patterns

We will study class patterns after we have studied classes.

What we have not discussed here is function definitions, class definitions, async for and async with statements.



[1] The with statement guarantees that if the __enter__() method returns without an error, then __exit__() will always be called. Thus, if an error occurs during the assignment to the target list, it will be treated the same as an error occurring within the suite would be.

[2] During failed pattern matches, some subpatterns may succeed. Do not rely on bindings being made for a failed match. Conversely, do not rely on variables remaining unchanged after a failed match. The exact behavior is dependent on implementation and may vary. This is an intentional decision made to allow different implementations to add optimizations.

[3] In pattern matching, a sequence is defined as one of the following:

  • a class that inherits from collections.abc.Sequence
  • a Python class that has been registered as collections.abc.Sequence
  • a builtin class that has its (CPython) Py_TPFLAGS_SEQUENCE bit set
  • a class that inherits from any of the above

The following standard library classes are sequences:

  • array.array
  • collections.deque
  • list
  • memoryview
  • range
  • tuple

© 2022 Shiv S. Dayal. www.ashtavakra.org. GNU FDL license v1.3 or later is applicable where not stated.