Skip to content

Assignments

Learning Objectives

At the end of this sub-unit, students should

  • understand how assignment works.
  • know how to assign values to variables in different ways.

Basic of Assignment

There are at least four different kinds of assignments. However, we will only discuss two of them while keeping the rest as optional knowledge. An assignment is different from expression. The operations from the previous units are called expressions. Their evaluation produces a value.

On the other hand, an assignment is what we call a statement. The evaluation of a statement does not produce a value but may modify the state of the program. By the state of the program, we meant the mapping from variable to values.

Simple Assignment

The first one is a simple assignment. First note that we use = operator for an assignment and not equality check since we use == for equality check. We need to be careful not to confuse this with mathematical equality check.

Simple Assignment

The syntax of the simple assignment is shown below.

1
lhs = rhs

Here, lhs is evaluated into an address and rhs is evaluated into a value. For instance, lhs can be a variable name and rhs can be an expression that evaluates into a value.

Although an assignment is written as a single line, it actually consists of several steps. The evaluation is as follows.

  1. Evaluate lhs to obtain an address \(A\).
  2. Evaluate rhs to obtain a value \(V\).
  3. Store the value \(V\) into the address \(A\).
    • If the variable does not exist, we will create the variable and the attached address.

Step 3 above has a conditional. In particular, this will create a new variable if one does not exist yet. If a variable does not exist and we try to retrieve the value, we will get a NameError. There is no variable declaration in Python, which may make the code shorter but may also cause more confusion.

Since a variable stores a value that persists, we now have a stateful operation.

Simple Assignment

Since x does not exist yet, we will get an error.

1
2
>>> x
NameError

After assignment, the name will exist.

1
2
3
4
5
>>> x = 2
>>> x
2
>>> x ** (x + 1)  # 2 ** (2 + 1) = 2 ** 3
8

The initial rhs can be any expression.

1
2
3
>>> x = 2 * 3 + 1
>>> x
7

With variable, we can now write more meaningful code. Below are some samples of our previous code rewritten with variable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> s = 'ABC'
>>> s < 'ACB'      # 'ABC' < 'ACB'
True
>>> s < s          # 'ABC' < 'ABC'
False
>>> ba = 'ba'
>>> na = 'na'
>>> two = 2
>>> ba + na * two  # 'ba' + 'na' * 2  = 'ba' + ('na' * 2)
'banana'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> P = 10000
>>> r = 0.05
>>> n = 12
>>> t = 10
>>> B = P * ((1 + (r / n)) ** (n * t))
>>> B
16470.0949769028
>>> x = 6
>>> x % 2 == 0 and x % 3 == 0  # 6 % 2 == 0 and 6 % 3 == 0
True

Re-Assignment

Recap how step 3 in the evaluation of an assignment has conditional. In particular, if the variable already exist, we simply retrieve the address. Then, we store the new value into this existing address. This means that the next time we ask for the value of this variable, it will have a different value1.

Additionally, the order of the operation means that there are certain code that may look mathematical but does not have a useful mathematical meaning.

x = x + 1

Consider the code x = x + 1. Ask any mathematicians, they will be horrified by that statement because there is no value of x that can satisfy this. However, this is a perfectly valid code.

1
2
3
4
5
6
>>> x = 0
>>> x
0
>>> x = x + 1   # x = 0 + 1
>>> x
1

The order of operation is done as if we are evaluating rhs first (i.e., evaluate x + 1 into 1) before storing it in lhs. Of course, the actual order is slightly different because we first evaluate lhs into an address. But this does not change the value of x.

Note that from line 2 up to the rhs evaluation of line 4 (as highlighted above), the value of x is equal to 0 as indicated in x = 0. The visualization below shows the steps starting from x = 2.

AssgStep01

??? danger "Bad Practice" Since the variable creation only happen in step 3, we may actually have problem when having the same variable name appearing in both lhs and rhs.

1
2
3
4
```{ .pycon .error }
>>> x = x + 1   # x does not exist yet!
NameError
```

Simultaneous Assignment

The second kind of assignment is called simultaneous assignment. In this kind of assignment, we can assign multiple variables simultaneously. It is particularly useful when we want to initialize several variables at the same time or to swap the values of variables.

Simultaneous Assignment

The syntax of the simple assignment is shown below.

1
lhs1, lhs2 = rhs1, rhs2

This syntax can be extended to an arbitrary number of lhs and rhs as long as there are equal number of both sides. But note that by equal number, we do not simply count the number of variables because rhs can be any expressions.

The evaluation is also similar to simple assignment, but we simply expand this to the number of lhs and rhs.

  1. Evaluate lhs1, lhs2, ... to obtain addresses \(A_1\), \(A_2\), ....
  2. Evaluate rhs1, rhs2, ... to obtain values \(V_1\), \(V_2\), ....
  3. Store the value \(V_1\) into the address \(A_1\), \(V_2\) into the address \(A_2\), ....
    • If any variable does not exist, we will create the variable and the attached address.

