Assignment Statements

We’ve been using the Python assignment statement for a while to assign objects to names. In its basic form, you write the target of an assignment on the left of an equals sign, and the object to be assigned on the right. The target on the left may be a name or object component, and the object on the right can be an arbitrary expression that computes an object. For the most part, assignments are straightforward, but here are a few properties to keep in mind:

Assignment Statement Forms

Although assignment is a general and pervasive concept in Python, we are primarily interested in assignment statements in this chapter. Table 11-1 illustrates the different assignment statement forms in Python, and their syntax patterns.

Table 11-1. Assignment statement forms

Operation

Interpretation

spam = 'Spam'

Basic form

spam, ham = 'yum', 'YUM'

Tuple assignment (positional)

[spam, ham] = ['yum', 'YUM']

List assignment (positional)

a, b, c, d = 'spam'

Sequence assignment, generalized

a, *b = 'spam'

Extended sequence unpacking (Python 3.X)

spam = ham = 'lunch'

Multiple-target assignment

spams += 42

Augmented assignment (equivalent to spams = spams + 42)

The first form in Table 11-1 is by far the most common: binding a name (or data structure component) to a single object. In fact, you could get all your work done with this basic form alone. The other table entries represent special forms that are all optional, but that programmers often find convenient in practice:

Tuple- and list-unpacking assignments

The second and third forms in the table are related. When you code a tuple or list on the left side of the =, Python pairs objects on the right side with targets on the left by position and assigns them from left to right. For example, in the second line of Table 11-1, the name spam is assigned the string 'yum', and the name ham is bound to the string 'YUM'. In this case Python internally may make a tuple of the items on the right, which is why this is called tuple-unpacking assignment.

Sequence assignments

In later versions of Python, tuple and list assignments were generalized into instances of what we now call sequence assignment—any sequence of names can be assigned to any sequence of values, and Python assigns the items one at a time by position. We can even mix and match the types of the sequences involved. The fourth line in Table 11-1, for example, pairs a tuple of names with a string of characters: a is assigned 's', b is assigned 'p', and so on.

Extended sequence unpacking

In Python 3.X (only), a new form of sequence assignment allows us to be more flexible in how we select portions of a sequence to assign. The fifth line in Table 11-1, for example, matches a with the first character in the string on the right and b with the rest: a is assigned 's', and b is assigned 'pam'. This provides a simpler alternative to assigning the results of manual slicing operations.

Multiple-target assignments

The sixth line in Table 11-1 shows the multiple-target form of assignment. In this form, Python assigns a reference to the same object (the object farthest to the right) to all the targets on the left. In the table, the names spam and ham are both assigned references to the same string object, 'lunch'. The effect is the same as if we had coded ham = 'lunch' followed by spam = ham, as ham evaluates to the original string object (i.e., not a separate copy of that object).

Augmented assignments

The last line in Table 11-1 is an example of augmented assignment—a shorthand that combines an expression and an assignment in a concise way. Saying spam += 42, for example, has the same effect as spam = spam + 42, but the augmented form requires less typing and is generally quicker to run. In addition, if the subject is mutable and supports the operation, an augmented assignment may run even quicker by choosing an in-place update operation instead of an object copy. There is one augmented assignment statement for every binary expression operator in Python.

Sequence Assignments

We’ve already used and explored basic assignments in this book, so we’ll take them as a given. Here are a few simple examples of sequence-unpacking assignments in action:

% python
>>> nudge = 1                      # Basic assignment
>>> wink  = 2
>>> A, B = nudge, wink             # Tuple assignment
>>> A, B                           # Like A = nudge; B = wink
(1, 2)
>>> [C, D] = [nudge, wink]         # List assignment
>>> C, D
(1, 2)

Notice that we really are coding two tuples in the third line in this interaction—we’ve just omitted their enclosing parentheses. Python pairs the values in the tuple on the right side of the assignment operator with the variables in the tuple on the left side and assigns the values one at a time.

