Knowing this, you can design a test suite for your module within the module itself by putting it in this if statement. When you run the module directly, __name__ is __main__, so the test suite executes. When you import the module, __name__ is something else, so the test suite is ignored. This makes it easier to develop and debug new modules before integrating
them into a larger program.
 | On MacPython, there is an additional step to make the if __name__ trick work. Pop up the module's options menu by clicking the black triangle in the upper-right corner of the window, and
make sure Run as __main__ is checked.
Further Reading on Importing Modules
3.4. Declaring variables
Now that you know something about dictionaries, tuples, and lists (oh my!), let's get back to the sample program from Chapter 2, odbchelper.py.
Python has local and global variables like most other languages, but it has no explicit variable declarations. Variables spring
into existence by being assigned a value, and they are automatically destroyed when they go out of scope.
Example 3.17. Defining the myParams Variable
if __name__ == "__main__":
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret" \
}
Notice the indentation. An if statement is a code block and needs to be indented just like a function.
Also notice that the variable assignment is one command split over several lines, with a backslash (“\”) serving as a line-continuation marker.
 | When a command is split among several lines with the line-continuation marker (“\”), the continued lines can be indented in any manner; Python's normally stringent indentation rules do not apply. If your Python IDE auto-indents the continued line, you should probably accept its default unless you have a burning reason not to.
Strictly speaking, expressions in parentheses, straight brackets, or curly braces (like defining a dictionary) can be split into multiple lines with or without the line continuation character (“\”). I like to include the backslash even when it's not required because I think it makes the code easier to read, but that's
a matter of style.
Third, you never declared the variable myParams, you just assigned a value to it. This is like VBScript without the option explicit option. Luckily, unlike VBScript, Python will not allow you to reference a variable that has never been assigned a value; trying to do so will raise an exception.
3.4.1. Referencing Variables
Example 3.18. Referencing an Unbound Variable>>> x
Traceback (innermost last):
File "<interactive input>", line 1, in ?
NameError: There is no variable named 'x'
>>> x = 1
>>> x
1 You will thank Python for this one day.
3.4.2. Assigning Multiple Values at Once
One of the cooler programming shortcuts in Python is using sequences to assign multiple values at once.
Example 3.19. Assigning multiple values at once>>> v = ('a', 'b', 'e')
>>> (x, y, z) = v ①
>>> x
'a'
>>> y
'b'
>>> z
'e'
- 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. I often want to assign names to a range of values. In 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.20. Assigning Consecutive Values>>> range(7) ①
[0, 1, 2, 3, 4, 5, 6]
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) ②
>>> MONDAY ③
0
>>> TUESDAY
1
>>> SUNDAY
6
- The built-in
range function returns a list of integers. In its simplest form, it takes an upper limit and returns a zero-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.)
- MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, and SUNDAY are the variables you're defining. (This example came from the
calendar module, a fun little module that prints calendars, like the UNIX program cal. The calendar module defines integer constants for days of the week.)
- Now each variable has its value: MONDAY is
0, TUESDAY is 1, and so forth.
You can also use multi-variable assignment to 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. Many standard Python libraries do this, including the os module, which you'll discuss in Chapter 6.
Further Reading on Variables
3.6. Mapping Lists
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.
Example 3.24. Introducing List Comprehensions>>> 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]
- To make sense of this, look at it from right to left. li is the list you're mapping. Python loops through li one element at a time, temporarily assigning the value of each element to the variable elem. Python then applies the function
elem*2 and appends that result to the returned list.
- Note that list comprehensions do not change the original list.
- It is safe to assign the result of a list comprehension to the variable that you're mapping. Python constructs the new list in memory, and when the list comprehension is complete, it assigns the result to the variable.
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.
Example 3.25. The keys, values, and items Functions>>> 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')]
- The
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.
- The
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.
- The
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.items(), 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 params.items(), but each element in the new list will be a string that contains both a key and its associated value from the params dictionary.
Example 3.26. List Comprehensions in buildConnectionString, Step by Step>>> 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']
- Note that you're using two variables to iterate through the
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().
- Here you're doing the same thing, but ignoring the value of k, so this list comprehension ends up being equivalent to
params.values().
- Combining the previous two examples with some simple string formatting, you get a list of strings that include both the key and value of each element of the dictionary. This looks suspiciously
like the output of the program. All that remains is to join the elements in this list into a single string.
Further Reading on List Comprehensions
(String splitting stuff was here)
Before diving into the next chapter, make sure you're comfortable doing all of these things:
- Using the Python IDE to test expressions interactively
- Writing Python programs and running them from within your IDE, or from the command line
- Importing modules and calling their functions
- Declaring functions and using
docstrings, local variables, and proper indentation
- Defining dictionaries, tuples, and lists
- Accessing attributes and methods of any object, including strings, lists, dictionaries, functions, and modules
- Concatenating values through string formatting
- Mapping lists into other lists using list comprehensions
- Splitting strings into lists and joining lists into strings
Chapter 4. The Power Of Introspection
This chapter covers one of Python's strengths: introspection. As you know, everything in Python is an object, and introspection is code looking at other modules and functions in memory as objects, getting information about them, and
manipulating them. Along the way, you'll define functions with no name, call functions with arguments out of order, and reference
functions whose names you don't even know ahead of time.
4.1. Diving In
Here is a complete, working Python program. You should understand a good deal about it just by looking at it. The numbered lines illustrate concepts covered
in Chapter 2, Your First Python Program. Don't worry if the rest of the code looks intimidating; you'll learn all about it throughout this chapter.
Example 4.1. apihelper.py
If you have not already done so, you can download this and other examples used in this book.
def info(object, spacing=10, collapse=1): ① ② ③
"""Print methods and docstrings.
Takes module, class, list, dictionary, or string."""
methodList = [method for method in dir(object) if callable(getattr(object, method))]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
if __name__ == "__main__": ④ ⑤
print info.__doc__
- This module has one function,
info. According to its function declaration, it takes three parameters: object, spacing, and collapse. The last two are actually optional parameters, as you'll see shortly.
- The
info function has a multi-line docstring that succinctly describes the function's purpose. Note that no return value is mentioned; this function will be used solely
for its effects, rather than its value.
- Code within the function is indented.
- The
if __name__ trick allows this program do something useful when run by itself, without interfering with its use as a module for other programs.
In this case, the program simply prints out the docstring of the info function.
if statements use == for comparison, and parentheses are not required.
The info function is designed to be used by you, the programmer, while working in the Python IDE. It takes any object that has functions or methods (like a module, which has functions, or a list, which has methods) and
prints out the functions and their docstrings.
Example 4.2. Sample Usage of apihelper.py>>> from apihelper import info
>>> li = []
>>> info(li)
append L.append(object) -- append object to end
count L.count(value) -> integer -- return number of occurrences of value
extend L.extend(list) -- extend list by appending list elements
index L.index(value) -> integer -- return index of first occurrence of value
insert L.insert(index, object) -- insert object before index
pop L.pop([index]) -> item -- remove and return item at index (default last)
remove L.remove(value) -- remove first occurrence of value
reverse L.reverse() -- reverse *IN PLACE*
sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1 By default the output is formatted to be easy to read. Multi-line docstrings are collapsed into a single long line, but this option can be changed by specifying 0 for the collapse argument. If the function names are longer than 10 characters, you can specify a larger value for the spacing argument to make the output easier to read.
Example 4.3. Advanced Usage of apihelper.py>>> import odbchelper
>>> info(odbchelper)
buildConnectionString Build a connection string from a dictionary Returns string.
>>> info(odbchelper, 30)
buildConnectionString Build a connection string from a dictionary Returns string.
>>> info(odbchelper, 30, 0)
buildConnectionString Build a connection string from a dictionary
Returns string.
(optional and named arguments stuff was here)
4.3.1. The type Function
The type function returns the datatype of any arbitrary object. The possible types are listed in the types module. This is useful for helper functions that can handle several types of data.
Example 4.5. Introducing type>>> type(1) ①
<type 'int'>
>>> li = []
>>> type(li) ②
<type 'list'>
>>> import odbchelper
>>> type(odbchelper) ③
<type 'module'>
>>> import types ④
>>> type(odbchelper) == types.ModuleType
True
type takes anything -- and I mean anything -- and returns its datatype. Integers, strings, lists, dictionaries, tuples, functions,
classes, modules, even types are acceptable.
type can take a variable and return its datatype.
type also works on modules.
- You can use the constants in the
types module to compare types of objects. This is what the info function does, as you'll see shortly.
4.3.2. The str Function
The str coerces data into a string. Every datatype can be coerced into a string.
Example 4.6. Introducing str
>>> str(1) ①
'1'
>>> horsemen = ['war', 'pestilence', 'famine']
>>> horsemen
['war', 'pestilence', 'famine']
>>> horsemen.append('Powerbuilder')
>>> str(horsemen) ②
"['war', 'pestilence', 'famine', 'Powerbuilder']"
>>> str(odbchelper) ③
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"
>>> str(None) ④
'None'
- For simple datatypes like integers, you would expect
str to work, because almost every language has a function to convert an integer to a string.
- However,
str works on any object of any type. Here it works on a list which you've constructed in bits and pieces.
str also works on modules. Note that the string representation of the module includes the pathname of the module on disk, so
yours will be different.
- A subtle but important behavior of
str is that it works on None, the Python null value. It returns the string 'None'. You'll use this to your advantage in the info function, as you'll see shortly.
At the heart of the info function is the powerful dir function. dir returns a list of the attributes and methods of any object: modules, functions, strings, lists, dictionaries... pretty much
anything.
Example 4.7. Introducing dir>>> li = []
>>> dir(li) ①
['append', 'count', 'extend', 'index', 'insert',
'pop', 'remove', 'reverse', 'sort']
>>> d = {}
>>> dir(d) ②
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values']
>>> import odbchelper
>>> dir(odbchelper) ③
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
- li is a list, so
dir(li) returns a list of all the methods of a list. Note that the returned list contains the names of the methods as strings, not
the methods themselves.
- d is a dictionary, so
dir(d) returns a list of the names of dictionary methods. At least one of these, keys, should look familiar.
- This is where it really gets interesting.
odbchelper is a module, so dir(odbchelper) returns a list of all kinds of stuff defined in the module, including built-in attributes, like __name__, __doc__, and whatever other attributes and methods you define. In this case, odbchelper has only one user-defined method, the buildConnectionString function described in Chapter 2.
Finally, the callable function takes any object and returns True if the object can be called, or False otherwise. Callable objects include functions, class methods, even classes themselves. (More on classes in the next chapter.)
Example 4.8. Introducing callable
>>> import string
>>> string.punctuation ①
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>> string.join②
<function join at 00C55A7C>
>>> callable(string.punctuation) ③
False
>>> callable(string.join) ④
True
>>> print string.join.__doc__ ⑤
join(list [,sep]) -> string
Return a string composed of the words in list, with
intervening occurrences of sep. The default separator is a
single space.
(joinfields and join are synonymous)
- The functions in the
string module are deprecated (although many people still use the join function), but the module contains a lot of useful constants like this string.punctuation, which contains all the standard punctuation characters.
string.join is a function that joins a list of strings.
- string.punctuation is not callable; it is a string. (A string does have callable methods, but the string itself is not callable.)
string.join is callable; it's a function that takes two arguments.
- Any callable object may have a
docstring. By using the callable function on each of an object's attributes, you can determine which attributes you care about (methods, functions, classes)
and which you want to ignore (constants and so on) without knowing anything about the object ahead of time.
4.3.3. Built-In Functions
type, str, dir, and all the rest of Python's built-in functions are grouped into a special module called __builtin__. (That's two underscores before and after.) If it helps, you can think of Python automatically executing from __builtin__ import * on startup, which imports all the “built-in” functions into the namespace so you can use them directly.
The advantage of thinking like this is that you can access all the built-in functions and attributes as a group by getting
information about the __builtin__ module. And guess what, Python has a function called info. Try it yourself and skim through the list now. We'll dive into some of the more important functions later. (Some of the
built-in error classes, like AttributeError, should already look familiar.)
Example 4.9. Built-in Attributes and Functions>>> from apihelper import info
>>> import __builtin__
>>> info(__builtin__, 20)
ArithmeticError Base class for arithmetic errors.
AssertionError Assertion failed.
AttributeError Attribute not found.
EOFError Read beyond end of file.
EnvironmentError Base class for I/O related errors.
Exception Common base class for all exceptions.
FloatingPointError Floating point operation failed.
IOError I/O operation failed.
[...snip...]
 | Python comes with excellent reference manuals, which you should peruse thoroughly to learn all the modules Python has to offer. But unlike most languages, where you would find yourself referring back to the manuals or man pages to remind
