diveintopython.org
Python for experienced programmers

 

3.5. Assigning multiple values at once

One of the cooler programming shortcuts in Python is using sequences to assign multiple values at once.

Example 3.6. Assigning multiple values at once

>>> v = ('a', 'b', 'e')
>>> (x, y, z) = v 1
>>> x
'a'
>>> y
'b'
>>> z
'e'
1 v is a tuple of three elements, and (x, y, z) is a tuple of three variables. Assigning one to the other assigns each of the values of v to each of the variables, in order.

This has all sorts of uses. When building reusable modules, you often want to assign names to a range of values. In C or C++, you would use enum and manually list each constant and its associated value, which seems especially tedious when the values are consecutive. In Python, you can use the built-in range function with multi-variable assignment to quickly assign consecutive values.

Example 3.7. Assigning consecutive values

>>> range(7)                                                                    1
[0, 1, 2, 3, 4, 5, 6]
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) 2
>>> MONDAY                                                                      3
0
>>> TUESDAY
1
>>> SUNDAY
6
1 The built-in range function returns a list of integers. In its simplest form, it takes an upper limit and returns a 0-based list counting up to but not including the upper limit. (If you like, you can pass other parameters to specify a base other than 0 and a step other than 1. You can print range.__doc__ for details.)
2 MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, and SUNDAY are the variables we're defining. (This example came from the calendar module, a fun little module which prints calendars, like the UNIX program cal. The calendar module defines integer constants for days of the week.)
3 Now each variable has its value: MONDAY is 0, TUESDAY is 1, and so forth.

Using this technique, you can build functions that return multiple values, simply by returning a tuple of all the values. The caller can treat it as a tuple, or assign the values to individual variables.

Example 3.8. Returning multiple values from a function

>>> import os
>>> os.path.split("/music/ap/mahadeva.mp3")                        1
('/music/ap', 'mahadeva.mp3')
>>> (filepath, filename) = os.path.split("/music/ap/mahadeva.mp3") 2
>>> filepath                                                       3
'/music/ap'
>>> filename                                                       4
'mahadeva.mp3'
>>> (shortname, extension) = os.path.splitext(filename)            5
>>> shortname
'mahadeva'
>>> extension
'.mp3'
1 The os module has lots of useful functions for manipulating files and directories, and os.path has functions for manipulating file paths. The split function splits a full pathname and returns a tuple containing the path and filename.
2 We assign the return value of the split function into a tuple of two variables. Each variable receives the value of the corresponding element of the returned tuple.
3 The first variable, filepath, receives the value of the first element of the tuple returned from split, the file path.
4 The second variable, filename, receives the value of the second element of the tuple returned from split, the filename.
5 os.path also contains a function splitext, which splits a filename and returns a tuple containing the filename and the file extension. We use the same technique to assign each of them to separate variables.
Note
Whenever possible, you should use the functions in os and os.path for file, directory, and path manipulations. These modules are wrappers for platform-specific modules, so functions like os.path.split work on UNIX, Windows, Mac OS, and any other supported Python platform.

There's even more you can do with multi-variable assignment. It works when iterating through a list of tuples, which means you can use it in for loops and list mapping. You might not think that a list of tuples is something you would come across every day, but in fact the items method of a dictionary returns a list of tuples, where each tuple is of the form (key, value). So multi-variable assignment allows you an easy way to iterate through the elements of a dictionary.

Example 3.9. Iterating through a dictionary

>>> for k, v in os.environ.items() 1 2
...     print "%s=%s" % (k, v)
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
PROCESSOR_IDENTIFIER=x86 Family 6 Model 6 Stepping 10, GenuineIntel
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[…snip…]
1 os.environ is a dictionary of the environment variables defined on your system. In Windows, these are your user and system variables accessible from MS-DOS. In UNIX, they are the variables exported in your shell's startup scripts. In Mac OS, there is no concept of environment variables, so this dictionary is empty.
2 os.environ.items() returns a list of tuples: [(key1, value1), (key2, value2), …]. The for loop iterates through this list. The first round, it assigns key1 to k and value1 to v, so k = USERPROFILE and v = C:\Documents and Settings\mpilgrim. The second round, k gets the second key, OS, and v gets the corresponding value, Windows_NT.
Note
Using multi-variable assignment is never strictly necessary. It's a convenient shortcut, and it can make your code more readable, especially when dealing with dictionaries (through the items method). But if you find yourself putting your code through contortions to get data in the right format just so you can assign two variables at once, it's probably not worth it.

Example 3.10. Dictionary mapping through multi-variable assignment

>>> print "\n".join(["%s=%s" % (k, v) for k, v in os.environ.items()]) 1
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
PROCESSOR_IDENTIFIER=x86 Family 6 Model 6 Stepping 10, GenuineIntel
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[…snip…]
1 Multi-variable assignment also works in list mapping, making this a convenient way to map dictionaries into lists. In this case, we're taking it one step further by joining the list into a string.Note that the output is the same as the for loop in the previous example. This is why you see so few for loops in Python; many complex things can be accomplished without them. You can argue whether this way is more readable, but it is definitely faster, because there is only one print statement instead of many.

Example 3.11. Multi-variable for loop in fileinfo.py

    for info in listDirectory("/music/_singles/", [".mp3"]): 1
        for key, value in info.items():                      2
            print "%s=%s" % (key, value)                     3
        print
1 For the time being, trust me that the listDirectory function returns a list of dictionaries. (This is not technically true; it returns a list of objects that act like dictionaries. More on this in the next section.)
2 As we iterate through each dictionary, key gets each dictionary key and value gets the corresponding value.
3 Since all we're doing here is printing out the results, we probably could have collapsed these nested for loops into some heinous-looking list mapping. After a certain point, list mapping becomes obfuscated and you should fall back on a more traditional code structure. Where this point is varies by programmer; I just hit mine.