Tuple assignment leads to a common coding trick in Python that was introduced in a solution to the exercises at the end of Part II. Because Python creates a temporary tuple that saves the original values of the variables on the right while the statement runs, unpacking assignments are also a way to swap two variables’ values without creating a temporary variable of your own—the tuple on the right remembers the prior values of the variables automatically:

>>> nudge = 1
>>> wink  = 2
>>> nudge, wink = wink, nudge      # Tuples: swaps values
>>> nudge, wink                    # Like T = nudge; nudge = wink; wink = T
(2, 1)

In fact, the original tuple and list assignment forms in Python have been generalized to accept any type of sequence (really, iterable) on the right as long as it is of the same length as the sequence on the left. You can assign a tuple of values to a list of variables, a string of characters to a tuple of variables, and so on. In all cases, Python assigns items in the sequence on the right to variables in the sequence on the left by position, from left to right:

>>> [a, b, c] = (1, 2, 3)          # Assign tuple of values to list of names
>>> a, c
(1, 3)
>>> (a, b, c) = "ABC"              # Assign string of characters to tuple
>>> a, c
('A', 'C')

Technically speaking, sequence assignment actually supports any iterable object on the right, not just any sequence. This is a more general category that includes collections both physical (e.g., lists) and virtual (e.g., a file’s lines), which was defined briefly in Chapter 4 and has popped up in passing ever since. We’ll firm up this term when we explore iterables in Chapter 14 and Chapter 20.

Advanced sequence assignment patterns

Although we can mix and match sequence types around the = symbol, we must generally have the same number of items on the right as we have variables on the left, or we’ll get an error. Python 3.X allows us to be more general with extended unpacking * syntax, described in the next section. But normally in 3.X—and always in 2.X—the number of items in the assignment target and subject must match:

>>> string = 'SPAM'
>>> a, b, c, d = string                            # Same number on both sides
>>> a, d
('S', 'M')

>>> a, b, c = string                               # Error if not
...error text omitted...
ValueError: too many values to unpack (expected 3)

To be more flexible, we can slice in both 2.X and 3.X. There are a variety of ways to employ slicing to make this last case work:

>>> a, b, c = string[0], string[1], string[2:]     # Index and slice
>>> a, b, c
('S', 'P', 'AM')

>>> a, b, c = list(string[:2]) + [string[2:]]      # Slice and concatenate
>>> a, b, c
('S', 'P', 'AM')

>>> a, b = string[:2]                              # Same, but simpler
>>> c = string[2:]
>>> a, b, c
('S', 'P', 'AM')

>>> (a, b), c = string[:2], string[2:]             # Nested sequences
>>> a, b, c
('S', 'P', 'AM')

As the last example in this interaction demonstrates, we can even assign nested sequences, and Python unpacks their parts according to their shape, as expected. In this case, we are assigning a tuple of two items, where the first item is a nested sequence (a string), exactly as though we had coded it this way:

>>> ((a, b), c) = ('SP', 'AM')                     # Paired by shape and position
>>> a, b, c
('S', 'P', 'AM')

Python pairs the first string on the right ('SP') with the first tuple on the left ((a, b)) and assigns one character at a time, before assigning the entire second string ('AM') to the variable c all at once. In this event, the sequence-nesting shape of the object on the left must match that of the object on the right. Nested sequence assignment like this is somewhat rare to see, but it can be convenient for picking out the parts of data structures with known shapes.

For example, we’ll see in Chapter 13 that this technique also works in for loops, because loop items are assigned to the target given in the loop header:

for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: ...          # Simple tuple assignment

for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: ...    # Nested tuple assignment

In a note in Chapter 18, we’ll also see that this nested tuple (really, sequence) unpacking assignment form works for function argument lists in Python 2.X (though not in 3.X), because function arguments are passed by assignment as well:

def f(((a, b), c)): ...          # For arguments too in Python 2.X, but not 3.X
f(((1, 2), 3))

Sequence-unpacking assignments also give rise to another common coding idiom in Python—assigning an integer series to a set of variables:

>>> red, green, blue = range(3)
>>> red, blue
(0, 2)