yourself how to use these modules, Python is largely self-documenting.
Further Reading on Built-In Functions
4.4. Getting Object References With getattr
You already know that Python functions are objects. What you don't know is that you can get a reference to a function without knowing its name until run-time, by using the
getattr function.
Example 4.10. Introducing getattr>>> li = ["Larry", "Curly"]
>>> li.pop ①
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop") ②
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe") ③
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear") ④
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop") ⑤
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'
- This gets a reference to the
pop method of the list. Note that this is not calling the pop method; that would be li.pop(). This is the method itself.
- This also returns a reference to the
pop method, but this time, the method name is specified as a string argument to the getattr function. getattr is an incredibly useful built-in function that returns any attribute of any object. In this case, the object is a list,
and the attribute is the pop method.
- In case it hasn't sunk in just how incredibly useful this is, try this: the return value of
getattr is the method, which you can then call just as if you had said li.append("Moe") directly. But you didn't call the function directly; you specified the function name as a string instead.
getattr also works on dictionaries.
- In theory,
getattr would work on tuples, except that tuples have no methods, so getattr will raise an exception no matter what attribute name you give.
4.4.1. getattr with Modules
getattr isn't just for built-in datatypes. It also works on modules.
Example 4.11. The getattr Function in apihelper.py>>> import odbchelper
>>> odbchelper.buildConnectionString ①
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString") ②
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method) ③
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method)) ④
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
True
>>> callable(getattr(object, method)) ⑤
True
- This returns a reference to the
buildConnectionString function in the odbchelper module, which you studied in Chapter 2, Your First Python Program. (The hex address you see is specific to my machine; your output will be different.)
- Using
getattr, you can get the same reference to the same function. In general, getattr(object, "attribute") is equivalent to object.attribute. If object is a module, then attribute can be anything defined in the module: a function, class, or global variable.
- And this is what you actually use in the
info function. object is passed into the function as an argument; method is a string which is the name of a method or function.
- In this case, method is the name of a function, which you can prove by getting its
type.
- Since method is a function, it is callable.
4.4.2. getattr As a Dispatcher
A common usage pattern of getattr is as a dispatcher. For example, if you had a program that could output data in a variety of different formats, you could
define separate functions for each output format and use a single dispatch function to call the right one.
For example, let's imagine a program that prints site statistics in HTML, XML, and plain text formats. The choice of output format could be specified on the command line, or stored in a configuration
file. A statsout module defines three functions, output_html, output_xml, and output_text. Then the main program defines a single output function, like this:
Example 4.12. Creating a Dispatcher with getattr
import statsout
def output(data, format="text"): ①
output_function = getattr(statsout, "output_%s" % format) ②
return output_function(data) ③
- The
output function takes one required argument, data, and one optional argument, format. If format is not specified, it defaults to text, and you will end up calling the plain text output function.
- You concatenate the format argument with "output_" to produce a function name, and then go get that function from the
statsout module. This allows you to easily extend the program later to support other output formats, without changing this dispatch
function. Just add another function to statsout named, for instance, output_pdf, and pass "pdf" as the format into the output function.
- Now you can simply call the output function in the same way as any other function. The output_function variable is a reference to the appropriate function from the
statsout module.
Did you see the bug in the previous example? This is a very loose coupling of strings and functions, and there is no error
checking. What happens if the user passes in a format that doesn't have a corresponding function defined in statsout? Well, getattr will return None, which will be assigned to output_function instead of a valid function, and the next line that attempts to call that function will crash and raise an exception. That's
bad.
Luckily, getattr takes an optional third argument, a default value.
Example 4.13. getattr Default Values
import statsout
def output(data, format="text"):
output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
return output_function(data) ①
- This function call is guaranteed to work, because you added a third argument to the call to
getattr. The third argument is a default value that is returned if the attribute or method specified by the second argument wasn't
found.
As you can see, getattr is quite powerful. It is the heart of introspection, and you'll see even more powerful examples of it in later chapters.
4.5. Filtering Lists
As you know, Python has powerful capabilities for mapping lists into other lists, via list comprehensions (Section 3.6, “Mapping Lists”). This can be combined with a filtering mechanism, where some elements in the list are mapped while others are skipped entirely.
Here is the list filtering syntax:
[mapping-expression for element in source-list if filter-expression]
This is an extension of the list comprehensions that you know and love. The first two thirds are the same; the last part, starting with the if, is the filter expression. A filter expression can be any expression that evaluates true or false (which in Python can be almost anything). Any element for which the filter expression evaluates true will be included in the mapping. All other elements are ignored,
so they are never put through the mapping expression and are not included in the output list.
Example 4.14. Introducing List Filtering>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"]
>>> [elem for elem in li if len(elem) > 1] ①
['mpilgrim', 'foo']
>>> [elem for elem in li if elem != "b"] ②
['a', 'mpilgrim', 'foo', 'c', 'd', 'd']
>>> [elem for elem in li if li.count(elem) == 1] ③
['a', 'mpilgrim', 'foo', 'c']
- The mapping expression here is simple (it just returns the value of each element), so concentrate on the filter expression.
As Python loops through the list, it runs each element through the filter expression. If the filter expression is true, the element
is mapped and the result of the mapping expression is included in the returned list. Here, you are filtering out all the
one-character strings, so you're left with a list of all the longer strings.
- Here, you are filtering out a specific value,
b. Note that this filters all occurrences of b, since each time it comes up, the filter expression will be false.
count is a list method that returns the number of times a value occurs in a list. You might think that this filter would eliminate
duplicates from a list, returning a list containing only one copy of each value in the original list. But it doesn't, because
values that appear twice in the original list (in this case, b and d) are excluded completely. There are ways of eliminating duplicates from a list, but filtering is not the solution.
Let's id="apihelper.filter.care" get back to this line from apihelper.py:
methodList = [method for method in dir(object) if callable(getattr(object, method))]
This looks complicated, and it is complicated, but the basic structure is the same. The whole filter expression returns a
list, which is assigned to the methodList variable. The first half of the expression is the list mapping part. The mapping expression is an identity expression,
which it returns the value of each element. dir(object) returns a list of object's attributes and methods -- that's the list you're mapping. So the only new part is the filter expression after the if.
The filter expression looks scary, but it's not. You already know about callable, getattr, and in. As you saw in the previous section, the expression getattr(object, method) returns a function object if object is a module and method is the name of a function in that module.
So this expression takes an object (named object). Then it gets a list of the names of the object's attributes, methods, functions, and a few other things. Then it filters
that list to weed out all the stuff that you don't care about. You do the weeding out by taking the name of each attribute/method/function
and getting a reference to the real thing, via the getattr function. Then you check to see if that object is callable, which will be any methods and functions, both built-in (like
the pop method of a list) and user-defined (like the buildConnectionString function of the odbchelper module). You don't care about other attributes, like the __name__ attribute that's built in to every module.
Further Reading on Filtering Lists
4.6. The Peculiar Nature of and and or
In Python, and and or perform boolean logic as you would expect, but they do not return boolean values; instead, they return one of the actual
values they are comparing.
Example 4.15. Introducing and>>> 'a' and 'b' ①
'b'
>>> '' and 'b' ②
''
>>> 'a' and 'b' and 'c' ③
'c'
- When using
and, values are evaluated in a boolean context from left to right. 0, '', [], (), {}, and None are false in a boolean context; everything else is true. Well, almost everything. By default, instances of classes are
true in a boolean context, but you can define special methods in your class to make an instance evaluate to false. You'll
learn all about classes and special methods in Chapter 5. If all values are true in a boolean context, and returns the last value. In this case, and evaluates 'a', which is true, then 'b', which is true, and returns 'b'.
- If any value is false in a boolean context,
and returns the first false value. In this case, '' is the first false value.
- All values are true, so
and returns the last value, 'c'.
Example 4.16. Introducing or>>> 'a' or 'b' ①
'a'
>>> '' or 'b' ②
'b'
>>> '' or [] or {} ③
{}
>>> def sidefx():
... print "in sidefx()"
... return 1
>>> 'a' or sidefx() ④
'a'
- When using
or, values are evaluated in a boolean context from left to right, just like and. If any value is true, or returns that value immediately. In this case, 'a' is the first true value.
or evaluates '', which is false, then 'b', which is true, and returns 'b'.
- If all values are false,
or returns the last value. or evaluates '', which is false, then [], which is false, then {}, which is false, and returns {}.
- Note that
or evaluates values only until it finds one that is true in a boolean context, and then it ignores the rest. This distinction
is important if some values can have side effects. Here, the function sidefx is never called, because or evaluates 'a', which is true, and returns 'a' immediately.
If you're a C hacker, you are certainly familiar with the bool ? a : b expression, which evaluates to a if bool is true, and b otherwise. Because of the way and and or work in Python, you can accomplish the same thing.
4.6.1. Using the and-or Trick
Example 4.17. Introducing the and-or Trick>>> a = "first"
>>> b = "second"
>>> 1 and a or b ①
'first'
>>> 0 and a or b ②
'second'
- This syntax looks similar to the
bool ? a : b expression in C. The entire expression is evaluated from left to right, so the and is evaluated first. 1 and 'first' evalutes to 'first', then 'first' or 'second' evalutes to 'first'.
0 and 'first' evalutes to False, and then 0 or 'second' evaluates to 'second'.
However, since this Python expression is simply boolean logic, and not a special construct of the language, there is one extremely important difference
between this and-or trick in Python and the bool ? a : b syntax in C. If the value of a is false, the expression will not work as you would expect it to. (Can you tell I was bitten by this? More than once?)
Example 4.18. When the and-or Trick Fails>>> a = ""
>>> b = "second"
>>> 1 and a or b ①
'second'
- Since a is an empty string, which Python considers false in a boolean context,
1 and '' evalutes to '', and then '' or 'second' evalutes to 'second'. Oops! That's not what you wanted.
The and-or trick, bool and a or b, will not work like the C expression bool ? a : b when a is false in a boolean context.
The real trick behind the and-or trick, then, is to make sure that the value of a is never false. One common way of doing this is to turn a into [a] and b into [b], then taking the first element of the returned list, which will be either a or b.
Example 4.19. Using the and-or Trick Safely>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b])[0] ①
''
- Since
[a] is a non-empty list, it is never false. Even if a is 0 or '' or some other false value, the list [a] is true because it has one element.
By now, this trick may seem like more trouble than it's worth. You could, after all, accomplish the same thing with an if statement, so why go through all this fuss? Well, in many cases, you are choosing between two constant values, so you can
use the simpler syntax and not worry, because you know that the a value will always be true. And even if you need to use the more complicated safe form, there are good reasons to do so.
For example, there are some cases in Python where if statements are not allowed, such as in lambda functions.
Further Reading on the and-or Trick
4.7. Using lambda Functions
Python supports an interesting syntax that lets you define one-line mini-functions on the fly. Borrowed from Lisp, these so-called lambda functions can be used anywhere a function is required.
Example 4.20. Introducing lambda Functions>>> def f(x):
... return x*2
...
>>> f(3)
6
>>> g = lambda x: x*2 ①
>>> g(3)
6
>>> (lambda x: x*2)(3) ②
6
- This is a
lambda function that accomplishes the same thing as the normal function above it. Note the abbreviated syntax here: there are no
parentheses around the argument list, and the return keyword is missing (it is implied, since the entire function can only be one expression). Also, the function has no name,
but it can be called through the variable it is assigned to.
- You can use a
lambda function without even assigning it to a variable. This may not be the most useful thing in the world, but it just goes to
show that a lambda is just an in-line function.
To generalize, a lambda function is a function that takes any number of arguments (including optional arguments) and returns the value of a single expression. lambda functions can not contain commands, and they can not contain more than one expression. Don't try to squeeze too much into
a lambda function; if you need something more complex, define a normal function instead and make it as long as you want.
 | lambda functions are a matter of style. Using them is never required; anywhere you could use them, you could define a separate
normal function and use that instead. I use them in places where I want to encapsulate specific, non-reusable code without
littering my code with a lot of little one-line functions.
4.7.1. Real-World lambda Functions
Here are the lambda functions in apihelper.py:
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
Notice that this uses the simple form of the and-or trick, which is okay, because a lambda function is always true in a boolean context. (That doesn't mean that a lambda function can't return a false value. The function is always true; its return value could be anything.)
Also notice that you're using the split function with no arguments. You've already seen it used with one or two arguments, but without any arguments it splits on whitespace.
Example 4.21. split With No Arguments>>> s = "this is\na\ttest" ①
>>> print s
this is
a test
>>> print s.split() ②
['this', 'is', 'a', 'test']
>>> print " ".join(s.split()) ③
'this is a test'
- This is a multiline string, defined by escape characters instead of triple quotes.
\n is a carriage return, and \t is a tab character.
split without any arguments splits on whitespace. So three spaces, a carriage return, and a tab character are all the same.
- You can normalize whitespace by splitting a string with
split and then rejoining it with join, using a single space as a delimiter. This is what the info function does to collapse multi-line docstrings into a single line.
So what is the info function actually doing with these lambda functions, splits, and and-or tricks?
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)processFunc is now a function, but which function it is depends on the value of the collapse variable. If collapse is true, processFunc(string) will collapse whitespace; otherwise, processFunc(string) will return its argument unchanged.
To do this in a less robust language, like Visual Basic, you would probably create a function that took a string and a collapse argument and used an if statement to decide whether to collapse the whitespace or not, then returned the appropriate value. This would be inefficient,
because the function would need to handle every possible case. Every time you called it, it would need to decide whether
to collapse whitespace before it could give you what you wanted. In Python, you can take that decision logic out of the function and define a lambda function that is custom-tailored to give you exactly (and only) what you want. This is more efficient, more elegant, and
less prone to those nasty oh-I-thought-those-arguments-were-reversed kinds of errors.
Further Reading on lambda Functions
4.8. Putting It All Together
The last line of code, the only one you haven't deconstructed yet, is the one that does all the work. But by now the work
is easy, because everything you need is already set up just the way you need it. All the dominoes are in place; it's time
to knock them down.
This is the meat of apihelper.py:
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
Note that this is one command, split over multiple lines, but it doesn't use the line continuation character (\). Remember when I said that some expressions can be split into multiple lines without using a backslash? A list comprehension is one of those expressions, since the entire expression is contained in
square brackets.
Now, let's take it from the end and work backwards. The
for method in methodList
shows that this is a list comprehension. As you know, methodList is a list of all the methods you care about in object. So you're looping through that list with method.
Example 4.22. Getting a docstring Dynamically>>> import odbchelper
>>> object = odbchelper ①
>>> method = 'buildConnectionString' ②
>>> getattr(object, method) ③
<function buildConnectionString at 010D6D74>
>>> print getattr(object, method).__doc__ ④
Build a connection string from a dictionary of parameters.
Returns string.
- In the
info function, object is the object you're getting help on, passed in as an argument.
- As you're looping through methodList, method is the name of the current method.
- Using the
getattr function, you're getting a reference to the method function in the object module.
- Now, printing the actual
docstring of the method is easy.
The next piece of the puzzle is the use of str around the docstring. As you may recall, str is a built-in function that coerces data into a string. But a docstring is always a string, so why bother with the str function? The answer is that not every function has a docstring, and if it doesn't, its __doc__ attribute is None.
Example 4.23. Why Use str on a docstring?>>> >>> def foo(): print 2
>>> >>> foo()
2
>>> >>> foo.__doc__ ①
>>> foo.__doc__ == None ②
True
>>> str(foo.__doc__) ③
'None'
- You can easily define a function that has no
docstring, so its __doc__ attribute is None. Confusingly, if you evaluate the __doc__ attribute directly, the Python IDE prints nothing at all, which makes sense if you think about it, but is still unhelpful.
- You can verify that the value of the
__doc__ attribute is actually None by comparing it directly.
- The
str function takes the null value and returns a string representation of it, 'None'.
 | In SQL, you must use IS NULL instead of = NULL to compare a null value. In Python, you can use either == None or is None, but is None is faster.
Now that you are guaranteed to have a string, you can pass the string to processFunc, which you have already defined as a function that either does or doesn't collapse whitespace. Now you see why it was important to use str to convert a None value into a string representation. processFunc is assuming a string argument and calling its split method, which would crash if you passed it None because None doesn't have a split method.
Stepping back even further, you see that you're using string formatting again to concatenate the return value of processFunc with the return value of method's ljust method. This is a new string method that you haven't seen before.
Example 4.24. Introducing ljust>>> s = 'buildConnectionString'
>>> s.ljust(30) ①
'buildConnectionString '
>>> s.ljust(20) ②
'buildConnectionString'
ljust pads the string with spaces to the given length. This is what the info function uses to make two columns of output and line up all the docstrings in the second column.
- If the given length is smaller than the length of the string,
ljust will simply return the string unchanged. It never truncates the string.
You're almost finished. Given the padded method name from the ljust method and the (possibly collapsed) docstring from the call to processFunc, you concatenate the two and get a single string. Since you're mapping methodList, you end up with a list of strings. Using the join method of the string "\n", you join this list into a single string, with each element of the list on a separate line, and print the result.
Example 4.25. Printing a List>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) ①
a
b
c
- This is also a useful debugging trick when you're working with lists. And in Python, you're always working with lists.
That's the last piece of the puzzle. You should now understand this code.
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
4.9. Summary
The apihelper.py program and its output should now make perfect sense.
def info(object, spacing=10, collapse=1):
"""Print methods and docstrings.
Takes module, class, list, dictionary, or string."""
methodList = [method for method in dir(object) if callable(getattr(object, method))]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
if __name__ == "__main__":
print info.__doc__
Here is the output of apihelper.py: >>> from apihelper import info
>>> li = []
>>> info(li)
append L.append(object) -- append object to end
count L.count(value) -> integer -- return number of occurrences of value
extend L.extend(list) -- extend list by appending list elements
index L.index(value) -> integer -- return index of first occurrence of value
insert L.insert(index, object) -- insert object before index
pop L.pop([index]) -> item -- remove and return item at index (default last)
remove L.remove(value) -- remove first occurrence of value
reverse L.reverse() -- reverse *IN PLACE*
sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1
Before diving into the next chapter, make sure you're comfortable doing all of these things:
- Defining and calling functions with optional and named arguments
- Using
str to coerce any arbitrary value into a string representation
- Using
getattr to get references to functions and other attributes dynamically
- Extending the list comprehension syntax to do list filtering
- Recognizing the
and-or trick and using it safely
- Defining
lambda functions
- Assigning functions to variables and calling the function by referencing the variable. I can't emphasize this enough, because this mode of thought is vital
to advancing your understanding of Python. You'll see more complex applications of this concept throughout this book.
Chapter 5. Objects and Object-Orientation
This chapter, and pretty much every chapter after this, deals with object-oriented Python programming.
5.1. Diving In
Here is a complete, working Python program. Read the docstrings of the module, the classes, and the functions to get an overview of what this program does and how it works. As usual, don't
worry about the stuff you don't understand; that's what the rest of the chapter is for.
Example 5.1. fileinfo.py
If you have not already done so, you can download this and other examples used in this book.
"""Framework for getting filetype-specific metadata.
Instantiate appropriate class with filename. Returned object acts like a
dictionary, with key-value pairs for each piece of metadata.
import fileinfo
info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3")
print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()])
Or use listDirectory function to get info on all files in a directory.
for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
...
Framework can be extended by adding classes for particular file types, e.g.
HTMLFileInfo, MPGFileInfo, DOCFileInfo. Each class is completely responsible for
parsing its files appropriately; see MP3FileInfo for example.
"""
import os
import sys
from UserDict import UserDict
def stripnulls(data):
"strip whitespace and nulls"
return data.replace("\00", "").strip()
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
class MP3FileInfo(FileInfo):
"store ID3v1.0 MP3 tags"
tagDataMap = {"title" : ( 3, 33, stripnulls),
"artist" : ( 33, 63, stripnulls),
"album" : ( 63, 93, stripnulls),
"year" : ( 93, 97, stripnulls),
"comment" : ( 97, 126, stripnulls),
"genre" : (127, 128, ord)}
def __parse(self, filename):
"parse ID3v1.0 tags from MP3 file"
self.clear()
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
finally:
fsock.close()
if tagdata[:3] == "TAG":
for tag, (start, end, parseFunc) in self.tagDataMap.items():
self[tag] = parseFunc(tagdata[start:end])
except IOError:
pass
def __setitem__(self, key, item):
if key == "name" and item:
self.__parse(item)
FileInfo.__setitem__(self, key, item)
def listDirectory(directory, fileExtList):
"get list of file info objects for files of particular extensions"
fileList = [os.path.normcase(f)
for f in os.listdir(directory)]
fileList = [os.path.join(directory, f)
for f in fileList
if os.path.splitext(f)[1] in fileExtList]
def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):
"get file info class from filename extension"
subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]
return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
return [getFileInfoClass(f)(f) for f in fileList]
if __name__ == "__main__":
for info in listDirectory("/music/_singles/", [".mp3"]): ①
print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
print
- This program's output depends on the files on your hard drive. To get meaningful output, you'll need to change the directory
path to point to a directory of MP3 files on your own machine.
This is the output I got on my machine. Your output will be different, unless, by some startling coincidence, you share my
exact taste in music.
album=
artist=Ghost in the Machine
title=A Time Long Forgotten (Concept
genre=31
name=/music/_singles/a_time_long_forgotten_con.mp3
year=1999
comment=http://mp3.com/ghostmachine
album=Rave Mix
artist=***DJ MARY-JANE***
title=HELLRAISER****Trance from Hell
genre=31
name=/music/_singles/hellraiser.mp3
year=2000
comment=http://mp3.com/DJMARYJANE
album=Rave Mix
artist=***DJ MARY-JANE***
title=KAIRO****THE BEST GOA
genre=31
name=/music/_singles/kairo.mp3
year=2000
comment=http://mp3.com/DJMARYJANE
album=Journeys
artist=Masters of Balance
title=Long Way Home
genre=31
name=/music/_singles/long_way_home1.mp3
year=2000
comment=http://mp3.com/MastersofBalan
album=
artist=The Cynic Project
title=Sidewinder
genre=18
name=/music/_singles/sidewinder.mp3
year=2000
comment=http://mp3.com/cynicproject
album=Digitosis@128k
artist=VXpanded
title=Spinning
genre=255
name=/music/_singles/spinning.mp3
year=2000
comment=http://mp3.com/artists/95/vxp 5.2. Importing Modules Using from module import
Python has two ways of importing modules. Both are useful, and you should know when to use each. One way, import module, you've already seen in Section 2.4, “Everything Is an Object”. The other way accomplishes the same thing, but it has subtle and important differences.
Here is the basic from module import syntax:
from UserDict import UserDict
This is similar to the import module syntax that you know and love, but with an important difference: the attributes and methods of the imported module types are imported directly into the local namespace, so they are available directly, without qualification by module name. You
can import individual items or use from module import * to import everything.
 | from module import * in Python is like use module in Perl; import module in Python is like require module in Perl.
 | from module import * in Python is like import module.* in Java; import module in Python is like import module in Java.
Example 5.2. import module vs. from module import>>> import types
>>> types.FunctionType ①
<type 'function'>
>>> FunctionType ②
Traceback (innermost last):
File "<interactive input>", line 1, in ?
NameError: There is no variable named 'FunctionType'
>>> from types import FunctionType ③
>>> FunctionType ④
<type 'function'>
- The
types module contains no methods; it just has attributes for each Python object type. Note that the attribute, FunctionType, must be qualified by the module name, types.
FunctionType by itself has not been defined in this namespace; it exists only in the context of types.
- This syntax imports the attribute
FunctionType from the types module directly into the local namespace.
- Now
FunctionType can be accessed directly, without reference to types.
When should you use from module import?
- If you will be accessing attributes and methods often and don't want to type the module name over and over, use
from module import.
- If you want to selectively import some attributes and methods but not others, use
from module import.
- If the module contains attributes or functions with the same name as ones in your module, you must use
import module to avoid name conflicts.
Other than that, it's just a matter of style, and you will see Python code written both ways.
 | Use from module import * sparingly, because it makes it difficult to determine where a particular function or attribute came from, and that makes
debugging and refactoring more difficult.
Further Reading on Module Importing Techniques
[classes stuff was here]
Example 5.4. Defining the FileInfo Class
from UserDict import UserDict
class FileInfo(UserDict): ①
- In Python, the ancestor of a class is simply listed in parentheses immediately after the class name. So the
FileInfo class is inherited from the UserDict class (which was imported from the UserDict module). UserDict is a class that acts like a dictionary, allowing you to essentially subclass the dictionary datatype and add your own behavior.
(There are similar classes UserList and UserString which allow you to subclass lists and strings.) There is a bit of black magic behind this, which you will demystify later
in this chapter when you explore the UserDict class in more depth.
 | In Python, the ancestor of a class is simply listed in parentheses immediately after the class name. There is no special keyword like
extends in Java.
Python supports multiple inheritance. In the parentheses following the class name, you can list as many ancestor classes as you
like, separated by commas.
5.3.1. Initializing and Coding Classes
If creating new instances is easy, destroying them is even easier. In general, there is no need to explicitly free instances,
because they are freed automatically when the variables assigned to them go out of scope. Memory leaks are rare in Python.
Example 5.8. Trying to Implement a Memory Leak>>> def leakmem():
... f = fileinfo.FileInfo('/music/_singles/kairo.mp3') ①
...
>>> for i in range(100):
... leakmem() ②
- Every time the
leakmem function is called, you are creating an instance of FileInfo and assigning it to the variable f, which is a local variable within the function. Then the function ends without ever freeing f, so you would expect a memory leak, but you would be wrong. When the function ends, the local variable f goes out of scope. At this point, there are no longer any references to the newly created instance of FileInfo (since you never assigned it to anything other than f), so Python destroys the instance for us.
- No matter how many times you call the
leakmem function, it will never leak memory, because every time, Python will destroy the newly created FileInfo class before returning from leakmem.
The technical term for this form of garbage collection is “reference counting”. Python keeps a list of references to every instance created. In the above example, there was only one reference to the FileInfo instance: the local variable f. When the function ends, the variable f goes out of scope, so the reference count drops to 0, and Python destroys the instance automatically.
In previous versions of Python, there were situations where reference counting failed, and Python couldn't clean up after you. If you created two instances that referenced each other (for instance, a doubly-linked list,
where each node has a pointer to the previous and next node in the list), neither instance would ever be destroyed automatically
because Python (correctly) believed that there is always a reference to each instance. Python 2.0 has an additional form of garbage collection called “mark-and-sweep” which is smart enough to notice this virtual gridlock and clean up circular references correctly.
As a former philosophy major, it disturbs me to think that things disappear when no one is looking at them, but that's exactly
what happens in Python. In general, you can simply forget about memory management and let Python clean up after you.
Further Reading on Garbage Collection
5.5. Exploring UserDict: A Wrapper Class
As you've seen, FileInfo is a class that acts like a dictionary. To explore this further, let's look at the UserDict class in the UserDict module, which is the ancestor of the FileInfo class. This is nothing special; the class is written in Python and stored in a .py file, just like any other Python code. In particular, it's stored in the lib directory in your Python installation.
 | In the ActivePython IDE on Windows, you can quickly open any module in your library path by selecting
File->Locate... (Ctrl-L).
Example 5.9. Defining the UserDict Class
class UserDict: ①
def __init__(self, dict=None): ②
self.data = {} ③
if dict is not None: self.update(dict) ④ ⑤
- Note that
UserDict is a base class, not inherited from any other class.
- This is the
__init__ method that you overrode in the FileInfo class. Note that the argument list in this ancestor class is different than the descendant. That's okay; each subclass can have
its own set of arguments, as long as it calls the ancestor with the correct arguments. Here the ancestor class has a way
to define initial values (by passing a dictionary in the dict argument) which the FileInfo does not use.
- Python supports data attributes (called “instance variables” in Java and Powerbuilder, and “member variables” in C++). Data attributes are pieces of data held by a specific instance of a class. In this case, each instance of
UserDict will have a data attribute data. To reference this attribute from code outside the class, you qualify it with the instance name, instance.data, in the same way that you qualify a function with its module name. To reference a data attribute from within the class,
you use self as the qualifier. By convention, all data attributes are initialized to reasonable values in the __init__ method. However, this is not required, since data attributes, like local variables, spring into existence when they are first assigned a value.
- The
update method is a dictionary duplicator: it copies all the keys and values from one dictionary to another. This does not clear the target dictionary first; if the target dictionary already has some keys, the ones from the source dictionary will
be overwritten, but others will be left untouched. Think of update as a merge function, not a copy function.
- This is a syntax you may not have seen before (I haven't used it in the examples in this book). It's an
if statement, but instead of having an indented block starting on the next line, there is just a single statement on the same
line, after the colon. This is perfectly legal syntax, which is just a shortcut you can use when you have only one statement
in a block. (It's like specifying a single statement without braces in C++.) You can use this syntax, or you can have indented code on subsequent lines, but you can't do both for the same block.
 | Java and Powerbuilder support function overloading by argument list, i.e. one class can have multiple methods with the same name but a different number of arguments, or arguments of different types.
Other languages (most notably PL/SQL) even support function overloading by argument name; i.e. one class can have multiple methods with the same name and the same number of arguments of the same type but different argument
names. Python supports neither of these; it has no form of function overloading whatsoever. Methods are defined solely by their name,
and there can be only one method per class with a given name. So if a descendant class has an __init__ method, it always overrides the ancestor __init__ method, even if the descendant defines it with a different argument list. And the same rule applies to any other method.
 | Guido, the original author of Python, explains method overriding this way: "Derived classes may override methods of their base classes. Because methods have no
special privileges when calling other methods of the same object, a method of a base class that calls another method defined
in the same base class, may in fact end up calling a method of a derived class that overrides it. (For C++ programmers: all methods in Python are effectively virtual.)" If that doesn't make sense to you (it confuses the hell out of me), feel free to ignore it.
I just thought I'd pass it along.
 | Always assign an initial value to all of an instance's data attributes in the __init__ method. It will save you hours of debugging later, tracking down AttributeError exceptions because you're referencing uninitialized (and therefore non-existent) attributes.
Example 5.10. UserDict Normal Methods
def clear(self): self.data.clear() ①
def copy(self): ②
if self.__class__ is UserDict: ③
return UserDict(self.data)
import copy ④
return copy.copy(self)
def keys(self): return self.data.keys() ⑤
def items(self): return self.data.items()
def values(self): return self.data.values()
clear is a normal class method; it is publicly available to be called by anyone at any time. Notice that clear, like all class methods, has self as its first argument. (Remember that you don't include self when you call the method; it's something that Python adds for you.) Also note the basic technique of this wrapper class: store a real dictionary (data) as a data attribute, define all the methods that a real dictionary has, and have each class method redirect to the corresponding
method on the real dictionary. (In case you'd forgotten, a dictionary's clear method deletes all of its keys and their associated values.)
- The
copy method of a real dictionary returns a new dictionary that is an exact duplicate of the original (all the same key-value pairs).
But UserDict can't simply redirect to self.data.copy, because that method returns a real dictionary, and what you want is to return a new instance that is the same class as self.
- You use the
__class__ attribute to see if self is a UserDict; if so, you're golden, because you know how to copy a UserDict: just create a new UserDict and give it the real dictionary that you've squirreled away in self.data. Then you immediately return the new UserDict you don't even get to the import copy on the next line.
- If
self.__class__ is not UserDict, then self must be some subclass of UserDict (like maybe FileInfo), in which case life gets trickier. UserDict doesn't know how to make an exact copy of one of its descendants; there could, for instance, be other data attributes defined
in the subclass, so you would need to iterate through them and make sure to copy all of them. Luckily, Python comes with a module to do exactly this, and it's called copy. I won't go into the details here (though it's a wicked cool module, if you're ever inclined to dive into it on your own).
Suffice it to say that copy can copy arbitrary Python objects, and that's how you're using it here.
- The rest of the methods are straightforward, redirecting the calls to the built-in methods on self.data.
 | In versions of Python prior to 2.2, you could not directly subclass built-in datatypes like strings, lists, and dictionaries. To compensate for
this, Python comes with wrapper classes that mimic the behavior of these built-in datatypes: UserString, UserList, and UserDict. Using a combination of normal and special methods, the UserDict class does an excellent imitation of a dictionary. In Python 2.2 and later, you can inherit classes directly from built-in datatypes like dict. An example of this is given in the examples that come with this book, in fileinfo_fromdict.py.
In Python, you can inherit directly from the dict built-in datatype, as shown in this example. There are three differences here compared to the UserDict version.
Example 5.11. Inheriting Directly from Built-In Datatype dict
class FileInfo(dict):①
"store file metadata"
def __init__(self, filename=None): ②
self["name"] = filename
- The first difference is that you don't need to import the
UserDict module, since dict is a built-in datatype and is always available. The second is that you are inheriting from dict directly, instead of from UserDict.UserDict.
- The third difference is subtle but important. Because of the way
UserDict works internally, it requires you to manually call its __init__ method to properly initialize its internal data structures. dict does not work like this; it is not a wrapper, and it requires no explicit initialization.
Further Reading on UserDict
5.6. Special Class Methods
In addition to normal class methods, there are a number of special methods that Python classes can define. Instead of being called directly by your code (like normal methods), special methods are called for
you by Python in particular circumstances or when specific syntax is used.
As you saw in the previous section, normal methods go a long way towards wrapping a dictionary in a class. But normal methods alone are not enough, because
there are a lot of things you can do with dictionaries besides call methods on them. For starters, you can get and set items with a syntax that doesn't include explicitly invoking methods. This is where special class methods come in: they
provide a way to map non-method-calling syntax into method calls.
5.6.1. Getting and Setting Items
Example 5.12. The __getitem__ Special Method
def __getitem__(self, key): return self.data[key]
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("name") ①
'/music/_singles/kairo.mp3'
>>> f["name"] ②
'/music/_singles/kairo.mp3'
- The
__getitem__ special method looks simple enough. Like the normal methods clear, keys, and values, it just redirects to the dictionary to return its value. But how does it get called? Well, you can call __getitem__ directly, but in practice you wouldn't actually do that; I'm just doing it here to show you how it works. The right way
to use __getitem__ is to get Python to call it for you.
- This looks just like the syntax you would use to get a dictionary value, and in fact it returns the value you would expect. But here's the missing link: under the covers, Python has converted this syntax to the method call
f.__getitem__("name"). That's why __getitem__ is a special class method; not only can you call it yourself, you can get Python to call it for you by using the right syntax.
Of course, Python has a __setitem__ special method to go along with __getitem__, as shown in the next example.
Example 5.13. The __setitem__ Special Method
def __setitem__(self, key, item): self.data[key] = item
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__setitem__("genre", 31) ①
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':31}
>>> f["genre"] = 32 ②
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':32}
- Like the
__getitem__ method, __setitem__ simply redirects to the real dictionary self.data to do its work. And like __getitem__, you wouldn't ordinarily call it directly like this; Python calls __setitem__ for you when you use the right syntax.
- This looks like regular dictionary syntax, except of course that f is really a class that's trying very hard to masquerade as a dictionary, and
__setitem__ is an essential part of that masquerade. This line of code actually calls f.__setitem__("genre", 32) under the covers.
__setitem__ is a special class method because it gets called for you, but it's still a class method. Just as easily as the __setitem__ method was defined in UserDict, you can redefine it in the descendant class to override the ancestor method. This allows you to define classes that act
like dictionaries in some ways but define their own behavior above and beyond the built-in dictionary.
This concept is the basis of the entire framework you're studying in this chapter. Each file type can have a handler class
that knows how to get metadata from a particular type of file. Once some attributes (like the file's name and location) are
known, the handler class knows how to derive other attributes automatically. This is done by overriding the __setitem__ method, checking for particular keys, and adding additional processing when they are found.
For example, MP3FileInfo is a descendant of FileInfo. When an MP3FileInfo's name is set, it doesn't just set the name key (like the ancestor FileInfo does); it also looks in the file itself for MP3 tags and populates a whole set of keys. The next example shows how this works.
Example 5.14. Overriding __setitem__ in MP3FileInfo
def __setitem__(self, key, item): ①
if key == "name" and item: ②
self.__parse(item) ③
FileInfo.__setitem__(self, key, item) ④
- Notice that this
__setitem__ method is defined exactly the same way as the ancestor method. This is important, since Python will be calling the method for you, and it expects it to be defined with a certain number of arguments. (Technically speaking,
the names of the arguments don't matter; only the number of arguments is important.)
- Here's the crux of the entire
MP3FileInfo class: if you're assigning a value to the name key, you want to do something extra.
- The extra processing you do for
names is encapsulated in the __parse method. This is another class method defined in MP3FileInfo, and when you call it, you qualify it with self. Just calling __parse would look for a normal function defined outside the class, which is not what you want. Calling self.__parse will look for a class method defined within the class. This isn't anything new; you reference data attributes the same way.
- After doing this extra processing, you want to call the ancestor method. Remember that this is never done for you in Python; you must do it manually. Note that you're calling the immediate ancestor,
FileInfo, even though it doesn't have a __setitem__ method. That's okay, because Python will walk up the ancestor tree until it finds a class with the method you're calling, so this line of code will eventually
find and call the __setitem__ defined in UserDict.
 | When accessing data attributes within a class, you need to qualify the attribute name: self.attribute. When calling other methods within a class, you need to qualify the method name: self.method.
Example 5.15. Setting an MP3FileInfo's name>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo() ①
>>> mp3file
{'name':None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3" ②
>>> mp3file
{'album': 'Rave Mix', 'artist': '***DJ MARY-JANE***', 'genre': 31,
'title': 'KAIRO****THE BEST GOA', 'name': '/music/_singles/kairo.mp3',
'year': '2000', 'comment': 'http://mp3.com/DJMARYJANE'}
>>> mp3file["name"] = "/music/_singles/sidewinder.mp3" ③
>>> mp3file
{'album': '', 'artist': 'The Cynic Project', 'genre': 18, 'title': 'Sidewinder',
'name': '/music/_singles/sidewinder.mp3', 'year': '2000',
'comment': 'http://mp3.com/cynicproject'}
- First, you create an instance of
MP3FileInfo, without passing it a filename. (You can get away with this because the filename argument of the __init__ method is optional.) Since MP3FileInfo has no __init__ method of its own, Python walks up the ancestor tree and finds the __init__ method of FileInfo. This __init__ method manually calls the __init__ method of UserDict and then sets the name key to filename, which is None, since you didn't pass a filename. Thus, mp3file initially looks like a dictionary with one key, name, whose value is None.
- Now the real fun begins. Setting the
name key of mp3file triggers the __setitem__ method on MP3FileInfo (not UserDict), which notices that you're setting the name key with a real value and calls self.__parse. Although you haven't traced through the __parse method yet, you can see from the output that it sets several other keys: album, artist, genre, title, year, and comment.
- Modifying the
name key will go through the same process again: Python calls __setitem__, which calls self.__parse, which sets all the other keys.
5.7. Advanced Special Class Methods
Python has more special methods than just __getitem__ and __setitem__. Some of them let you emulate functionality that you may not even know about.
This example shows some of the other special methods in UserDict.
Example 5.16. More Special Methods in UserDict
def __repr__(self): return repr(self.data) ①
def __cmp__(self, dict): ②
if isinstance(dict, UserDict):
return cmp(self.data, dict.data)
else:
return cmp(self.data, dict)
def __len__(self): return len(self.data) ③
def __delitem__(self, key): del self.data[key] ④
__repr__ is a special method that is called when you call repr(instance). The repr function is a built-in function that returns a string representation of an object. It works on any object, not just class
instances. You're already intimately familiar with repr and you don't even know it. In the interactive window, when you type just a variable name and press the ENTER key, Python uses repr to display the variable's value. Go create a dictionary d with some data and then print repr(d) to see for yourself.
__cmp__ is called when you compare class instances. In general, you can compare any two Python objects, not just class instances, by using ==. There are rules that define when built-in datatypes are considered equal; for instance, dictionaries are equal when they
have all the same keys and values, and strings are equal when they are the same length and contain the same sequence of characters.
For class instances, you can define the __cmp__ method and code the comparison logic yourself, and then you can use == to compare instances of your class and Python will call your __cmp__ special method for you.
__len__ is called when you call len(instance). The len function is a built-in function that returns the length of an object. It works on any object that could reasonably be thought
of as having a length. The len of a string is its number of characters; the len of a dictionary is its number of keys; the len of a list or tuple is its number of elements. For class instances, define the __len__ method and code the length calculation yourself, and then call len(instance) and Python will call your __len__ special method for you.
__delitem__ is called when you call del instance[key], which you may remember as the way to delete individual items from a dictionary. When you use del on a class instance, Python calls the __delitem__ special method for you.
 | In Java, you determine whether two string variables reference the same physical memory location by using str1 == str2. This is called object identity, and it is written in Python as str1 is str2. To compare string values in Java, you would use str1.equals(str2); in Python, you would use str1 == str2. Java programmers who have been taught to believe that the world is a better place because == in Java compares by identity instead of by value may have a difficult time adjusting to Python's lack of such “gotchas”.
At this point, you may be thinking, “All this work just to do something in a class that I can do with a built-in datatype.” And it's true that life would be easier (and the entire UserDict class would be unnecessary) if you could inherit from built-in datatypes like a dictionary. But even if you could, special
methods would still be useful, because they can be used in any class, not just wrapper classes like UserDict.
Special methods mean that any class can store key/value pairs like a dictionary, just by defining the __setitem__ method. Any class can act like a sequence, just by defining the __getitem__ method. Any class that defines the __cmp__ method can be compared with ==. And if your class represents something that has a length, don't define a GetLength method; define the __len__ method and use len(instance).
 | While other object-oriented languages only let you define the physical model of an object (“this object has a GetLength method”), Python's special class methods like __len__ allow you to define the logical model of an object (“this object has a length”).
Python has a lot of other special methods. There's a whole set of them that let classes act like numbers, allowing you to add,
subtract, and do other arithmetic operations on class instances. (The canonical example of this is a class that represents
complex numbers, numbers with both real and imaginary components.) The __call__ method lets a class act like a function, allowing you to call a class instance directly. And there are other special methods
that allow classes to have read-only and write-only data attributes; you'll talk more about those in later chapters.
Further Reading on Special Class Methods
5.8. Introducing Class Attributes
You already know about data attributes, which are variables owned by a specific instance of a class. Python also supports class attributes, which are variables owned by the class itself.
Example 5.17. Introducing Class Attributes
class MP3FileInfo(FileInfo):
"store ID3v1.0 MP3 tags"
tagDataMap = {"title" : ( 3, 33, stripnulls),
"artist" : ( 33, 63, stripnulls),
"album" : ( 63, 93, stripnulls),
"year" : ( 93, 97, stripnulls),
"comment" : ( 97, 126, stripnulls),
"genre" : (127, 128, ord)}
>>> import fileinfo
>>> fileinfo.MP3FileInfo ①
<class fileinfo.MP3FileInfo at 01257FDC>
>>> fileinfo.MP3FileInfo.tagDataMap ②
{'title': (3, 33, <function stripnulls at 0260C8D4>),
'genre': (127, 128, <built-in function ord>),
'artist': (33, 63, <function stripnulls at 0260C8D4>),
'year': (93, 97, <function stripnulls at 0260C8D4>),
'comment': (97, 126, <function stripnulls at 0260C8D4>),
'album': (63, 93, <function stripnulls at 0260C8D4>)}
>>> m = fileinfo.MP3FileInfo() ③
>>> m.tagDataMap
{'title': (3, 33, <function stripnulls at 0260C8D4>),
'genre': (127, 128, <built-in function ord>),
'artist': (33, 63, <function stripnulls at 0260C8D4>),
'year': (93, 97, <function stripnulls at 0260C8D4>),
'comment': (97, 126, <function stripnulls at 0260C8D4>),
'album': (63, 93, <function stripnulls at 0260C8D4>)}
MP3FileInfo is the class itself, not any particular instance of the class.
- tagDataMap is a class attribute: literally, an attribute of the class. It is available before creating any instances of the class.
- Class attributes are available both through direct reference to the class and through any instance of the class.
 | In Java, both static variables (called class attributes in Python) and instance variables (called data attributes in Python) are defined immediately after the class definition (one with the static keyword, one without). In Python, only class attributes can be defined here; data attributes are defined in the __init__ method.
Class attributes can be used as class-level constants (which is how you use them in MP3FileInfo), but they are not really constants. You can also change them.
 | There are no constants in Python. Everything can be changed if you try hard enough. This fits with one of the core principles of Python: bad behavior should be discouraged but not banned. If you really want to change the value of None, you can do it, but don't come running to me when your code is impossible to debug.
Example 5.18. Modifying Class Attributes>>> class counter:
... count = 0 ①
... def __init__(self):
... self.__class__.count += 1 ②
...
>>> counter
<class __main__.counter at 010EAECC>
>>> counter.count ③
0
>>> c = counter()
>>> c.count ④
1
>>> counter.count
1
>>> d = counter() ⑤
>>> d.count
2
>>> c.count
2
>>> counter.count
2
- count is a class attribute of the
counter class.
__class__ is a built-in attribute of every class instance (of every class). It is a reference to the class that self is an instance of (in this case, the counter class).
- Because count is a class attribute, it is available through direct reference to the class, before you have created any instances of the
class.
- Creating an instance of the class calls the
__init__ method, which increments the class attribute count by 1. This affects the class itself, not just the newly created instance.
- Creating a second instance will increment the class attribute count again. Notice how the class attribute is shared by the class and all instances of the class.
5.9. Private Functions
Like most languages, Python has the concept of private elements:
- Private functions, which can't be called from outside their module
- Private class methods, which can't be called from outside their class
- Private attributes, which can't be accessed from outside their class.
Unlike in most languages, whether a Python function, method, or attribute is private or public is determined entirely by its name.
If the name of a Python function, class method, or attribute starts with (but doesn't end with) two underscores, it's private; everything else is
public. Python has no concept of protected class methods (accessible only in their own class and descendant classes). Class methods are either private (accessible
only in their own class) or public (accessible from anywhere).
In MP3FileInfo, there are two methods: __parse and __setitem__. As you have already discussed, __setitem__ is a special method; normally, you would call it indirectly by using the dictionary syntax on a class instance, but it is public, and you could
call it directly (even from outside the fileinfo module) if you had a really good reason. However, __parse is private, because it has two underscores at the beginning of its name.
 | In Python, all special methods (like __setitem__) and built-in attributes (like __doc__) follow a standard naming convention: they both start with and end with two underscores. Don't name your own methods and
attributes this way, because it will only confuse you (and others) later.
Example 5.19. Trying to Call a Private Method>>> import fileinfo
>>> m = fileinfo.MP3FileInfo()
>>> m.__parse("/music/_singles/kairo.mp3") ①
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'MP3FileInfo' instance has no attribute '__parse'
- If you try to call a private method, Python will raise a slightly misleading exception, saying that the method does not exist. Of course it does exist, but it's private,
so it's not accessible outside the class.Strictly speaking, private methods are accessible outside their class, just not easily accessible. Nothing in Python is truly private; internally, the names of private methods and attributes are mangled and unmangled on the fly to make them
seem inaccessible by their given names. You can access the
__parse method of the MP3FileInfo class by the name _MP3FileInfo__parse. Acknowledge that this is interesting, but promise to never, ever do it in real code. Private methods are private for a
reason, but like many other things in Python, their privateness is ultimately a matter of convention, not force.
Further Reading on Private Functions
5.10. Summary
That's it for the hard-core object trickery. You'll see a real-world application of special class methods in Chapter 12, which uses getattr to create a proxy to a remote web service.
The next chapter will continue using this code sample to explore other Python concepts, such as exceptions, file objects, and for loops.
Before diving into the next chapter, make sure you're comfortable doing all of these things:
Chapter 6. Exceptions and File Handling
In this chapter, you will dive into exceptions, file objects, for loops, and the os and sys modules. If you've used exceptions in another programming language, you can skim the first section to get a sense of Python's syntax. Be sure to tune in again for file handling.
6.1. Handling Exceptions
Like many other programming languages, Python has exception handling via try...except blocks.
 | Python uses try...except to handle exceptions and raise to generate them. Java and C++ use try...catch to handle exceptions, and throw to generate them.
Exceptions are everywhere in Python. Virtually every module in the standard Python library uses them, and Python itself will raise them in a lot of different circumstances. You've already seen them repeatedly throughout this book.
In each of these cases, you were simply playing around in the Python IDE: an error occurred, the exception was printed (depending on your IDE, perhaps in an intentionally jarring shade of red), and that was that. This is called an unhandled exception. When the exception was raised, there was no code to explicitly notice it and deal with it, so it bubbled its
way back to the default behavior built in to Python, which is to spit out some debugging information and give up. In the IDE, that's no big deal, but if that happened while your actual Python program was running, the entire program would come to a screeching halt.
An exception doesn't need result in a complete program crash, though. Exceptions, when raised, can be handled. Sometimes an exception is really because you have a bug in your code (like accessing a variable that doesn't exist), but
many times, an exception is something you can anticipate. If you're opening a file, it might not exist. If you're connecting
to a database, it might be unavailable, or you might not have the correct security credentials to access it. If you know
a line of code may raise an exception, you should handle the exception using a try...except block.
Example 6.1. Opening a Non-Existent File>>> fsock = open("/notthere", "r") ①
Traceback (innermost last):
File "<interactive input>", line 1, in ?
IOError: [Errno 2] No such file or directory: '/notthere'
>>> try:
... fsock = open("/notthere") ②
... except IOError: ③
... print "The file does not exist, exiting gracefully"
... print "This line will always print" ④
The file does not exist, exiting gracefully
This line will always print
- Using the built-in
open function, you can try to open a file for reading (more on open in the next section). But the file doesn't exist, so this raises the IOError exception. Since you haven't provided any explicit check for an IOError exception, Python just prints out some debugging information about what happened and then gives up.
- You're trying to open the same non-existent file, but this time you're doing it within a
try...except block.
- When the
open method raises an IOError exception, you're ready for it. The except IOError: line catches the exception and executes your own block of code, which in this case just prints a more pleasant error message.
- Once an exception has been handled, processing continues normally on the first line after the
try...except block. Note that this line will always print, whether or not an exception occurs. If you really did have a file called
notthere in your root directory, the call to open would succeed, the except clause would be ignored, and this line would still be executed.
Exceptions may seem unfriendly (after all, if you don't catch the exception, your entire program will crash), but consider
the alternative. Would you rather get back an unusable file object to a non-existent file? You'd need to check its validity
somehow anyway, and if you forgot, somewhere down the line, your program would give you strange errors somewhere down the
line that you would need to trace back to the source. I'm sure you've experienced this, and you know it's not fun. With
exceptions, errors occur immediately, and you can handle them in a standard way at the source of the problem.
6.1.1. Using Exceptions For Other Purposes
There are a lot of other uses for exceptions besides handling actual error conditions. A common use in the standard Python library is to try to import a module, and then check whether it worked. Importing a module that does not exist will raise
an ImportError exception. You can use this to define multiple levels of functionality based on which modules are available at run-time,
or to support multiple platforms (where platform-specific code is separated into different modules).
You can also define your own exceptions by creating a class that inherits from the built-in Exception class, and then raise your exceptions with the raise command. See the further reading section if you're interested in doing this.
The next example demonstrates how to use an exception to support platform-specific functionality. This code comes from the
getpass module, a wrapper module for getting a password from the user. Getting a password is accomplished differently on UNIX, Windows, and Mac OS platforms, but this code encapsulates all of those differences.
# Bind the name getpass to the appropriate function
try:
import termios, TERMIOS ①
except ImportError:
try:
import msvcrt ②
except ImportError:
try:
from EasyDialogs import AskPassword ③
except ImportError:
getpass = default_getpass ④
else: ⑤
getpass = AskPassword
else:
getpass = win_getpass
else:
getpass = unix_getpass
termios is a UNIX-specific module that provides low-level control over the input terminal. If this module is not available (because it's not
on your system, or your system doesn't support it), the import fails and Python raises an ImportError, which you catch.
- OK, you didn't have
termios, so let's try msvcrt, which is a Windows-specific module that provides an API to many useful functions in the Microsoft Visual C++ runtime services. If this import fails, Python will raise an ImportError, which you catch.
- If the first two didn't work, you try to import a function from
EasyDialogs, which is a Mac OS-specific module that provides functions to pop up dialog boxes of various types. Once again, if this import fails, Python will raise an ImportError, which you catch.
- None of these platform-specific modules is available (which is possible, since Python has been ported to a lot of different platforms), so you need to fall back on a default password input function (which is
defined elsewhere in the
getpass module). Notice what you're doing here: assigning the function default_getpass to the variable getpass. If you read the official getpass documentation, it tells you that the getpass module defines a getpass function. It does this by binding getpass to the correct function for your platform. Then when you call the getpass function, you're really calling a platform-specific function that this code has set up for you. You don't need to know or
care which platform your code is running on -- just call getpass, and it will always do the right thing.
- A
try...except block can have an else clause, like an if statement. If no exception is raised during the try block, the else clause is executed afterwards. In this case, that means that the from EasyDialogs import AskPassword import worked, so you should bind getpass to the AskPassword function. Each of the other try...except blocks has similar else clauses to bind getpass to the appropriate function when you find an import that works.
Further Reading on Exception Handling
[for loop stuff was here]
Example 6.12. Introducing sys.modules>>> import sys ①
>>> print '\n'.join(sys.modules.keys()) ②
win32api
os.path
os
exceptions
__main__
ntpath
nt
sys
__builtin__
site
signal
UserDict
stat
- The
sys module contains system-level information, such as the version of Python you're running (sys.version or sys.version_info), and system-level options such as the maximum allowed recursion depth (sys.getrecursionlimit() and sys.setrecursionlimit()).
sys.modules is a dictionary containing all the modules that have ever been imported since Python was started; the key is the module name, the value is the module object. Note that this is more than just the modules your program has imported. Python preloads some modules on startup, and if you're using a Python IDE, sys.modules contains all the modules imported by all the programs you've run within the IDE.
This example demonstrates how to use sys.modules.
Example 6.13. Using sys.modules>>> import fileinfo ①
>>> print '\n'.join(sys.modules.keys())
win32api
os.path
os
fileinfo
exceptions
__main__
ntpath
nt
sys
__builtin__
site
signal
UserDict
stat
>>> fileinfo
<module 'fileinfo' from 'fileinfo.pyc'>
>>> sys.modules["fileinfo"] ②
<module 'fileinfo' from 'fileinfo.pyc'>
- As new modules are imported, they are added to
sys.modules. This explains why importing the same module twice is very fast: Python has already loaded and cached the module in sys.modules, so importing the second time is simply a dictionary lookup.
- Given the name (as a string) of any previously-imported module, you can get a reference to the module itself through the
sys.modules dictionary.
The next example shows how to use the __module__ class attribute with the sys.modules dictionary to get a reference to the module in which a class is defined.
Example 6.14. The __module__ Class Attribute>>> from fileinfo import MP3FileInfo
>>> MP3FileInfo.__module__ ①
'fileinfo'
>>> sys.modules[MP3FileInfo.__module__] ②
<module 'fileinfo' from 'fileinfo.pyc'>
- Every Python class has a built-in class attribute
__module__, which is the name of the module in which the class is defined.
- Combining this with the
sys.modules dictionary, you can get a reference to the module in which a class is defined.
Now you're ready to see how sys.modules is used in fileinfo.py, the sample program introduced in Chapter 5. This example shows that portion of the code.
Example 6.15. sys.modules in fileinfo.py
def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]): ①
"get file info class from filename extension"
subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:] ②
return hasattr(module, subclass) and getattr(module, subclass) or FileInfo ③
- This is a function with two arguments; filename is required, but module is optional and defaults to the module that contains the
FileInfo class. This looks inefficient, because you might expect Python to evaluate the sys.modules expression every time the function is called. In fact, Python evaluates default expressions only once, the first time the module is imported. As you'll see later, you never call this
function with a module argument, so module serves as a function-level constant.
- You'll plow through this line later, after you dive into the
os module. For now, take it on faith that subclass ends up as the name of a class, like MP3FileInfo.
- You already know about
getattr, which gets a reference to an object by name. hasattr is a complementary function that checks whether an object has a particular attribute; in this case, whether a module has
a particular class (although it works for any object and any attribute, just like getattr). In English, this line of code says, “If this module has the class named by subclass then return it, otherwise return the base class FileInfo.”
Further Reading on Modules
6.5. Working with Directories
The os.path module has several functions for manipulating files and directories. Here, we're looking at handling pathnames and listing
the contents of a directory.
Example 6.16. Constructing Pathnames
>>> import os
>>> os.path.join("c:\\music\\ap\\", "mahadeva.mp3") ① ②
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.join("c:\\music\\ap", "mahadeva.mp3") ③
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.expanduser("~") ④
'c:\\Documents and Settings\\mpilgrim\\My Documents'
>>> os.path.join(os.path.expanduser("~"), "Python") ⑤
'c:\\Documents and Settings\\mpilgrim\\My Documents\\Python'
os.path is a reference to a module -- which module depends on your platform. Just as getpass encapsulates differences between platforms by setting getpass to a platform-specific function, os encapsulates differences between platforms by setting path to a platform-specific module.
- The
join function of os.path constructs a pathname out of one or more partial pathnames. In this case, it simply concatenates strings. (Note that dealing
with pathnames on Windows is annoying because the backslash character must be escaped.)
- In this slightly less trivial case,
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.
expanduser 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, like Windows,
UNIX, and Mac OS X; it has no effect on Mac OS.
- Combining these techniques, you can easily construct pathnames for directories and files under the user's home directory.
Example 6.17. Splitting Pathnames>>> os.path.split("c:\\music\\ap\\mahadeva.mp3") ①
('c:\\music\\ap', 'mahadeva.mp3')
>>> (filepath, filename) = os.path.split("c:\\music\\ap\\mahadeva.mp3") ②
>>> filepath ③
'c:\\music\\ap'
>>> filename ④
'mahadeva.mp3'
>>> (shortname, extension) = os.path.splitext(filename) ⑤
>>> shortname
'mahadeva'
>>> extension
'.mp3'
- The
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? Well, split is such a function.
- You 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.
- The first variable, filepath, receives the value of the first element of the tuple returned from
split, the file path.
- The second variable, filename, receives the value of the second element of the tuple returned from
split, the filename.
os.path also contains a function splitext, 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.
Example 6.18. Listing Directories>>> os.listdir("c:\\music\\_singles\\") ①
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3',
'spinning.mp3']
>>> dirname = "c:\\"
>>> os.listdir(dirname) ②
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'cygwin',
'docbook', 'Documents and Settings', 'Incoming', 'Inetpub', 'IO.SYS',
'MSDOS.SYS', 'Music', 'NTDETECT.COM', 'ntldr', 'pagefile.sys',
'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
>>> [f for f in os.listdir(dirname)
... if os.path.isfile(os.path.join(dirname, f))] ③
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'IO.SYS', 'MSDOS.SYS',
'NTDETECT.COM', 'ntldr', 'pagefile.sys']
>>> [f for f in os.listdir(dirname)
... if os.path.isdir(os.path.join(dirname, f))] ④
['cygwin', 'docbook', 'Documents and Settings', 'Incoming',
'Inetpub', 'Music', 'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
- The
listdir function takes a pathname and returns a list of the contents of the directory.
listdir returns both files and folders, with no indication of which is which.
- You can use list filtering and the
isfile function of the os.path module to separate the files from the folders. isfile takes a pathname and returns 1 if the path represents a file, and 0 otherwise. Here you're using os.path.join to ensure a full pathname, but isfile also works with a partial path, relative to the current working directory. You can use os.getcwd() to get the current working directory.
os.path also has a isdir function which returns 1 if the path represents a directory, and 0 otherwise. You can use this to get a list of the subdirectories
within a directory.
Example 6.19. Listing Directories in fileinfo.py
def listDirectory(directory, fileExtList):
"get list of file info objects for files of particular extensions"
fileList = [os.path.normcase(f)
for f in os.listdir(directory)] ① ②
fileList = [os.path.join(directory, f)
for f in fileList
if os.path.splitext(f)[1] in fileExtList] ③ ④ ⑤
os.listdir(directory) returns a list of all the files and folders in directory.
- Iterating through the list with f, you use
os.path.normcase(f) to normalize the case according to operating system defaults. normcase is a useful little function that compensates for case-insensitive operating systems that think that mahadeva.mp3 and mahadeva.MP3 are the same file. For instance, on Windows and Mac OS, normcase will convert the entire filename to lowercase; on UNIX-compatible systems, it will return the filename unchanged.
- Iterating through the normalized list with f again, you use
os.path.splitext(f) to split each filename into name and extension.
- For each file, you see if the extension is in the list of file extensions you care about (fileExtList, which was passed to the
listDirectory function).
- For each file you care about, you use
os.path.join(directory, f) to construct the full pathname of the file, and return a list of the full pathnames.
 | 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 platform supported by Python.
There is one other way to get the contents of a directory. It's very powerful, and it uses the sort of wildcards that you
may already be familiar with from working on the command line.
Example 6.20. Listing Directories with glob
>>> os.listdir("c:\\music\\_singles\\") ①
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3',
'spinning.mp3']
>>> import glob
>>> glob.glob('c:\\music\\_singles\\*.mp3') ②
['c:\\music\\_singles\\a_time_long_forgotten_con.mp3',
'c:\\music\\_singles\\hellraiser.mp3',
'c:\\music\\_singles\\kairo.mp3',
'c:\\music\\_singles\\long_way_home1.mp3',
'c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\_singles\\s*.mp3') ③
['c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\*\\*.mp3')④
- As you saw earlier,
os.listdir simply takes a directory path and lists all files and directories in that directory.
- The
glob module, on the other hand, takes a wildcard and returns the full path of all files and directories matching the wildcard.
Here the wildcard is a directory path plus "*.mp3", which will match all .mp3 files. Note that each element of the returned list already includes the full path of the file.
- If you want to find all the files in a specific directory that start with "s" and end with ".mp3", you can do that too.
- Now consider this scenario: you have a
music directory, with several subdirectories within it, with .mp3 files within each subdirectory. You can get a list of all of those with a single call to glob, by using two wildcards at once. One wildcard is the "*.mp3" (to match .mp3 files), and one wildcard is within the directory path itself, to match any subdirectory within c:\music. That's a crazy amount of power packed into one deceptively simple-looking function!
Further Reading on the os Module
[HTML stuff was here]
8.5. locals and globals
Let's digress from HTML processing for a minute and talk about how Python handles variables. Python has two built-in functions, locals and globals, which provide dictionary-based access to local and global variables.
Remember locals? You first saw it here:
def unknown_starttag(self, tag, attrs):
strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
self.pieces.append("<%(tag)s%(strattrs)s>" % locals())
No, wait, you can't learn about locals yet. First, you need to learn about namespaces. This is dry stuff, but it's important, so pay attention.
Python uses what are called namespaces to keep track of variables. A namespace is just like a dictionary where the keys are names
of variables and the dictionary values are the values of those variables. In fact, you can access a namespace as a Python dictionary, as you'll see in a minute.
At any particular point in a Python program, there are several namespaces available. Each function has its own namespace, called the local namespace, which
keeps track of the function's variables, including function arguments and locally defined variables. Each module has its
own namespace, called the global namespace, which keeps track of the module's variables, including functions, classes, any
other imported modules, and module-level variables and constants. And there is the built-in namespace, accessible from any
module, which holds built-in functions and exceptions.
When a line of code asks for the value of a variable x, Python will search for that variable in all the available namespaces, in order:
- local namespace - specific to the current function or class method. If the function defines a local variable x, or has an argument x, Python will use this and stop searching.
- global namespace - specific to the current module. If the module has defined a variable, function, or class called x, Python will use that and stop searching.
- built-in namespace - global to all modules. As a last resort, Python will assume that x is the name of built-in function or variable.
If Python doesn't find x in any of these namespaces, it gives up and raises a NameError with the message There is no variable named 'x', which you saw back in Example 3.18, “Referencing an Unbound Variable”, but you didn't appreciate how much work Python was doing before giving you that error.
 | Python 2.2 introduced a subtle but important change that affects the namespace search order: nested scopes. In versions of Python prior to 2.2, when you reference a variable within a nested function or lambda function, Python will search for that variable in the current (nested or lambda) function's namespace, then in the module's namespace. Python 2.2 will search for the variable in the current (nested or lambda) function's namespace, then in the parent function's namespace, then in the module's namespace. Python 2.1 can work either way; by default, it works like Python 2.0, but you can add the following line of code at the top of your module to make your module work like Python 2.2:
from __future__ import nested_scopes
Are you confused yet? Don't despair! This is really cool, I promise. Like many things in Python, namespaces are directly accessible at run-time. How? Well, the local namespace is accessible via the built-in locals function, and the global (module level) namespace is accessible via the built-in globals function.
Example 8.10. Introducing locals>>> def foo(arg): ①
... x = 1
... print locals()
...
>>> foo(7) ②
{'arg': 7, 'x': 1}
>>> foo('bar') ③
{'arg': 'bar', 'x': 1}
- The function
| | | | | | | | | | | | | | | | | | | | | | |