Single Assignment Statement Python Found

7. Simple 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 ::= | | | | | | | | | | | | | |

7.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 ). Other uses of expression statements are allowed and occasionally useful. The syntax for an expression statement is:

expression_stmt ::=

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

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

7.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 ::= ("," )* [","] target ::= | "(" [] ")" | "[" [] "]" | | | | "*"

(See section 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 section The standard type hierarchy).

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 empty: The object must also be an empty iterable.
  • If the target list is a single target in parentheses: The object is assigned to that target.
  • If the target list is a comma-separated list of targets, or a single target in square brackets: 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.
    • 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 or 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 , 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, 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 ).

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

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

    classCls:x=3# class variableinst=Cls()inst.x=inst.x+1# writes inst.x as 4 leaving Cls.x as 3
  • 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, 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 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 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 :

See also

PEP 3132 - Extended Iterable Unpacking
The specification for the feature.

7.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 ::= "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="

(See section Primaries for the syntax definitions of the 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 can be rewritten as to achieve a similar, but not exactly equal effect. In the augmented version, 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.

Unlike normal assignments, augmented assignments evaluate the left-hand side before evaluating the right-hand side. For example, first looks-up , then it evaluates and performs the addition, and lastly, it writes the result back to .

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, the same caveat about class and instance attributes applies as for regular assignments.

7.2.2. Annotated assignment statements¶

Annotation assignment is the combination, in a single statement, of a variable or attribute annotation and an optional assignment statement:

annotated_assignment_stmt ::= ":" ["=" ]

The difference from normal Assignment statements is that only single target and only single right hand side value 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 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 or call.

See also

PEP 526 - Variable and attribute annotation syntax PEP 484 - Type hints

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

7.3. The statement¶

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

assert_stmt ::= "assert" ["," ]

The simple form, , is equivalent to

The extended form, , is equivalent to

These equivalences assume that and refer to the built-in variables with those names. In the current implementation, the built-in variable is under normal circumstances, 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 are illegal. The value for the built-in variable is determined when the interpreter starts.

if__debug__:ifnotexpression:raiseAssertionError
if__debug__:ifnotexpression1:raiseAssertionError(expression2)

7.4. The statement¶

pass_stmt ::= "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:

deff(arg):pass# a function that does nothing (yet)classC:pass# a class with no methods (yet)

7.5. The statement¶

del_stmt ::= "del"

Deletion is recursively defined very similar to the way assignment is defined. Rather than spelling it out in full details, here are some hints.

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 statement in the same code block. If the name is unbound, a 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).

Changed in version 3.2: Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block.

7.6. The statement¶

return_stmt ::= "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 is substituted.

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

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

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

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

7.7. The statement¶

yield_stmt ::=

A 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

are equivalent to the yield expression statements

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.

For full details of semantics, refer to the Yield expressions section.

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

7.8. The statement¶

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

If no expressions are present, re-raises the last exception that was active in the current scope. If no exception is active in the current scope, a exception is raised indicating that this is an error.

Otherwise, evaluates the first expression as the exception object. It must be either a subclass or an instance of . 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 attribute, which is writable. You can create an exception and set your own traceback in one step using the exception method (which returns the same exception instance, with its traceback set to its argument), like so:

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

A similar mechanism works implicitly if an exception is raised inside an exception handler or a clause: the previous exception is then attached as the new exception’s attribute:

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

Additional information on exceptions can be found in section Exceptions, and information about handling exceptions is in section The try statement.

Changed in version 3.3: is now permitted as in .

New in version 3.3: The attribute to suppress automatic display of the exception context.

raiseException("foo occurred").with_traceback(tracebackobj)
>>> try:... print(1/0)... exceptExceptionasexc:... raiseRuntimeError("Something bad happened")fromexc...Traceback (most recent call last): File "<stdin>", line 2, in <module>ZeroDivisionError: division by zeroThe 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
>>> try:... print(1/0)... except:... raiseRuntimeError("Something bad happened")...Traceback (most recent call last): File "<stdin>", line 2, in <module>ZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback (most recent call last): File "<stdin>", line 4, in <module>RuntimeError: Something bad happened
>>> try:... print(1/0)... except:... raiseRuntimeError("Something bad happened")fromNone...Traceback (most recent call last): File "<stdin>", line 4, in <module>RuntimeError: Something bad happened