This initializes the three names to the integer codes 0, 1, and 2, respectively (it’s Python’s equivalent of the enumerated data types you may have seen in other languages). To make sense of this, you need to know that the range built-in function generates a list of successive integers (in 3.X only, it requires a list around it if you wish to display its values all at once like this):

>>> list(range(3))                       # list() required in Python 3.X only
[0, 1, 2]

This call was previewed briefly in Chapter 4; because range is commonly used in for loops, we’ll say more about it in Chapter 13.

Another place you may see a tuple assignment at work is for splitting a sequence into its front and the rest in loops like this:

>>> L = [1, 2, 3, 4]
>>> while L:
...     front, L = L[0], L[1:]           # See next section for 3.X * alternative
...     print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []

The tuple assignment in the loop here could be coded as the following two lines instead, but it’s often more convenient to string them together:

...     front = L[0]
...     L = L[1:]

Notice that this code is using the list as a sort of stack data structure, which can often also be achieved with the append and pop methods of list objects; here, front = L.pop(0) would have much the same effect as the tuple assignment statement, but it would be an in-place change. We’ll learn more about while loops, and other (often better) ways to step through a sequence with for loops, in Chapter 13.

Extended Sequence Unpacking in Python 3.X

The prior section demonstrated how to use manual slicing to make sequence assignments more general. In Python 3.X (but not 2.X), sequence assignment has been generalized to make this easier. In short, a single starred name, *X, can be used in the assignment target in order to specify a more general matching against the sequence—the starred name is assigned a list, which collects all items in the sequence not assigned to other names. This is especially handy for common coding patterns such as splitting a sequence into its “front” and “rest,” as in the preceding section’s last example.

Extended unpacking in action

Let’s look at an example. As we’ve seen, sequence assignments normally require exactly as many names in the target on the left as there are items in the subject on the right. We get an error if the lengths disagree in both 2.X and 3.X (unless we manually sliced on the right, as shown in the prior section):

C:\code> c:\python33\python
>>> seq = [1, 2, 3, 4]

>>> a, b, c, d = seq
>>> print(a, b, c, d)
1 2 3 4

>>> a, b = seq
ValueError: too many values to unpack (expected 2)

In Python 3.X, though, we can use a single starred name in the target to match more generally. In the following continuation of our interactive session, a matches the first item in the sequence, and b matches the rest:

>>> a, *b = seq
>>> a
1
>>> b
[2, 3, 4]

When a starred name is used, the number of items in the target on the left need not match the length of the subject sequence. In fact, the starred name can appear anywhere in the target. For instance, in the next interaction b matches the last item in the sequence, and a matches everything before the last:

>>> *a, b = seq
>>> a
[1, 2, 3]
>>> b
4

When the starred name appears in the middle, it collects everything between the other names listed. Thus, in the following interaction a and c are assigned the first and last items, and b gets everything in between them:

>>> a, *b, c = seq
>>> a
1
>>> b
[2, 3]
>>> c
4

More generally, wherever the starred name shows up, it will be assigned a list that collects every unassigned name at that position:

>>> a, b, *c = seq
>>> a
1
>>> b
2
>>> c
[3, 4]

Naturally, like normal sequence assignment, extended sequence unpacking syntax works for any sequence types (really, again, any iterable), not just lists. Here it is unpacking characters in a string and a range (an iterable in 3.X):

>>> a, *b = 'spam'
>>> a, b
('s', ['p', 'a', 'm'])

>>> a, *b, c = 'spam'
>>> a, b, c
('s', ['p', 'a'], 'm')

>>> a, *b, c = range(4)
>>> a, b, c
(0, [1, 2], 3)

This is similar in spirit to slicing, but not exactly the same—a sequence unpacking assignment always returns a list for multiple matched items, whereas slicing returns a sequence of the same type as the object sliced:

>>> S = 'spam'

>>> S[0], S[1:]    # Slices are type-specific, * assignment always returns a list
('s', 'pam')

>>> S[0], S[1:3], S[3]
('s', 'pa', 'm')

Given this extension in 3.X, as long as we’re processing a list the last example of the prior section becomes even simpler, since we don’t have to manually slice to get the first and rest of the items:

>>> L = [1, 2, 3, 4]
>>> while L:
...     front, *L = L                    # Get first, rest without slicing
...     print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []

Boundary cases

Although extended sequence unpacking is flexible, some boundary cases are worth noting. First, the starred name may match just a single item, but is always assigned a list:

>>> seq = [1, 2, 3, 4]

>>> a, b, c, *d = seq
>>> print(a, b, c, d)
1 2 3 [4]

Second, if there is nothing left to match the starred name, it is assigned an empty list, regardless of where it appears. In the following, a, b, c, and d have matched every item in the sequence, but Python assigns e an empty list instead of treating this as an error case:

>>> a, b, c, d, *e = seq
>>> print(a, b, c, d, e)
1 2 3 4 []

>>> a, b, *e, c, d = seq
>>> print(a, b, c, d, e)
1 2 3 4 []

Finally, errors can still be triggered if there is more than one starred name, if there are too few values and no star (as before), and if the starred name is not itself coded inside a sequence:

>>> a, *b, c, *d = seq
SyntaxError: two starred expressions in assignment

>>> a, b = seq
ValueError: too many values to unpack (expected 2)

>>> *a = seq
SyntaxError: starred assignment target must be in a list or tuple

>>> *a, = seq
>>> a
[1, 2, 3, 4]

A useful convenience

Keep in mind that extended sequence unpacking assignment is just a convenience. We can usually achieve the same effects with explicit indexing and slicing (and in fact must in Python 2.X), but extended unpacking is simpler to code. The common “first, rest” splitting coding pattern, for example, can be coded either way, but slicing involves extra work:

>>> seq
[1, 2, 3, 4]

>>> a, *b = seq                        # First, rest
>>> a, b
(1, [2, 3, 4])

>>> a, b = seq[0], seq[1:]             # First, rest: traditional
>>> a, b
(1, [2, 3, 4])

The also-common “rest, last” splitting pattern can similarly be coded either way, but the new extended unpacking syntax requires noticeably fewer keystrokes:

>>> *a, b = seq                        # Rest, last
>>> a, b
([1, 2, 3], 4)

>>> a, b = seq[:-1], seq[-1]           # Rest, last: traditional
>>> a, b
([1, 2, 3], 4)

Because it is not only simpler but, arguably, more natural, extended sequence unpacking syntax will likely become widespread in Python code over time.

Application to for loops

Because the loop variable in the for loop statement can be any assignment target, extended sequence assignment works here too. We met the for loop iteration tool briefly in Chapter 4 and will study it formally in Chapter 13. In Python 3.X, extended assignments may show up after the word for, where a simple variable name is more commonly used:

for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]:
    ...

When used in this context, on each iteration Python simply assigns the next tuple of values to the tuple of names. On the first loop, for example, it’s as if we’d run the following assignment statement:

a, *b, c = (1, 2, 3, 4)                            # b gets [2, 3]

The names a, b, and c can be used within the loop’s code to reference the extracted components. In fact, this is really not a special case at all, but just an instance of general assignment at work. As we saw earlier in this chapter, we can do the same thing with simple tuple assignment in both Python 2.X and 3.X:

for (a, b, c) in [(1, 2, 3), (4, 5, 6)]:           # a, b, c = (1, 2, 3), ...

And we can always emulate 3.X’s extended assignment behavior in 2.X by manually slicing:

for all in [(1, 2, 3, 4), (5, 6, 7, 8)]:
    a, b, c = all[0], all[1:3], all[3]

Since we haven’t learned enough to get more detailed about the syntax of for loops, we’ll return to this topic in Chapter 13.

Multiple-Target Assignments

A multiple-target assignment simply assigns all the given names to the object all the way to the right. The following, for example, assigns the three variables a, b, and c to the string 'spam':

>>> a = b = c = 'spam'
>>> a, b, c
('spam', 'spam', 'spam')

This form is equivalent to (but easier to code than) these three assignments:

>>> c = 'spam'
>>> b = c
>>> a = b

Multiple-target assignment and shared references