AssgStep02

We recommend using this simultaneous assignment on two cases:

  1. Initializing multiple variables to initial their respective values.
  2. Swapping the values of variables.

Simultaneous Assignment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> x, y, z = 0, 1, 2   # initialization
>>> x
0
>>> y
1
>>> z
2
>>> x, y = y, x         # swap
>>> x
1
>>> y
0

??? idlerepl "Extra Variable Swap" Swapping can be done by adding one more variable to store temporary values. Using simultaneous assignment, we can rewrite this in a single line and with no extra variable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
```pycon
>>> x, y = 0, 1
>>> tmp = x
>>> x = y
>>> y = tmp
>>> x
1
>>> y
0
```

Note if we do not use simultaneous assignment, we cannot avoid the extra variable in general.
The following code shows how without extra variable, we will get the wrong output.

```pycon hl_lines="7"
>>> x, y = 0, 1
>>> x = y
>>> y = x
>>> x
1
>>> y
1
```

But what could be problem if we use it all the time? That looks similar enough to a simple assignment and we know how to evaluate simple assignment. Do read the bad practice below to know more, but the best advice is to simply do not use simultaneous assignment except for the two cases above.

??? danger "Bad Practice" As usual, when thinking about the behavior in more details, there may be complications that exist. First, note that all rhs1, rhs2, ... will be evaluated before creating any new variable. So it can only use old variable. Hence, the following will fail.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
```{ .pycon .error }
>>> x, y = 0, x   # x does not exist yet
NameError
```

This same ordering of steps that allow us to perform a swap may cause confusion in other cases.
Consider the following code.

```pycon hl_lines="2 3"
>>> x, y = 0, 1
>>> x = y + 1
>>> y = x + 1
>>> x
2
>>> y
3
```

Since there are two assignments above, can we rewrite that into a single line using simultaneous assignment?

```pycon hl_lines="6"
>>> x, y = 0, 1
>>> x, y = y + 1, x + 1
>>> x
2
>>> y
1
```

This produces different result.
In short, please do not use simultaneous assignment except for the two cases we identified above.
In fact, this will be more complicated to reason with once we have other data types.

Other Kinds of Assignment

??? question "Multiple Target Assignment" Another kind of assignment has multiple lhs but only a single rhs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
!!! note "Syntax"
    ```py
    lhs1 = lhs2 = rhs
    ```

Again, there can be more than two `#!py3 lhs`, each must evaluate into an address.
There is only one `#!py3 rhs` which can be any expression.

!!! note "Semantics"
    1. Evaluate `#!py3 lhs1`, `#!py3 lhs2`, ..., `#!py3 lhsn` to obtain addresses $A_1$, $A_2$, ...., $A_n$
    2. Evaluate `#!py3 rhs` to obtain a value $V$.
    3. Store the value $V$ into the address $A_n$, ..., $A_2$, $A_1$.
        - If any variable does not exist, we will create the variable and the attached address.

There can be a lot of complications if you do not fully understand the intricacies of the steps above.
As such, we would only recommend this for one purpose only.
You should use this if you are _initializing_ multiple variables to the same value.

```pycon
>>> x = y = z = 0  # simpler than x, y, z = 0, 0, 0
```

??? question "Augmented Assignment" Another kind of assignment combines assignment with operations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
!!! note "Syntax"
    ```py
    lhs += rhs
    ```

    Here, the operation `#!py3 +` can be any valid arithmetic operations.
    So `#!py3 x *= y` and `#!py3 x **= y` is fine but `#!py3 x and= False` is not.

    Also, do not confuse this with `#!py3 x <= y` which is its own operation `#!py3 <=` and not an augmentation with `#!py3 <`.

Instead of mentioning the semantics, augmented assignment can be explained more simply by using rewriting rule.

!!! note "Rewriting Rule"
    We can rewrite

    ```py
    lhs += rhs
    ```

    into

    ```py
    lhs = lhs + (rhs)
    ```

    where the `#!py3 rhs` has to be evaluated first as emphasized by the parentheses.

Note how the parentheses is needed.
Without it, we will get the wrong result as shown below.

=== "Augmented Assignment"
    ```pycon
    >>> x = 2
    >>> x
    2
    >>> x *= 2 + 1   # equivalent to x = x * (2 + 1)  or  x = 2 * 3
    >>> x
    6
    ```

=== "Correct Rewriting"
    ```pycon
    >>> x = 2
    >>> x
    2
    >>> x = x * (2 + 1)
    >>> x
    6
    ```

=== "Incorrect Rewriting"
    ```pycon
    >>> x = 2
    >>> x
    2
    >>> x = x * 2 + 1
    >>> x
    5
    ```

A naïve rewriting without parentheses produce incorrect result as shown above.

  1. The use of the symbol = in lhs = rhs can be thought of saying that the lhs is equal to rhs but only until the next assignment.