Dictionaries, Variables and Statements in Python

In this third part of a nine-part series that quickly goes over the Python language, you will learn about dictionaries, references, and much more. It is excerpted from chapter four of the book Python in a Nutshell, Second Edition, written by Alex Martelli (O’Reilly; ISBN: 0596100469). Copyright © 2007 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.


A mapping is an arbitrary collection of objects indexed by nearly arbitrary values called keys. Mappings are mutable and, unlike sequences, are not ordered.

Python provides a single built-in mapping type, the dictionary type. Library and extension modules provide other mapping types, and you can write others
yourself (as discussed in "Mappings" on page 110). Keys in a dictionary may be of different types, but they must be hashable (see hash on page 162). Values in a dictionary are arbitrary objects and may be of different types. An item in a dictionary is a key/value pair. You can think of a dictionary as an associative array (known in other languages as a "map," "hash table, or "hash").

To specify a dictionary, you can use a series of pairs of expressions (the pairs are the items of the dictionary) separated by commas (,) within braces ({}). You may optionally place a redundant comma after the last item. Each item in a dictionary is written as key:value , where key  is an expression giving the item’s key and value  is an expression giving the item’s value. If a key appears more than once in a dictionary literal, only one of the items with that key is kept in the resulting
dictionary object–dictionaries do not allow duplicate keys. To denote an empty dictionary, use an empty pair of braces. Here are some dictionaries:

  {‘x’:42, ‘y’:3.14, ‘z’:7 }    # Dictionary with three items and string keys
  {1:2, 3:4 }                   # Dictionary with two items and integer keys
  {}                            # Empty dictionary

You can also call the built-in type dict to create a dictionary in a way that, while less concise, can sometimes be more readable. For example, the dictionaries in this last snippet can also, equivalently, be written as, respectively:

  dict(x=42, y=3.14, z=7)       # Dictionary with three items and string keys
  dict([[1, 2], [3, 4]])        # Dictionary with two items and integer keys
  dict()                        # Empty dictionary

dict() without arguments creates and returns an empty dictionary. When the argument x to dict is a mapping, dict returns a new dictionary object with the same keys and values as x . When x  is iterable, the items in x  must be pairs, and dict(x) returns a dictionary whose items (key/value pairs) are the same as the items in x . If a key appears more than once in x , only the last item with that key is kept in the resulting dictionary.

When you call dict, in addition to or instead of the positional argument x  you may pass named arguments, each with the syntax name=value , where name  is an identifier to use as an item’s key and value  is an expression giving the item’s value. When you call dict and pass both a positional argument and one or more named arguments, if a key appears both in the positional argument and as a named argument, Python associates to that key the value given with the named argument (i.e., the named argument wins).

You can also create a dictionary by calling dict.fromkeys. The first argument is an iterable whose items become the keys of the dictionary; the second argument is the value that corresponds to each key (all keys initially have the same corresponding value). If you omit the second argument, the value corresponding to each key is None. For example:

  dict.fromkeys(‘hello’, 2)    # same as {‘h’:2, ‘e’:2, ‘l’:2, ‘o’:2}
  dict.fromkeys([1, 2, 3])     # same as {1:None, 2:None, 3:None}


The built-in None denotes a null object. None has no methods or other attributes. You can use None as a placeholder when you need a reference but you don’t care what object you refer to, or when you need to indicate that no object is there. Functions return None as their result unless they have specific return statements coded to return other values.


In Python, callable types are those whose instances support the function call operation (see "Calling Functions" on page 73). Functions are callable. Python provides several built-in functions (see "Built-in Functions" on page 158) and supports user-defined functions (see "The def Statement" on page 70). Generators are also callable (see "Generators" on page 78).

Types are also callable, as we already saw for the dict, list, and tuple built-in types. (See "Built-in Types" on page 154 for a complete list of built-in types.) As we’ll discuss in "Python Classes" on page 82, class objects (user-defined types) are also callable. Calling a type normally creates and returns a new instance of that type.

Other callables are methods, which are functions bound to class attributes and instances of classes that supply a special method named __call__.

Boolean Values

Every data value in Python can be taken as a truth value: true or false. Any nonzero number or nonempty container (e.g., string, tuple, list, set, or dictionary) is true. 0 (of any numeric type), None, and empty containers are false. Be careful about using a floating-point number as a truth value: such use is equivalent to comparing the number for exact equality with zero, and floating-point numbers should almost never be compared for exact equality!

Built-in type bool is a subclass of int. The only two values of type bool are True and False, which have string representations of ‘True’ and ‘False’, but also numerical values of 1 and 0, respectively. Several built-in functions return bool results, as do comparison operators. You can call bool(x) with any x as the argument. The result is True if x  is true and False if x  is false. Good Python style is not to use such calls when they are redundant: always write if x:, never if bool(x):, if x==True:, if
, and so on.

{mospagebreak title=Variables and Other References}

A Python program accesses data values through references. A reference is a name that refers to the location in memory of a value (object). References take the form of variables, attributes, and items. In Python, a variable or other reference has no intrinsic type. The object to which a reference is bound at a given time always has a type, but a given reference may be bound to objects of various types during the program’s execution.


In Python there are no declarations. The existence of a variable begins with a statement that binds the variable, or, in other words, sets a name to hold a
reference to some object. You can also unbind a variable, resetting the name so it no longer holds a reference. Assignment statements are the most common way to bind variables and other references. The del statement unbinds references.

Binding a reference that was already bound is also known as rebinding it. Whenever I mention binding in this book, I implicitly include rebinding except where I explicitly exclude it. Rebinding or unbinding a reference has no effect on the object to which the reference was bound, except that an object disappears when nothing refers to it. The automatic cleanup of objects bereft of references is known as garbage collection.

You can name a variable with any identifier except the 30 that are reserved as Pythons keywords (see "Keywords" on page 35). A variable can be global or local. A global variable is an attribute of a module object (Chapter 7 covers modules). A local variable lives in a functions local namespace (see "Namespaces" on page 76).

Object attributes and items

The main distinction between the attributes and items of an object is in the syntax you use to access them. An attribute of an object is denoted by a reference to the object, followed by a period (.), followed by an identifier known as the attribute name (for example, x. y  refers to one of the attributes of the object bound to name x , specifically that attribute which is named y ).

An item of an object is denoted by a reference to the object, followed by an expression within brackets ([]). The expression in brackets is known as the item’s index or key, and the object is known as the item’s container (for example, x[y] refers to the item at the key or index bound to name y , within the container object bound to name x ).

Attributes that are callable are also known as methods. Python draws no strong distinctions between callable and noncallable attributes, as some other languages do. All rules about attributes also apply to callable attributes (methods).

Accessing nonexistent references

A common programming error is trying to access a reference that does not exist. For example, a variable may be unbound, or an attribute name or item index may not be valid for the object to which you apply it. The Python compiler, when it analyzes and compiles source code, diagnoses only syntax errors. Compilation does not diagnose semantic errors, such as trying to access an unbound attribute, item, or variable. Python diagnoses semantic errors only when the errant code executes, i.e., at runtime. When an operation is a Python semantic error, attempting it raises an exception (see Chapter 6). Accessing a nonexistent
variable, attribute, or item, just like any other semantic error, raises an exception.

{mospagebreak title=Assignment Statements}

Assignment statements can be plain or augmented. Plain assignment to a variable (e.g., name=value ) is how you create a new variable or rebind an existing variable to a new value. Plain assignment to an object attribute (e.g., x.attr=value ) is a request to object x to create or rebind attribute attr. Plain assignment to an item in a container (e.g., x[k]=value ) is a request to container x to create or rebind the item with index k .

Augmented assignment (e.g., name+=value ) cannot, per se, create new references. Augmented assignment can rebind a variable, ask an object to rebind one of its existing attributes or items, or request the target object to modify itself (an object may, of course, create whatever it wants in response to such requests). When you make a request to an object, it is up to the object to decide whether to honor the request or raise an exception.

Plain assignment

A plain assignment statement in the simplest form has the syntax: 

  target = expression

The target is also known as the lefthand side (LHS), and the expression is the righthand side (RHS). When the assignment executes, Python evaluates the RHS expression, then binds the expressions value to the LHS target. The binding does not depend on the type of the value. In particular, Python draws no strong distinction between callable and noncallable objects, as some other languages do, so you can bind functions, methods, types, and other callables to variables, just as you can numbers, strings, lists, and so on.

Details of the binding do depend on the kind of target, however. The target in an assignment may be an identifier, an attribute reference, an indexing, or a slicing:

An identifier

Is a variable’s name. Assignment to an identifier binds the variable with this name.

An attribute reference

Has the syntax obj.name. obj  is an arbitrary expression, and name  is an identifier, known as an attribute name of the object. Assignment to an attribute reference asks object obj to bind its attribute named name .

An indexing

Has the syntax obj[expr]. obj  and expr  are arbitrary expressions. Assignment to an indexing asks container obj  to bind its item indicated by the value of expr , also known as the index or key of the item in the container.

A slicing

Has the syntax obj[start:stop] or
obj[start:stop:stride]. obj, start, stop, and stride are arbitrary expressions. start , stop , and stride are all optional (i.e., obj[:stop:] and obj[:stop] are also syntactically correct slicings, equivalent to obj[None:stop:None]). Assignment to a slicing asks container obj  to bind or unbind some of its items. Assigning to a slicing such as obj[start:stop:stride] is equivalent to assigning to the indexing obj[slice(start, stop, stride)] , where slice is a Python built-in type (see slice on page 156) whose instances represent slices.

I’ll come back to indexing and slicing targets when I discuss operations on lists, in "Modifying a list" on page 56, and on dictionaries, in "Indexing a Dictionary" on page 60.

When the target of the assignment is an identifier, the assignment statement specifies the binding of a variable. This is never disallowed: when you request it, it takes place. In all other cases, the assignment statement specifies a request to an object to bind one or more of its attributes or items. An object may refuse to create or rebind some (or all) attributes or items, raising an exception if you attempt a disallowed creation or rebinding (see also __setattr__ on page 108 and __setitem__ on page 112).

You can give multiple targets and equals signs (=) in a plain assignment. For example:

  a = b = c = 0

binds variables a, b, and c to the same value, 0. Each time the statement executes, the RHS expression is evaluated just once, no matter how many targets are part of the statement. Each target then gets bound to the single object returned by the expression, just as if several simple assignments executed one after the other.

The target in a plain assignment can list two or more references separated by commas, optionally enclosed in parentheses or brackets. For example:

  a, b, c = x

This statement requires x to be an iterable with exactly three items, and binds a to the first item, b to the second, and c to the third. This kind of assignment is known as an unpacking assignment. The RHS expression must be an iterable with exactly as many items as there are references in the target; otherwise, Python raises an exception. Each reference in the target gets bound to the corresponding item in the RHS. An unpacking assignment can also be used to swap references:

  a, b = b, a

This assignment statement rebinds name a  to what name b  was bound to, and vice versa.

{mospagebreak title=Augmented assignment}

An augmented assignment differs from a plain assignment in that, instead of an equals sign (=) between the target and the expression, it uses an augmented operator, which is a binary operator followed by =. The augmented operators are +=, -=, *=, /=, //=, %=, **=, |=, >>=, <<=, &=, and ^=. An augmented assignment can have only one target on the LHS; augmented assignment doesn’t support multiple targets.

In an augmented assignment, just as in a plain one, Python first evaluates the RHS expression. Then, if the LHS refers to an object that has a special method for the appropriate in-place version of the operator, Python calls the method with the RHS value as its argument. It is up to the method to modify the LHS object appropriately and return the modified object ("Special Methods" on page 104 covers special methods). If the LHS object has no appropriate in-place special method, Python applies the corresponding binary operator to the LHS and RHS objects, then rebinds the target reference to the operator’s result. For example, x+=y  is like x=x.__iadd__(y) when x  has special method __iadd__. Otherwise, x+=y  is like x=x+y .

Augmented assignment never creates its target reference; the target must already be bound when augmented assignment executes. Augmented assignment can rebind the target reference to a new object or modify the same object to which the target reference was already bound. Plain assignment, in contrast, can create or rebind the LHS target reference, but it never modifies the object, if any, to which the target reference was previously bound. The distinction between objects and references to objects is crucial here. For example, x=x+y  does not modify the object to which name x  was originally bound. Rather, it rebinds the name x  to refer to a new object. x+=y , in contrast, modifies the object to which the name x  is bound when that object has special method __iadd__; otherwise, x+=y  rebinds the name x to a new object, just like x=x+y .

del Statements

Despite its name, a del statement does not delete objects; rather, it unbinds references. Object deletion may automatically follow as a consequence, by garbage collection, when no more references to an object exist.

A del statement consists of the keyword del, followed by one or more target references separated by commas (,). Each target can be a variable, attribute reference, indexing, or slicing, just like for assignment statements, and must be bound at the time del executes. When a del target is an identifier, the del statement means to unbind the variable. If the identifier was bound, unbinding it is never disallowed; when requested, it takes place.

In all other cases, the del statement specifies a request to an object to unbind one or more of its attributes or items. An object may refuse to unbind some (or all) attributes or items, raising an exception if you attempt a disallowed unbinding (see also __delattr__ on page 106 and __delitem__ on page 112). Unbinding a slicing normally has the same effect as assigning an empty sequence to that slice, but it is up to the container object to implement this equivalence.

Please check back next week for the continuation of this article.

Google+ Comments

Google+ Comments