7.9. The statement¶

break_stmt ::= "break"

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

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

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

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

7.10. The statement¶

continue_stmt ::= "continue"

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

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

7.11. The statement¶

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

The basic import statement (no 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 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 details of the first step, finding and loading modules are described in greater detail in the section on the import system, which also describes the various types of packages and modules that can be imported, as well as all the hooks that can be used to customize the import system. Note that failures in this step may indicate either that the module could not be located, or that an error occurred while initializing the module, which includes execution of the module’s code.

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 , then the name following 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 form uses a slightly more complex process:

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

Examples:

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 statement occurs.

The public names defined by a module are determined by checking the module’s namespace for a variable named ; if defined, it must be a sequence of strings which are names defined or imported by that module. The names given in are all considered public and are required to exist. If 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 (). 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 — — is only allowed at the module level. Attempting to use it in class or function definitions will raise a .

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 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 a module in the package then you will end up importing . If you execute from within you will import . The specification for relative imports is contained within PEP 328.

is provided to support applications that determine dynamically the modules to be loaded.

7.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_statement ::= "from" "__future__" "import" feature ["as" name] ("," feature ["as" name])* | "from" "__future__" "import" "(" feature ["as" name] ("," feature ["as" name])* [","] ")" feature ::= identifier name ::= 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 features recognized by Python 3.0 are , , , , , and . 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 , 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:

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 and that occur in a module 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 — see the documentation of that function for details.

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 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 also

PEP 236 - Back to the __future__
The original proposal for the __future__ mechanism.
import__future__[asname]
importfoo# foo imported and bound locallyimportfoo.bar.baz# foo.bar.baz imported, foo bound locallyimportfoo.bar.bazasfbb# foo.bar.baz imported and bound as fbbfromfoo.barimportbaz# foo.bar.baz imported and bound as bazfromfooimportattr# foo imported and foo.attr bound as attr

7.12. The statement¶

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

The 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 , although free variables may refer to globals without being declared global.

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

Names listed in a statement must not be defined as formal parameters or in a loop control target, definition, function definition, 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: is a directive to the parser. It applies only to code parsed at the same time as the statement. In particular, a statement contained in a string or code object supplied to the built-in function does not affect the code block containing the function call, and code contained in such a string is unaffected by statements in the code containing the function call. The same applies to the and functions.

7.13. The statement¶

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

The 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 statement, unlike those listed in a 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 statement must not collide with pre-existing bindings in the local scope.

See also

PEP 3104 - Access to Names in Outer Scopes
The specification for the statement.

Variables¶

Recall that a variable is a label for a location in memory. It can be used to hold a value. In statically typed languages, variables have predetermined types, and a variable can only be used to hold values of that type. In Python, we may reuse the same variable to store values of any type.

A variable is similar to the memory functionality found in most calculators, in that it holds one value which can be retrieved many times, and that storing a new value erases the old. A variable differs from a calculator’s memory in that one can have many variables storing different values, and that each variable is referred to by name.

Defining variables¶

To define a new variable in Python, we simply assign a value to a label. For example, this is how we create a variable called , which contains an integer value of zero:

This is exactly the same syntax as assigning a new value to an existing variable called . Later in this chapter we will discuss under what circumstances this statement will cause a new variable to be created.

If we try to access the value of a variable which hasn’t been defined anywhere yet, the interpreter will exit with a name error.

We can define several variables in one line, but this is usually considered bad style:

In keeping with good programming style, we should make use of meaningful names for variables.

# Define three variables at once:count,result,total=0,0,0# This is equivalent to:count=0result=0total=0

Variable scope and lifetime¶

Not all variables are accessible from all parts of our program, and not all variables exist for the same amount of time. Where a variable is accessible and how long it exists depend on how it is defined. We call the part of a program where a variable is accessible its scope, and the duration for which the variable exists its lifetime.

A variable which is defined in the main body of a file is called a global variable. It will be visible throughout the file, and also inside any file which imports that file. Global variables can have unintended consequences because of their wide-ranging effects – that is why we should almost never use them. Only objects which are intended to be used globally, like functions and classes, should be put in the global namespace.

A variable which is defined inside a function is local to that function. It is accessible from the point at which it is defined until the end of the function, and exists for as long as the function is executing. The parameter names in the function definition behave like local variables, but they contain the values that we pass into the function when we call it. When we use the assignment operator () inside a function, its default behaviour is to create a new local variable – unless a variable with the same name is already defined in the local scope.

Here is an example of variables in different scopes:

Note

The inside of a class body is also a new local variable scope. Variables which are defined in the class body (but outside any class method) are called class attributes. They can be referenced by their bare names within the same scope, but they can also be accessed from outside this scope if we use the attribute access operator () on a class or an instance (an object which uses that class as its type). An attribute can also be set explicitly on an instance or class from inside a method. Attributes set on instances are called instance attributes. Class attributes are shared between all instances of a class, but each instance has its own separate instance attributes. We will look at this in greater detail in the chapter about classes.

# This is a global variablea=0ifa==0:# This is still a global variableb=1defmy_function(c):# this is a local variabled=3print(c)print(d)# Now we call the function, passing the value 7 as the first and only parametermy_function(7)# a and b still existprint(a)print(b)# c and d don't exist anymore -- these statements will give us name errors!print(c)print(d)

The assignment operator¶

As we saw in the previous sections, the assignment operator in Python is a single equals sign (). This operator assigns the value on the right hand side to the variable on the left hand side, sometimes creating the variable first. If the right hand side is an expression (such as an arithmetic expression), it will be evaluated before the assignment occurs. Here are a few examples:

The last statement might look a bit strange if we were to interpret as a mathematical equals sign – clearly a number cannot be equal to the same number plus one! Remember that is an assignment operator – this statement is assigning a new value to the variable which is equal to the old value of plus one.

Assigning an initial value to variable is called initialising the variable. In some languages defining a variable can be done in a separate step before the first value assignment. It is thus possible in those languages for a variable to be defined but not have a value – which could lead to errors or unexpected behaviour if we try to use the value before it has been assigned. In Python a variable is defined and assigned a value in a single step, so we will almost never encounter situations like this.

The left hand side of the assignment statement must be a valid target:

An assignment statement may have multiple targets separated by equals signs. The expression on the right hand side of the last equals sign will be assigned to all the targets. All the targets must be valid:

a_number=5# a_number becomes 5a_number=total# a_number becomes the value of totala_number=total+5# a_number becomes the value of total + 5a_number=a_number+1# a_number becomes the value of a_number + 1
# this is fine:a=3# these are all illegal:3=43=aa+b=3
# both a and b will be set to zero:a=b=0# this is illegal, because we can't set 0 to b:a=0=b

Compound assignment operators¶

We have already seen that we can assign the result of an arithmetic expression to a variable:

Counting is something that is done often in a program. For example, we might want to keep count of how many times a certain event occurs by using a variable called . We would initialise this variable to zero and add one to it every time the event occurs. We would perform the addition with this statement:

This is in fact a very common operation. Python has a shorthand operator, , which lets us express it more cleanly, without having to write the name of the variable twice:

There is a similar operator, , which lets us decrement numbers:

Other common compound assignment operators are given in the table below:

OperatorExampleEquivalent to
# These statements mean exactly the same thing:count=count+1count+=1# We can increment a variable by any number we like.count+=2count+=7count+=a+b
# These statements mean exactly the same thing:count=count-3count-=3

More about scope: crossing boundaries¶

What if we want to access a global variable from inside a function? It is possible, but doing so comes with a few caveats:

The print statement will output , the value of the global variable , as you probably expected. But what about this program?

When we call the function, the print statement inside outputs – but why does the print statement at the end of the program output ?

By default, the assignment statement creates variables in the local scope. So the assignment inside the function does not modify the global variable – it creates a new local variable called , and assigns the value to that variable. The first print statement outputs the value of the new local variable – because if a local variable has the same name as a global variable the local variable will always take precedence. The last print statement prints out the global variable, which has remained unchanged.

What if we really want to modify a global variable from inside a function? We can use the keyword:

We may not refer to both a global variable and a local variable by the same name inside the same function. This program will give us an error:

Because we haven’t declared to be global, the assignment in the second line of the function will create a local variable . This means that we can’t refer to the global variable elsewhere in the function, even before this line! The first print statement now refers to the local variable – but this variable doesn’t have a value in the first line, because we haven’t assigned it yet!

Note that it is usually very bad practice to access global variables from inside functions, and even worse practice to modify them. This makes it difficult to arrange our program into logically encapsulated parts which do not affect each other in unexpected ways. If a function needs to access some external value, we should pass the value into the function as a parameter. If the function is a method of an object, it is sometimes appropriate to make the value an attribute of the same object – we will discuss this in the chapter about object orientation.

Note

There is also a keyword in Python – when we nest a function inside another function, it allows us to modify a variable in the outer function from inside the inner function (or, if the function is nested multiple times, a variable in one of the outer functions). If we use the keyword, the assignment statement will create the variable in the global scope if it does not exist already. If we use the keyword, however, the variable must be defined, because it is impossible for Python to determine in which scope it should be created.

a=0defmy_function():print(a)my_function()
a=0defmy_function():a=3print(a)my_function()print(a)
a=0defmy_function():globalaa=3print(a)my_function()print(a)
a=0defmy_function():print(a)a=3print(a)my_function()

Exercise 1¶

  1. Describe the scope of the variables , , and in this example:

    defmy_function(a):b=a-2returnbc=3ifc>2:d=my_function(5)print(d)
  2. What is the lifetime of these variables? When will they be created and destroyed?

  3. Can you guess what would happen if we were to assign a value of instead?

  4. Why would this be a problem? Can you think of a way to avoid it?

Modifying values¶

Constants¶

In some languages, it is possible to define special variables which can be assigned a value only once – once their values have been set, they cannot be changed. We call these kinds of variables constants. Python does not allow us to set such a restriction on variables, but there is a widely used convention for marking certain variables to indicate that their values are not meant to change: we write their names in all caps, with underscores separating words:

Why do we bother defining variables that we don’t intend to change? Consider this example:

There are several good reasons to define instead of just writing inside the print statement. First, this gives the number a descriptive label which explains what it is – this makes the code more understandable. Second, we may eventually need to refer to this number in our program more than once. If we ever need to update our code with a new value for the maximum mark, we will only have to change it in one place, instead of finding every place where it is used – such replacements are often error-prone.

Literal numbers scattered throughout a program are known as “magic numbers” – using them is considered poor coding style. This does not apply to small numbers which are considered self-explanatory – it’s easy to understand why a total is initialised to zero or incremented by one.

Sometimes we want to use a variable to distinguish between several discrete options. It is useful to refer to the option values using constants instead of using them directly if the values themselves have no intrinsic meaning:

In the above example, the values , and are not important – they are completely meaningless. We could equally well use , and or the strings , and . The only important thing is that the three values must be different. If we used the numbers directly instead of the constants the program would be much more confusing to read. Using meaningful strings would make the code more readable, but we could accidentally make a spelling mistake while setting one of the values and not notice – if we mistype the name of one of the constants we are more likely to get an error straight away.

Some Python libraries define common constants for our convenience, for example:

Note that many built-in constants don’t follow the all-caps naming convention.

# These variables are "constants" by convention:NUMBER_OF_DAYS_IN_A_WEEK=7NUMBER_OF_MONTHS_IN_A_YEAR=12# Nothing is actually stopping us from redefining them...NUMBER_OF_DAYS_IN_A_WEEK=8# ...but it's probably not a good idea.
MAXIMUM_MARK=80tom_mark=58print(("Tom's mark is %.2f%%"%(tom_mark/MAXIMUM_MARK*100)))# %% is how we escape a literal % inside a string
# We define some optionsLOWER,UPPER,CAPITAL=1,2,3name="jane"# We use our constants when assigning these values...print_style=UPPER# ...and when checking them:ifprint_style==LOWER:print(name.lower())elifprint_style==UPPER:print(name.upper())elifprint_style==CAPITAL:print(name.capitalize())else:# Nothing prevents us from accidentally setting print_style to 4, 90 or# "spoon", so we put in this fallback just in case:print("Unknown style option!")
# we need to import these libraries before we use themimportstringimportmathimportre# All the lowercase ASCII letters: 'abcdefghijklmnopqrstuvwxyz'print(string.ascii_lowercase)# The mathematical constants pi and e, both floating-point numbersprint(math.pi)# ratio of circumference of a circle to its diameterprint(math.e)# natural base of logarithms# This integer is an option which we can pass to functions in the re# (regular expression) library.print(re.IGNORECASE)

Mutable and immutable types¶

Some values in python can be modified, and some cannot. This does not ever mean that we can’t change the value of a variable – but if a variable contains a value of an immutable type, we can only assign it a new value. We cannot alter the existing value in any way.

Integers, floating-point numbers and strings are all immutable types – in all the previous examples, when we changed the values of existing variables we used the assignment operator to assign them new values:

Even this operator doesn’t modify the value of in-place – it also assigns a new value:

We haven’t encountered any mutable types yet, but we will use them extensively in later chapters. Lists and dictionaries are mutable, and so are most objects that we are likely to write ourselves:

a=3a=2b="jane"b="bob"
# this is a list of numbersmy_list=[1,2,3]my_list[0]=5# we can change just the first element of the listprint(my_list)classMyClass(object):pass# this is a very silly class# Now we make a very simple object using our class as a typemy_object=MyClass()# We can change the values of attributes on the objectmy_object.some_property=42

More about input¶

In the earlier sections of this unit we learned how to make a program display a message using the function or read a string value from the user using the function. What if we want the user to input numbers or other types of variables? We still use the function, but we must convert the string values returned by to the types that we want. Here is a simple example:

is a function which converts values of various types to ints. We will discuss type conversion in greater detail in the next section, but for now it is important to know that will not be able to convert a string to an integer if it contains anything except digits. The program above will exit with an error if the user enters , or even . When we write a program which relies on user input, which can be incorrect, we need to add some safeguards so that we can recover if the user makes a mistake. For example, we can detect if the user entered bad input and exit with a nicer error message:

This program will still only attempt to read in the input once, and exit if it is incorrect. If we want to keep asking the user for input until it is correct, we can do something like this:

We will learn more about boolean values, loops and exceptions later.

height=int(input("Enter height of rectangle: "))width=int(input("Enter width of rectangle: "))print("The area of the rectangle is %d"%(width*height))
try:height=int(input("Enter height of rectangle: "))width=int(input("Enter width of rectangle: "))exceptValueErrorase:# if a value error occurs, we will skip to this pointprint("Error reading height and width: %s"%e)
correct_input=False# this is a boolean value -- it can be either true or false.whilenotcorrect_input:# this is a while looptry:height=int(input("Enter height of rectangle: "))width=int(input("Enter width of rectangle: "))exceptValueError:print("Please enter valid integers for the height and width.")else:# this will be executed if there is no value errorcorrect_input=True

Example: calculating petrol consumption of a car¶

In this example, we will write a simple program which asks the user for the distance travelled by a car, and the monetary value of the petrol that was used to cover that distance. From this information, together with the price per litre of petrol, the program will calculate the efficiency of the car, both in litres per 100 kilometres and and kilometres per litre.

First we will define the petrol price as a constant at the top. This will make it easy for us to update the price when it changes on the first Wednesday of every month:

When the program starts,we want to print out a welcome message:

Ask the user for his or her name:

Ask the user for the distance travelled:

Then ask the user for the amount paid:

Now we will do the calculations:

Finally, we output the results:

PETROL_PRICE_PER_LITRE=4.50
print("*** Welcome to the fuel efficiency calculator! ***\n")# we add an extra blank line after the message with \n
name=input("Enter your name: ")
# float is a function which converts values to floating-point numbers.distance_travelled=float(input("Enter distance travelled in km: "))
amount_paid=float(input("Enter monetary value of fuel bought for the trip: R"))
fuel_consumed=amount_paid/PETROL_PRICE_PER_LITREefficiency_l_per_100_km=fuel_consumed/distance_travelled*100efficiency_km_per_l=distance_travelled/fuel_consumed
print("Hi, %s!"%name)print("Your car's efficiency is %.2f litres per 100 km."%efficiency_l_per_100_km)print("This means that you can travel %.2f km on a litre of petrol."%efficiency_km_per_l)# we add an extra blank line before the message with \nprint("\nThanks for using the program.")

Exercise 2¶

  1. Write a Python program to convert a temperature given in degrees Fahrenheit to its equivalent in degrees Celsius. You can assume that T_c = (5/9) x (T_f - 32), where T_c is the temperature in °C and T_f is the temperature in °F. Your program should ask the user for an input value, and print the output. The input and output values should be floating-point numbers.
  2. What could make this program crash? What would we need to do to handle this situation more gracefully?

Type conversion¶

As we write more programs, we will often find that we need to convert data from one type to another, for example from a string to an integer or from an integer to a floating-point number. There are two kinds of type conversions in Python: implicit and explicit conversions.

Implicit conversion¶

Recall from the section about floating-point operators that we can arbitrarily combine integers and floating-point numbers in an arithmetic expression – and that the result of any such expression will always be a floating-point number. This is because Python will convert the integers to floating-point numbers before evaluating the expression. This is an implicit conversion – we don’t have to convert anything ourselves. There is usually no loss of precision when an integer is converted to a floating-point number.

For example, the integer will automatically be converted to a floating-point number in the following example:

is a while is an . Python will automatically convert operands so that they are of the same type. In this case this is achieved if the integer is converted to the floating-point equivalent . Then the two floating-point numbers can be multiplied.

Let’s have a look at a more complex example:

Python performs operations according to the order of precedence, and decides whether a conversion is needed on a per-operation basis. In our example has the highest precedence, so it will be processed first. and are both integers and is the integer division operator – the result of this operation is the integer . Now we are left with . The addition and subtraction are at the same level of precedence, so they are evaluated left-to-right, starting with addition. First is converted to the floating-point number , and the two floating-point numbers are added, which leaves us with . The result of this floating-point subtraction is , which is assigned to .

result=8.5+7//3-2.5

Explicit conversion¶

Converting numbers from to will result in a loss of precision. For example, try to convert to an – it is not possible to do this without losing precision. In order for this to happen, we must explicitly tell Python that we are aware that precision will be lost. For example, we need to tell the compiler to convert a to an like this:

The function converts a to an by discarding the fractional part – it will always round down! If we want more control over the way in which the number is rounded, we will need to use a different function:

Explicit conversion is sometimes also called casting – we may read about a being cast to or vice-versa.

# the floor and ceil functions are in the math moduleimportmath# ceil returns the closest integer greater than or equal to the number# (so it always rounds up)i=math.ceil(5.834)# floor returns the closest integer less than or equal to the number# (so it always rounds down)i=math.floor(5.834)# round returns the closest integer to the number# (so it rounds up or down)# Note that this is a built-in function -- we don't need to import math to use it.i=round(5.834)

Converting to and from strings¶

As we saw in the earlier sections, Python seldom performs implicit conversions to and from – we usually have to convert values explicitly. If we pass a single number (or any other value) to the function, it will be converted to a string automatically – but if we try to add a number and a string, we will get an error:

To convert numbers to strings, we can use string formatting – this is usually the cleanest and most readable way to insert multiple values into a message. If we want to convert a single number to a string, we can also use the function explicitly:

# This is OKprint(5)print(6.7)# This is not OKprint("3"+4)# Do you mean this...print("3%d"%4)# concatenate "3" and "4" to get "34"# Or this?print(int("3")+4)# add 3 and 4 to get 7
# These lines will do the same thingprint("3%d"%4)print("3"+str(4))

More about conversions¶

In Python, functions like , and will try to convert anything to their respective types – for example, we can use the function to convert strings to integers or to convert floating-point numbers to integers. Note that although can convert a float to an integer it can’t convert a string containing a float to an integer directly!

Values of type can contain the value or . These values are used extensively in conditional statements, which execute or do not execute parts of our program depending on some binary condition:

The condition is often an expression which evaluates to a boolean value:

However, almost any value can implicitly be converted to a boolean if it is used in a statement like this:

This usually behaves in the way that you would expect: non-zero numbers are values and zero is . However, we need to be careful when using strings – the empty string is treated as , but any other string is – even and !

# This is OKint("3")# This is OKint(3.7)# This is not OKint("3.7")# This is a string representation of a float, not an integer!# We have to convert the string to a float firstint(float("3.7"))
my_flag=Trueifmy_flag:print("Hello!")
if3>4:print("This will not be printed.")
my_number=3ifmy_number:print("My number is non-zero!")
# bool is a function which converts values to booleansbool(34)# Truebool(0)# Falsebool(1)# Truebool("")# Falsebool("Jane")# Truebool("0")# True!bool("False")# Also True!

Exercise 3¶

  1. Convert to a float.
  2. Convert to an integer (with rounding).
  3. Convert to an integer (with rounding).
  4. Convert to a string.
  5. Convert to a string.
  6. Convert to a float.
  7. Convert to a boolean.

Answers to exercises¶

Answer to exercise 1¶

  1. is a local variable in the scope of because it is an argument name. is also a local variable inside , because it is assigned a value inside . and are both global variables. It doesn’t matter that is created inside an block, because the inside of an block is not a new scope – everything inside the block is part of the same scope as the outside (in this case the global scope). Only function definitions (which start with ) and class definitions (which start with ) indicate the start of a new level of scope.
  2. Both and will be created every time is called and destroyed when has finished executing. is created when it is assigned the value , and exists for the remainder of the program’s execution. is created inside the block (when it is assigned the value which is returned from the function), and also exists for the remainder of the program’s execution.
  3. As we will learn in the next chapter, blocks are executed conditionally. If were not greater than in this program, the block would not be executed, and if that were to happen the variable would never be created.
  4. We may use the variable later in the code, assuming that it always exists, and have our program crash unexpectedly if it doesn’t. It is considered poor coding practice to allow a variable to be defined or undefined depending on the outcome of a conditional statement. It is better to ensure that is always defined, no matter what – for example, by assigning it some default value at the start. It is much easier and cleaner to check if a variable has the default value than to check whether it exists at all.

Answer to exercise 2¶

  1. Here is an example program:

    Note

    The formatting symbol is used with floats, and instructs Python to pick a sensible human-readable way to display the float.

    T_f=float(input("Please enter a temperature in °F: "))T_c=(5/9)*(T_f-32)print("%g°F = %g°C"%(T_f,T_c))
  2. The program could crash if the user enters a value which cannot be converted to a floating-point number. We would need to add some kind of error checking to make sure that this doesn’t happen – for example, by storing the string value and checking its contents. If we find that the entered value is invalid, we can either print an error message and exit or keep prompting the user for input until valid input is entered.

Answer to exercise 3¶

Here are example answers:

importmatha_1=float("8.8")a_2=math.round(8.8)a_3=math.round("8.8")a_4="%g"%8.8a_5="%d"%8a_6=float(8)a_7=bool(8)

0 Thoughts to “Single Assignment Statement Python Found

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *