You are here: Home ‣ Dive Into Python 3 ‣
Difficulty level: ♦♦♢♢♢
❝ FIXME ❞
— FIXME
This chapter will teach you about list comprehensions, dictionary comprehensions, and set comprehensions: three related concepts centered around one very powerful technique. But first, I want to take a little detour into two modules that will help you navigate your local file system.
os modulePython 3 comes with a module called os, which stands for “operating system.” The os module contains a plethora of functions to get information on — and in some cases, to manipulate — local directories, files, processes, and environment variables. Python does its best to offer a unified API across all supported operating systems so your programs can run on any computer with as little platform-specific code as possible.
When you’re just getting started with Python, you’re going to spend a lot of time in the Python Shell. Throughout this book, you will see examples that go like this:
examples folder
If you don’t know about the current working directory, step 1 will probably fail with an ImportError. Why? Because Python will look for the example module in the import search path, but it won’t find it because the examples folder isn’t one of the directories in the search path. To get past this, you can do one of two things:
examples folder to the import search path
examples folder
The current working directory is an invisible property that Python holds in memory at all times. There is always a current working directory, whether you’re in the Python Shell, running your own Python script from the command line, or running a Python CGI script on a web server somewhere.
The os module contains two functions to deal with the current working directory.
>>> import os ① >>> print(os.getcwd()) ② C:\Python31 >>> os.chdir('/Users/pilgrim/diveintopython3/examples') ③ >>> print(os.getcwd()) ④ C:\Users\pilgrim\diveintopython3\examples
os module comes with Python; you can import it anytime, anywhere.
os.getcwd() function to get the current working directory. When you run the graphical Python Shell, the current working directory starts as the directory where the Python Shell executable is. On Windows, this depends on where you installed Python; the default directory is c:\Python31. If you run the Python Shell from the command line, the current working directory starts as the directory you were in when you ran python3.
os.chdir() function to change the current working directory.
os.chdir() function, I used a Linux-style pathname (forward slashes, no drive letter) even though I’m on Windows. This is one of the places where Python tries to paper over the differences between operating systems.
os.path moduleWhile we’re on the subject of directories, I want to point out the os.path submodule. os.path contains functions for manipulating filenames and directory names.
>>> import os
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py')) ①
/Users/pilgrim/diveintopython3/examples/humansize.py
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py')) ②
/Users/pilgrim/diveintopython3/examples\humansize.py
>>> print(os.path.expanduser('~')) ③
c:\Users\pilgrim
>>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py')) ④
c:\Users\pilgrim\diveintopython3\examples\humansize.py
os.path.join() function constructs a pathname out of one or more partial pathnames. In this case, it simply concatenates strings.
join will add an extra backslash to the pathname before joining it to the filename. I was overjoyed when I discovered this, since addSlashIfNecessary() is one of the stupid little functions I always need to write when building up my toolbox in a new language. Do not write this stupid little function in Python; smart people have already taken care of it for you.
os.path.expanduser() function will expand a pathname that uses ~ to represent the current user’s home directory. This works on any platform where users have a home directory, including Linux, Mac OS X, and Windows.
os.path also contains functions to split full pathnames, directory names, and filenames into their constituent parts.
>>> pathname = '/Users/pilgrim/diveintopython3/examples/humansize.py' >>> os.path.split(pathname) ① ('/Users/pilgrim/diveintopython3/examples', 'humansize.py') >>> (dirname, filename) = os.path.split(pathname) ② >>> dirname ③ '/Users/pilgrim/diveintopython3/examples' >>> filename ④ 'humansize.py' >>> (shortname, extension) = os.path.splitext(filename) ⑤ >>> shortname 'humansize' >>> extension '.py'
split function splits a full pathname and returns a tuple containing the path and filename. Remember when I said you could use multi-variable assignment to return multiple values from a function? The os.path.split() function does exactly that.
split function into a tuple of two variables. Each variable receives the value of the corresponding element of the returned tuple.
os.path.split() function, the file path.
os.path.split() function, the filename.
os.path also contains the os.path.splitext() function, which splits a filename and returns a tuple containing the filename and the file extension. You use the same technique to assign each of them to separate variables.
glob moduleThe glob module is another tool in the Python standard library. It’s an easy way to get the contents of a directory programmatically, and it uses the sort of wildcards that you may already be familiar with from working on the command line.
>>> os.chdir('/Users/pilgrim/diveintopython3/')
>>> import glob
>>> glob.glob('examples/*.xml') ①
['examples\\feed-broken.xml',
'examples\\feed-ns0.xml',
'examples\\feed.xml']
>>> os.chdir('examples/') ②
>>> glob.glob('*test*.py') ③
['alphameticstest.py',
'pluraltest1.py',
'pluraltest2.py',
'pluraltest3.py',
'pluraltest4.py',
'pluraltest5.py',
'pluraltest6.py',
'romantest1.py',
'romantest10.py',
'romantest2.py',
'romantest3.py',
'romantest4.py',
'romantest5.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
glob module takes a wildcard and returns the path of all files and directories matching the wildcard. In this example, the wildcard is a directory path plus “*.xml”, which will match all .xml files in the examples subdirectory.
examples subdirectory. The os.chdir() function can take relative pathnames.
.py extension and contain the word test anywhere in their filename.
Now you’re ready to learn about comprehensions.
One of the most powerful features of Python is the list comprehension, which provides a compact way of mapping a list into another list by applying a function to each of the elements of the list.
>>> li = [1, 9, 8, 4] >>> [elem * 2 for elem in li] ① [2, 18, 16, 8] >>> li ② [1, 9, 8, 4] >>> li = [elem * 2 for elem in li] ③ >>> li [2, 18, 16, 8]
elem*2 and appends that result to the returned list.
FIXME Here are the list comprehensions in the buildConnectionString function that you declared in Chapter 2:
["%s=%s" % (k, v) for k, v in params.items()]
First, notice that you're calling the items function of the params dictionary. This function returns a list of tuples of all the data in the dictionary.
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.keys() ①
['server', 'uid', 'database', 'pwd']
>>> params.values() ②
['mpilgrim', 'sa', 'master', 'secret']
>>> params.items() ③
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
keys method of a dictionary returns a list of all the keys. The list is not in the order in which the dictionary was defined
(remember that elements in a dictionary are unordered), but it is a list.
values method returns a list of all the values. The list is in the same order as the list returned by keys, so params.values()[n] == params[params.keys()[n]] for all values of n.
items method returns a list of tuples of the form (key, value). The list contains all the data in the dictionary.
Now let's see what buildConnectionString does. It takes a list, params., and maps it to a new list by applying string formatting to each element. The new list will have the same number of elements
as items()params., but each element in the new list will be a string that contains both a key and its associated value from the params dictionary.
items()
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
>>> [k for k, v in params.items()] ①
['server', 'uid', 'database', 'pwd']
>>> [v for k, v in params.items()] ②
['mpilgrim', 'sa', 'master', 'secret']
>>> ["%s=%s" % (k, v) for k, v in params.items()] ③
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
params.items() list. This is another use of multi-variable assignment. The first element of params.items() is ('server', 'mpilgrim'), so in the first iteration of the list comprehension, k will get 'server' and v will get 'mpilgrim'. In this case, you're ignoring the value of v and only including the value of k in the returned list, so this list comprehension ends up being equivalent to params.keys().
params.values().
FIXME
>>> print("\n".join(["{0:>8} {1}".format(humansize.approximate_size(os.stat(f).st_size, False), os.path.abspath(f)) for f in glob.glob('*.py')]))
2.5 KB c:\Users\pilgrim\diveintopython3\examples\alphametics.py
2.5 KB c:\Users\pilgrim\diveintopython3\examples\alphameticstest.py
1.5 KB c:\Users\pilgrim\diveintopython3\examples\fibonacci.py
1.8 KB c:\Users\pilgrim\diveintopython3\examples\fibonacci2.py
2.5 KB c:\Users\pilgrim\diveintopython3\examples\humansize.py
0.2 KB c:\Users\pilgrim\diveintopython3\examples\oneline.py
1.9 KB c:\Users\pilgrim\diveintopython3\examples\plural1.py
2.3 KB c:\Users\pilgrim\diveintopython3\examples\plural2.py
2.3 KB c:\Users\pilgrim\diveintopython3\examples\plural3.py
2.3 KB c:\Users\pilgrim\diveintopython3\examples\plural4.py
2.4 KB c:\Users\pilgrim\diveintopython3\examples\plural5.py
2.8 KB c:\Users\pilgrim\diveintopython3\examples\plural6.py
3.0 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest1.py
3.0 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest2.py
3.0 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest3.py
3.0 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest4.py
3.0 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest5.py
6.1 KB c:\Users\pilgrim\diveintopython3\examples\pluraltest6.py
0.5 KB c:\Users\pilgrim\diveintopython3\examples\regression.py
2.2 KB c:\Users\pilgrim\diveintopython3\examples\roman1.py
3.4 KB c:\Users\pilgrim\diveintopython3\examples\roman10.py
2.3 KB c:\Users\pilgrim\diveintopython3\examples\roman2.py
2.3 KB c:\Users\pilgrim\diveintopython3\examples\roman3.py
2.5 KB c:\Users\pilgrim\diveintopython3\examples\roman4.py
2.7 KB c:\Users\pilgrim\diveintopython3\examples\roman5.py
3.6 KB c:\Users\pilgrim\diveintopython3\examples\roman6.py
3.7 KB c:\Users\pilgrim\diveintopython3\examples\roman7.py
3.7 KB c:\Users\pilgrim\diveintopython3\examples\roman8.py
3.7 KB c:\Users\pilgrim\diveintopython3\examples\roman9.py
4.0 KB c:\Users\pilgrim\diveintopython3\examples\romantest1.py
6.7 KB c:\Users\pilgrim\diveintopython3\examples\romantest10.py
4.2 KB c:\Users\pilgrim\diveintopython3\examples\romantest2.py
4.5 KB c:\Users\pilgrim\diveintopython3\examples\romantest3.py
4.7 KB c:\Users\pilgrim\diveintopython3\examples\romantest4.py
5.3 KB c:\Users\pilgrim\diveintopython3\examples\romantest5.py
6.1 KB c:\Users\pilgrim\diveintopython3\examples\romantest6.py
6.3 KB c:\Users\pilgrim\diveintopython3\examples\romantest7.py
6.5 KB c:\Users\pilgrim\diveintopython3\examples\romantest8.py
6.6 KB c:\Users\pilgrim\diveintopython3\examples\romantest9.py
0.4 KB c:\Users\pilgrim\diveintopython3\examples\stdout.py
⁂
FIXME
⁂
FIXME
⁂
© 2001–9 Mark Pilgrim