Keep in mind that there is just one object here, shared by all three variables (they all wind up pointing to the same object in memory). This behavior is fine for immutable types—for example, when initializing a set of counters to zero (recall that variables must be assigned before they can be used in Python, so you must initialize counters to zero before you can start adding to them):

>>> a = b = 0
>>> b = b + 1
>>> a, b
(0, 1)

Here, changing b only changes b because numbers do not support in-place changes. As long as the object assigned is immutable, it’s irrelevant if more than one name references it.

As usual, though, we have to be more cautious when initializing variables to an empty mutable object such as a list or dictionary:

>>> a = b = []
>>> b.append(42)
>>> a, b
([42], [42])

This time, because a and b reference the same object, appending to it in place through b will impact what we see through a as well. This is really just another example of the shared reference phenomenon we first met in Chapter 6. To avoid the issue, initialize mutable objects in separate statements instead, so that each creates a distinct empty object by running a distinct literal expression:

>>> a = []
>>> b = []                 # a and b do not share the same object
>>> b.append(42)
>>> a, b
([], [42])

A tuple assignment like the following has the same effect—by running two list expressions, it creates two distinct objects:

>>> a, b = [], []          # a and b do not share the same object

Augmented Assignments

Beginning with Python 2.0, the set of additional assignment statement formats listed in Table 11-2 became available. Known as augmented assignments, and borrowed from the C language, these formats are mostly just shorthand. They imply the combination of a binary expression and an assignment. For instance, the following two formats are roughly equivalent:

X = X + Y                       # Traditional form
X += Y                          # Newer augmented form

Table 11-2. Augmented assignment statements

X += Y

X &= Y

X −= Y

X |= Y

X *= Y

X ^= Y

X /= Y

X >>= Y

X %= Y

X <<= Y

X **= Y

X //= Y

Augmented assignment works on any type that supports the implied binary expression. For example, here are two ways to add 1 to a name:

>>> x = 1
>>> x = x + 1                   # Traditional
>>> x
2
>>> x += 1                      # Augmented
>>> x
3

When applied to a sequence such as a string, the augmented form performs concatenation instead. Thus, the second line here is equivalent to typing the longer S = S + "SPAM":

>>> S = "spam"
>>> S += "SPAM"                 # Implied concatenation
>>> S
'spamSPAM'

As shown in Table 11-2, there are analogous augmented assignment forms for every Python binary expression operator (i.e., each operator with values on the left and right side). For instance, X *= Y multiplies and assigns, X >>= Y shifts right and assigns, and so on. X //= Y (for floor division) was added in version 2.2.

Augmented assignments have three advantages:[22]

  • There’s less for you to type. Need I say more?

  • The left side has to be evaluated only once. In X += Y, X may be a complicated object expression. In the augmented form, its code must be run only once. However, in the long form, X = X + Y, X appears twice and must be run twice. Because of this, augmented assignments usually run faster.

  • The optimal technique is automatically chosen. That is, for objects that support in-place changes, the augmented forms automatically perform in-place change operations instead of slower copies.

The last point here requires a bit more explanation. For augmented assignments, in-place operations may be applied for mutable objects as an optimization. Recall that lists can be extended in a variety of ways. To add a single item to the end of a list, we can concatenate or call append:

>>> L = [1, 2]
>>> L = L + [3]                 # Concatenate: slower
>>> L
[1, 2, 3]
>>> L.append(4)                 # Faster, but in place
>>> L
[1, 2, 3, 4]

And to add a set of items to the end, we can either concatenate again or call the list extend method:[23]

>>> L = L + [5, 6]              # Concatenate: slower
>>> L
[1, 2, 3, 4, 5, 6]
>>> L.extend([7, 8])            # Faster, but in place
>>> L
[1, 2, 3, 4, 5, 6, 7, 8]

In both cases, concatenation is less prone to the side effects of shared object references but will generally run slower than the in-place equivalent. Concatenation operations must create a new object, copy in the list on the left, and then copy in the list on the right. By contrast, in-place method calls simply add items at the end of a memory block (it can be a bit more complicated than that internally, but this description suffices).

When we use augmented assignment to extend a list, we can largely forget these details—Python automatically calls the quicker extend method instead of using the slower concatenation operation implied by +:

>>> L += [9, 10]                # Mapped to L.extend([9, 10])
>>> L
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note however, that because of this equivalence += for a list is not exactly the same as a + and = in all cases—for lists += allows arbitrary sequences (just like extend), but concatenation normally does not:

>>> L = []
>>> L += 'spam'                 # += and extend allow any sequence, but + does not!
>>> L
['s', 'p', 'a', 'm']
>>> L = L + 'spam'
TypeError: can only concatenate list (not "str") to list

Augmented assignment and shared references

This behavior is usually what we want, but notice that it implies that the += is an in-place change for lists; thus, it is not exactly like + concatenation, which always makes a new object. As for all shared reference cases, this difference might matter if other names reference the object being changed:

>>> L = [1, 2]
>>> M = L                       # L and M reference the same object
>>> L = L + [3, 4]              # Concatenation makes a new object
>>> L, M                        # Changes L but not M
([1, 2, 3, 4], [1, 2])

>>> L = [1, 2]
>>> M = L
>>> L += [3, 4]                 # But += really means extend
>>> L, M                        # M sees the in-place change too!
([1, 2, 3, 4], [1, 2, 3, 4])

This only matters for mutables like lists and dictionaries, and it is a fairly obscure case (at least, until it impacts your code!). As always, make copies of your mutable objects if you need to break the shared reference structure.

Variable Name Rules

Now that we’ve explored assignment statements, it’s time to get more formal about the use of variable names. In Python, names come into existence when you assign values to them, but there are a few rules to follow when choosing names for the subjects of your programs:

Syntax: (underscore or letter) + (any number of letters, digits, or underscores)

Variable names must start with an underscore or letter, which can be followed by any number of letters, digits, or underscores. _spam, spam, and Spam_1 are legal names, but 1_Spam, spam$, and @#! are not.

Case matters: SPAM is not the same as spam

Python always pays attention to case in programs, both in names you create and in reserved words. For instance, the names X and x refer to two different variables. For portability, case also matters in the names of imported module files, even on platforms where the filesystems are case-insensitive. That way, your imports still work after programs are copied to differing platforms.

Reserved words are off-limits

Names you define cannot be the same as words that mean special things in the Python language. For instance, if you try to use a variable name like class, Python will raise a syntax error, but klass and Class work fine. Table 11-3 lists the words that are currently reserved (and hence off-limits for names of your own) in Python.

Table 11-3. Python 3.X reserved words

False

class

finally

is

return

None

continue

for

lambda

try

True

def

from

nonlocal

while

and

del

global

not

with

as

elif

if

or

yield

assert

else

import

pass

 

break

except

in

raise

 

Table 11-3 is specific to Python 3.X. In Python 2.X, the set of reserved words differs slightly:

  • print is a reserved word, because printing is a statement, not a built-in function (more on this later in this chapter).

  • exec is a reserved word, because it is a statement, not a built-in function.

  • nonlocal is not a reserved word because this statement is not available.

In older Pythons the story is also more or less the same, with a few variations:

  • with and as were not reserved until 2.6, when context managers were officially enabled.

  • yield was not reserved until Python 2.3, when generator functions came online.

  • yield morphed from statement to expression in 2.5, but it’s still a reserved word, not a built-in function.

As you can see, most of Python’s reserved words are all lowercase. They are also all truly reserved—unlike names in the built-in scope that you will meet in the next part of this book, you cannot redefine reserved words by assignment (e.g., and = 1 results in a syntax error).[24]

Besides being of mixed case, the first three entries in Table 11-3, True, False, and None, are somewhat unusual in meaning—they also appear in the built-in scope of Python described in Chapter 17, and they are technically names assigned to objects. In 3.X they are truly reserved in all other senses, though, and cannot be used for any other purpose in your script other than that of the objects they represent. All the other reserved words are hardwired into Python’s syntax and can appear only in the specific contexts for which they are intended.

Furthermore, because module names in import statements become variables in your scripts, variable name constraints extend to your module filenames too. For instance, you can code files called and.py and my-code.py and run them as top-level scripts, but you cannot import them: their names without the “.py” extension become variables in your code and so must follow all the variable rules just outlined. Reserved words are off-limits, and dashes won’t work, though underscores will. We’ll revisit this module idea in Part V of this book.

Naming conventions

Besides these rules, there is also a set of naming conventions—rules that are not required but are followed in normal practice. For instance, because names with two leading and trailing underscores (e.g., __name__) generally have special meaning to the Python interpreter, you should avoid this pattern for your own names. Here is a list of the conventions Python follows:

  • Names that begin with a single underscore (_X) are not imported by a from module import * statement (described in Chapter 23).

  • Names that have two leading and trailing underscores (__X__) are system-defined names that have special meaning to the interpreter.

  • Names that begin with two underscores and do not end with two more (__X) are localized (“mangled”) to enclosing classes (see the discussion of pseudoprivate attributes in Chapter 31).

  • The name that is just a single underscore (_) retains the result of the last expression when you are working interactively.

In addition to these Python interpreter conventions, there are various other conventions that Python programmers usually follow. For instance, later in the book we’ll see that class names commonly start with an uppercase letter and module names with a lowercase letter, and that the name self, though not reserved, usually has a special role in classes. In Chapter 17 we’ll also study another, larger category of names known as the built-ins, which are predefined but not reserved (and so can be reassigned: open = 42 works, though sometimes you might wish it didn’t!).

Names have no type, but objects do

This is mostly review, but remember that it’s crucial to keep Python’s distinction between names and objects clear. As described in Chapter 6, objects have a type (e.g., integer, list) and may be mutable or not. Names (a.k.a. variables), on the other hand, are always just references to objects; they have no notion of mutability and have no associated type information, apart from the type of the object they happen to reference at a given point in time.

Thus, it’s OK to assign the same name to different kinds of objects at different times:

>>> x = 0               # x bound to an integer object
>>> x = "Hello"         # Now it's a string
>>> x = [1, 2, 3]       # And now it's a list

In later examples, you’ll see that this generic nature of names can be a decided advantage in Python programming. In Chapter 17, you’ll also learn that names also live in something called a scope, which defines where they can be used; the place where you assign a name determines where it is visible.[25]

Note

For additional naming suggestions, see the discussion of naming conventions in Python’s semi-official style guide, known as PEP 8. This guide is available at http://www.python.org/dev/peps/pep-0008, or via a web search for “Python PEP 8.” Technically, this document formalizes coding standards for Python library code.

Though useful, the usual caveats about coding standards apply here. For one thing, PEP 8 comes with more detail than you are probably ready for at this point in the book. And frankly, it has become more complex, rigid, and subjective than it may need to be—some of its suggestions are not at all universally accepted or followed by Python programmers doing real work. Moreover, some of the most prominent companies using Python today have adopted coding standards of their own that differ.

PEP 8 does codify useful rule-of-thumb Python knowledge, though, and it’s a great read for Python beginners, as long as you take its recommendations as guidelines, not gospel.



[22] C/C++ programmers take note: although Python now supports statements like X += Y, it still does not have C’s auto-increment/decrement operators (e.g., X++, −−X). These don’t quite map to the Python object model because Python has no notion of in-place changes to immutable objects like numbers.

[23] As suggested in Chapter 6, we can also use slice assignment (e.g., L[len(L):] = [11,12,13]), but this works roughly the same as the simpler and more mnemonic list extend method.

[24] In standard CPython, at least. Alternative implementations of Python might allow user-defined variable names to be the same as Python reserved words. See Chapter 2 for an overview of alternative implementations, such as Jython.

[25] If you’ve used a more restrictive language like C++, you may be interested to know that there is no notion of C++’s const declaration in Python; certain objects may be immutable, but names can always be assigned. Python also has ways to hide names in classes and modules, but they’re not the same as C++’s declarations (if hiding attributes matters to you, see the coverage of _X module names in Chapter 25, __X class names in Chapter 31, and the Private and Public class decorators example in Chapter 39).