Intermediate Python Course Overview
Intermediate Python Course Overview
John Strickler
Welcome!
• We’re glad you’re here
Instructor name:
Instructor e-mail:
Have Fun!
• No phone conversations
We’re all adults here; feel free to leave the classroom if you need to use the restroom, make a phone
call, etc. You don’t have to wait for a lab or break, but please try not to disturb others.
Please do not bring killer rabbits to class. They might maim, dismember, or
IMPORTANT
otherwise disturb your fellow students.
• The instructor doesn’t know you need help unless you tell them. It’s ok to ask for help often.
Course Outline
Half-Day 1
Half-Day 2
Half-Day 3
Numpy
Matplotlib
Half-Day 4
Pandas
Half-Day 5
Half-Day 6
Chapter 8 Multiprogramming
Chapter 9 Network Programming
Half-Day 7
The actual schedule varies with circumstances. The last day may include ad hoc topics
NOTE
requested by students
Student files
You will need to load some files onto your computer. The files are in a compressed archive. When you
extract them onto your computer, they will all be extracted into a directory named py3cirrusint.
py3cirrusint contains data and other files needed for the exercises
py3cirrusint/EXAMPLES contains the examples from the course manuals.
py3cirrusint/ANSWERS contains sample answers to the labs.
The student files do not contain Python itself. It will need to be installed
WARNING
separately. This has probably already been done for you.
Open the file [Link]. Extract all files to your desktop. This will create the folder
py3cirrusint.
Copy or download [Link] to your home directory. In your home directory, type
This will create the py3cirrusint directory under your home directory.
Examples
Nearly all examples from the course manual are provided in the EXAMPLES subdirectory.
Example
cmd_line_args.py
#!/usr/bin/env python
import sys ①
print([Link]) ②
name = [Link][1] ③
print("name is", name)
cmd_line_args.py Fred
['/Users/jstrick/curr/courses/python/examples3/cmd_line_args.py', 'Fred']
name is Fred
Lab Exercises
• Relax – the labs are not quizzes
Appendices
• Appendix A: Python Bibliography
Tim Peters is a longtime contributor to Python. He wrote the standard sorting routine, known as
timsort.
The above text is printed out when you execute the code import this. Generally speaking, if code
follows the guidelines in the Zen of Python, then it’s Pythonic.
Tuples
• Fixed-size, read-only
A tuple is a collection of related data. While on the surface it seems like just a read-only list, it is used
when you need to pass multiple values to or from a function, but the values are not all the same type
To create a tuple, use a comma-separated list of objects. Parentheses are not needed around a tuple
unless the tuple is nested in a larger data structure.
While both tuples and lists can be used for any data:
• Use a tuple when you have a collection of related objects, which may or may not be similar.
Example
birthday = ( 'April',5,1978 )
Iterable unpacking
When you have an iterable such as a tuple or list, you access individual elements by index. However,
spam[0] and spam[1] are not so readable compared to first_name and company. To copy an iterable to a
list of variable names, just assign the iterable to a comma-separated list of names:
birthday = ( 'April',5,1978 )
month, day, year = birthday
You may be thinking "why not just assign to the variables in the first place?". For a single tuple or list,
this would be true. The power of unpacking comes in the following areas:
Example
unpacking_people.py
#!/usr/bin/env python
#
people = [ ①
('Melinda', 'Gates', 'Gates Foundation'),
('Steve', 'Jobs', 'Apple'),
('Larry', 'Wall', 'Perl'),
('Paul', 'Allen', 'Microsoft'),
('Larry', 'Ellison', 'Oracle'),
('Bill', 'Gates', 'Microsoft'),
('Mark', 'Zuckerberg', 'Facebook'),
('Sergey', 'Brin', 'Google'),
('Larry', 'Page', 'Google'),
('Linus', 'Torvalds', 'Linux'),
]
② The for loop unpacks each tuple into the three variables.
unpacking_people.py
Melinda Gates
Steve Jobs
Larry Wall
Paul Allen
Larry Ellison
Bill Gates
Mark Zuckerberg
Sergey Brin
Larry Page
Linus Torvalds
When unpacking iterables, sometimes you want to grab parts of the iterable as a group. This is
provided by extended iterable unpacking.
One (and only one) variable in the result of unpacking can have a star prepended. This variable will be
a list of all values not assigned to other variables.
Example
extended_iterable_unpacking.py
#!/usr/bin/env python
x, y, *z = values ②
print("x: {} y: {} z: {}".format(x, y, z))
print()
x, *y, z = values ②
print("x: {} y: {} z: {}".format(x, y, z))
print()
*x, y, z = values ②
print("x: {} y: {} z: {}".format(x, y, z))
print()
people = [
('Bill', 'Gates', 'Microsoft'),
('Steve', 'Jobs', 'Apple'),
('Paul', 'Allen', 'Microsoft'),
('Larry', 'Ellison', 'Oracle'),
('Mark', 'Zuckerberg', 'Facebook'),
('Sergey', 'Brin', 'Google'),
('Larry', 'Page', 'Google'),
('Linux', 'Torvalds', 'Linux'),
]
extended_iterable_unpacking.py
['Bill', 'Gates']
['Steve', 'Jobs']
['Paul', 'Allen']
['Larry', 'Ellison']
['Mark', 'Zuckerberg']
['Sergey', 'Brin']
['Larry', 'Page']
['Linux', 'Torvalds']
• Use * or **
Sometimes you need the other end of iterable unpacking. What do you do if you have a list of three
values, and you want to pass them to a method that expects three positional arguments? One approach
is to use the individual items by index. A more Pythonic approach is to use * to unpack the iterable into
individual items:
Use a single asterisk to unpack a list or tuple (or similar iterable); use two asterisks to unpack a
dictionary or similar.
In the example, see how the list HEADINGS is passed to .format(), which expects individual
parameters, not one parameter containing multiple values.
Example
unpacking_function_args.py
#!/usr/bin/env python
#
people = [ ①
('Joe', 'Schmoe', 'Burbank', 'CA'),
('Mary', 'Rattburger', 'Madison', 'WI'),
('Jose', 'Ramirez', 'Ames', 'IA'),
]
unpacking_function_args.py
Example
shoe_sizes.py
#!/usr/bin/env python
#
BARLEYCORN = 1 / 3.0
CM_TO_INCH = 2.54
MENS_START_SIZE = 12
WOMENS_START_SIZE = 10.5
SIZE_RANGE = []
for i in range(6, 14):
SIZE_RANGE.extend([i, i + .5])
def main():
for heading, flag in [("MEN'S", True), ("WOMEN'S", False)]:
print(heading)
print(([Link](*HEADINGS))) ①
for size in SIZE_RANGE:
inches, cm = get_length(size, flag)
print([Link](size, inches, cm))
print()
if __name__ == '__main__':
main()
① format expects individual arguments for each placeholder; the asterisk unpacks HEADINGS into
individual strings
shoe_sizes.py
MEN'S
Size Inches CM
6.0 10.00 25.40
6.5 10.17 25.82
7.0 10.33 26.25
7.5 10.50 26.67
8.0 10.67 27.09
8.5 10.83 27.52
key=
reverse=
The sorted() builtin function returns a sorted copy of its argument, which can be any iterable.
Example
basic_sorting.py
#!/usr/bin/env python
sorted_fruit = sorted(fruits) ①
print(sorted_fruit)
basic_sorting.py
You can specify a function with the key parameter of the sorted() function. This function will be used
once for each element of the list being sorted, to provide the comparison value. Thus, you can sort a list
of strings case-insensitively, or sort a list of zip codes by the number of Starbucks within the zip code.
The function must take exactly one parameter (which is one element of the sequence being sorted) and
return either a single value or a tuple of values. The returned values will be compared in order.
You can use any builtin Python function or method that meets these requirements, or you can write
your own function.
The lower() method can be called directly from the builtin object str. It takes one string
TIP
argument and returns a lower case copy.
Example
custom_sort_keys.py
#!/usr/bin/env python
def ignore_case(item): ①
return [Link]() ②
def by_length_then_name(item):
return (len(item), [Link]()) ④
n1 = sorted(nums) ⑤
print("Numbers sorted numerically:")
for n in n1:
print(n, end=' ')
print("\n")
n2 = sorted(nums, key=str) ⑥
print("Numbers sorted as strings:")
for n in n2:
print(n, end=' ')
print()
custom_sort_keys.py
Ignoring case:
Apple apricot banana BLUEberry cherry date elderberry FIG grape guava Kiwi lemon lime
lychee ORANGE papaya peach pear persimmon pomegranate Tamarind Watermelon
Example
sort_holmes.py
#!/usr/bin/env python
"""Sort titles, ignoring leading articles"""
books = [
"A Study in Scarlet",
"The Sign of the Four",
"The Hound of the Baskervilles",
"The Valley of Fear",
"The Adventures of Sherlock Holmes",
"The Memoirs of Sherlock Holmes",
"The Return of Sherlock Holmes",
"His Last Bow",
"The Case-Book of Sherlock Holmes",
]
def strip_articles(title): ①
title = [Link]()
for article in 'a ', 'an ', 'the ':
if [Link](article):
title = title[len(article):] ②
break
return title
① create function which takes element to compare and returns comparison key
sort_holmes.py
Lambda functions
A lambda function is a brief function definition that makes it easy to create a function on the fly. This
can be useful for passing functions into other functions, to be called later. Functions passed in this way
are referred to as "callbacks". Normal functions can be callbacks as well. The advantage of a lambda
function is solely the programmer’s convenience. There is no speed or other advantage.
One important use of lambda functions is for providing sort keys; another is to provide event handlers
in GUI programming.
where parameter-list is a list of function parameters, and expression is an expression involving the
parameters. The expression is the return value of the function.
def function-name(param-list):
return expr
But it is not possible to use the normal syntax as a function parameter, or as an element in a list.
Example
lambda_examples.py
#!/usr/bin/env python
print(" ".join(sfruits))
① The lambda function takes one fruit and returns it in lower case
lambda_examples.py
List comprehensions
A list comprehension is a Python idiom that creates a shortcut for a for loop. It returns a copy of a list
with every element transformed via an expression. Functional programmers refer to this as a mapping
function.
results = []
for var in sequence:
[Link](expr) # where expr involves var
can be rewritten as
Example
[Link]
#!/usr/bin/env python
[Link]
Dictionary comprehensions
A dictionary comprehension has syntax similar to a list comprehension. The expression is a key:value
pair, and is added to the resulting dictionary. If a key is used more than once, it overrides any previous
keys. This can be handy for building a dictionary from a sequence of values.
Example
dict_comprehension.py
#!/usr/bin/env python
print(d, '\n')
② Use a nested dictionary comprehension to create a dictionary mapping words to dictionaries which
map letters to their counts (could be useful for anagrams)
dict_comprehension.py
Set comprehensions
A set comprehension is useful for turning any sequence into a set. Items can be modified or skipped as
the set is built.
If you don’t need to modify the items, it’s probably easier to just past the sequence to the set()
constructor.
Example
set_comprehension.py
#!/usr/bin/env python
import re
① Get unique words from file. Only one line is in memory at a time. Skip "empty" words.
set_comprehension.py
{'lamb', 'and', 'that', 'everywhere', 'go', 'the', 'had', 'its', 'white', 'as', 'went',
'a', 'snow', 'sure', 'mary', 'little', 'was', 'fleece', 'to'}
Iterables
Python has many builtin iterables – a file object, for instance, which allows iterating through the lines
in a file.
All builtin collections (list, tuple, str, bytes) are iterables. They keep all their values in memory. Many
other builtin iterables are generators.
A generator does not keep all its values in memory – it creates them one at a time as needed, and feeds
them to the for-in loop. This is a Good Thing, because it saves memory.
Generator Expressions
• More efficient
A generator expression is similar to a list comprehension, but it provides a generator instead of a list.
That is, while a list comprehension returns a complete list, the generator expression returns one item
at a time.
The main difference in syntax is that the generator expression uses parentheses rather than brackets.
Generator expressions are especially useful with functions like sum(), min(), and max() that reduce an
iterable input to a single value:
Example
gen_ex.py
#!/usr/bin/env python
③ only one line in memory at a time. max() iterates over generated values
gen_ex.py
285 285
30
Generator functions
• Maintains state
A generator is like a normal function, but instead of a return statement, it has a yield statement. Each
time the yield statement is reached, it provides the next value in the sequence. When there are no
more values, the function calls return, and the loop stops. A generator function maintains state
between calls, unlike a normal function.
Example
sieve_generator.py
#!/usr/bin/env python
def next_prime(limit):
flags = set() ①
np = next_prime(200) ④
for prime in np: ⑤
print(prime, end=' ')
sieve_generator.py
Example
line_trimmer.py
#!/usr/bin/env python
def trimmed(file_name):
with open(file_name) as file_in:
for line in file_in:
yield [Link]('\n\r') ①
line_trimmer.py
String formatting
• Numbered placeholders
The traditional (i.e., old) way to format strings in Python was with the % operator and a format string
containing fields designated with percent signs. The new, improved method of string formatting uses
the format() method. It takes a format string and one or more arguments. The format strings contains
placeholders which consist of curly braces, which may contain formatting details. This new method
has much more flexibility.
By default, the placeholders are numbered from left to right, starting at 0. This corresponds to the
order of arguments to format().
Placeholders can be manually numbered. This is handy when you want to use a format() parameter
more than once.
Example
stringformat_ex.py
#!/usr/bin/env python
color = 'blue'
animal = 'iguana'
fahr = 98.6839832
print('{:.1f}'.format(fahr)) ②
value = 12345
print('{0:d} {0:04x} {0:08o} {0:016b}'.format(value)) ③
② Formatting directives start with ':'; .1f means format floating point with one decimal place
stringformat_ex.py
blue iguana
98.7
12345 3039 00030071 0011000000111001
A 38
B 127
C 9
f-strings
A great new feature, f-strings, was added to Python 3.6. These are strings that contain placeholders, as
used with normal string formatting, but the expression to be formatted is also placed in the
placeholder. This makes formatting strings more readable, with less typing. As with formatted strings,
any expression can be formatted.
Other than putting the value to be formatted directly in the placeholder, the formatting directives are
the same as normal Python 3 string formatting.
x = 24
y = 32.2345
name = 'Bill Gates'
company = 'Bill Gates'
print("{} founded {}.format(name, company)"
print("{:10s} {:.2f}".format(x, y)
x = 24
y = 32.2345
name = 'Bill Gates'
company = 'Bill Gates'
print(f"{name} founded {company})"
print(f"{x:10s} {y:.2f})"
Example
f_strings.py
#!/usr/bin/env python
import sys
name = "Tim"
count = 5
avg = 3.456
info = 2093
result = 38293892
print(f"Name is [{name:<10s}]") ①
print(f"Name is [{name:>10s}]") ②
print(f"count is {count:03d} avg is {avg:.2f}") ③
print(f"${result:,d}") ⑤
city = 'Orlando'
temp = 85
else:
print("Sorry -- f-strings are only supported by Python 3.6+")
① < means left justify (default for non-numbers), 10 is field width, s formats a string
f_strings.py
Name is [Tim ]
Name is [ Tim]
count is 005 avg is 3.46
info is 2093 2093 4055 82d
$38,293,892
It is 85 in Orlando
Chapter 1 Exercises
Exercise 1-1 (pres_upper.py)
Read the file [Link], creating a list of of the presidents' last names. Then, use a list
comprehension to make a copy of the list of names in upper case. Finally, loop through the list
returned by the list comprehension and print out the names one per line.
Print out all the presidents first and last names, date of birth, and their political affiliations, sorted by
date of birth.
Read the [Link] file, putting the four fields into a list of tuples.
Loop through the list, sorting by date of birth, and printing the information for each president. Use
sorted() and a lambda function.
Write a generator function to provide a sequence of the names of presidents (in "FIRSTNAME
MIDDLENAME LASTNAME" format) from the [Link] file. They should be provided in the same
order they are in the file. You should not read the entire file into memory, but one-at-a-time from the
file.
Then iterate over the the generator returned by your function and print the names.
Functions
• Accept parameters
• Return a value
Functions are a way of isolating code that is needed in more than one place, refactoring code to make it
more modular. They are defined with the def statement.
Functions can take various types of parameters, as described on the following page. Parameter types
are dynamic.
Functions can return one object of any type, using the return statement. If there is no return
statement, the function returns None.
Be sure to separate your business logic (data and calculations) from your presentation
TIP
logic (the user interface).
Example
function_basics.py
#!/usr/bin/env python
def say_hello(): ①
print("Hello, world")
print()
②
say_hello() ③
def get_hello():
return "Hello, world" ④
h = get_hello() ⑤
print(h)
print()
def sqrt(num): ⑥
return num ** .5
m = sqrt(1234) ⑦
n = sqrt(2)
function_basics.py
Hello, world
Hello, world
m is 35.128 n is 1.414
Function parameters
• Positional or named
• Required or optional
Functions can accept both positional and named parameters. Furthermore, parameters can be
required or optional. They must be specified in the order presented here.
The first set of parameters, if any, is a set of comma-separated names. These are all required. Next you
can specify a variable preceded by an asterisk — this will accept any optional parameters.
After the optional positional parameters you can specify required named parameters. These must
come after the optional parameters. If there are no optional parameters, you can use a plain asterisk as
a placeholder. Finally, you can specify a variable preceded by two asterisks to accept optional named
parameters.
Example
function_parameters.py
#!/usr/bin/env python
def fun_one(): ①
print("Hello, world")
def fun_two(n): ②
return n ** 2
x = fun_two(5)
print("fun_two(5) is {}\n".format(x))
def fun_three(count=3): ③
for _ in range(count):
print("spam", end=' ')
print()
fun_three()
fun_three(10)
print()
fun_four('apple')
fun_four('apple', "blueberry", "peach", "cherry")
fun_five(spam=1, eggs=2)
fun_five(eggs=2, spam=2)
fun_five(spam=1)
fun_five(eggs=2)
fun_five()
def fun_six(**named_args): ⑥
print("fun_six():")
for name in named_args:
print(name, "==> ", named_args[name])
① no parameters
⑤ keyword-only parameters
function_parameters.py
fun_two(5) is 25
fun_four():
n is apple
opt is ()
--------------------
fun_four():
n is apple
opt is ('blueberry', 'peach', 'cherry')
--------------------
fun_five():
spam is: 1
eggs is: 2
fun_five():
spam is: 2
eggs is: 2
fun_five():
spam is: 1
eggs is: 0
fun_five():
spam is: 0
eggs is: 2
fun_five():
spam is: 0
eggs is: 0
fun_six():
name ==> Lancelot
quest ==> Grail
color ==> red
Default parameters
Required parameters can have default values. They are assigned to parameters with the equals sign.
Parameters without defaults cannot be specified after parameters with defaults.
Example
default_parameters.py
#!/usr/bin/env python
spam("Hello", "Mom") ②
spam("Hello") ③
print()
ham(file_name='eggs') ⑤
ham(file_name='toast', file_format='csv')
default_parameters.py
Hello, Mom
Hello, world
• Slots which have had values assigned to them are marked as 'filled'. Slots which have no value
assigned to them yet are considered 'empty'.
◦ Attempt to bind the argument to the first unfilled parameter slot. If the slot is not a vararg slot,
then mark the slot as 'filled'.
◦ If the next unfilled slot is a vararg slot, and it does not have a name, then it is an error.
◦ Otherwise, if the next unfilled slot is a vararg slot then all remaining non-keyword arguments
are placed into the vararg slot.
◦ If there is a parameter with the same name as the keyword, then the argument value is
assigned to that parameter slot. However, if the parameter slot is already filled, then that is an
error.
◦ Otherwise, if there is a 'keyword dictionary' argument, the argument is added to the dictionary
using the keyword name as the dictionary key, unless there is already an entry with that key, in
which case it is an error.
• Finally:
◦ If the vararg slot is not yet filled, assign an empty tuple as its value.
◦ For each remaining empty slot: if there is a default value for that slot, then fill the slot with the
default value. If there is no default value, then it is an error.
• In accordance with the current Python implementation, any errors encountered will be signaled by
raising TypeError.
• What is "scope"?
A scope is the area of a Python program where an unqualified (not preceded by a module name) name
can be looked up.
Scopes are used dynamically. There are four nested scopes that are searched for names in the
following order:
Within a function, all assignments and declarations create local names. All variables found outside of
local scope (that is, outside of the function) are read-only.
Inside functions, local scope references the local names of the current function. Outside functions,
local scope is the same as the global scope – the module’s namespace. Class definitions also create a
local scope.
Nested functions provide another scope. Code in function B which is defined inside function A has
read-only access to all of A’s variables. This is called nonlocal scope.
Example
scope_examples.py
#!/usr/bin/env python
x = 42 ①
def function_a():
y = 5 ②
def function_b():
z = 32 ③
print("function_b(): z is", z) ④
print("function_b(): y is", y) ⑤
print("function_b(): x is", x) ⑥
print("function_b(): type(x) is", type(x)) ⑦
return function_b
f = function_a() ⑧
f() ⑨
① global variable
③ local variable
④ local scope
⑥ global scope
⑦ builtin scope
⑨ calling function_b
scope_examples.py
function_b(): z is 32
function_b(): y is 5
function_b(): x is 42
function_b(): type(x) is <class 'int'>
The global keyword allows a function to modify a global variable. This is universally acknowledged as
a BAD IDEA. Mutating global data can lead to all sorts of hard-to-diagnose bugs, because a function
might change a global that affects some other part of the program. It’s better to pass data into functions
as parameters and return data as needed. Mutable objects, such as lists, sets, and dictionaries can be
modified in-place.
The nonlocal keyword can be used like global to make nonlocal variables in an outer function
writable.
Modules
A module is a file containing Python definitions and statements. The file name is the module name
with the suffix .py appended. Within a module, the module’s name (as a string) is available as the value
of the global variable name.
This does not enter the names of the functions defined in spam directly into the symbol table; it only
adds the module name spam. Use the module name to access the functions or other attributes.
Python uses modules to contain functions that can be loaded as needed by scripts. A simple module
contains one or more functions; more complex modules can contain initialization code as well. Python
classes are also implemented as modules.
A module is only loaded once, even there are multiple places in an application that import it.
Using import
• Three variations
◦ import module
Variation 1
import module
loads the module so its data and functions can be used, but does not put its attributes (names of
classes, functions, and variables) into the current namespace.
Variation 2
from module import function, ...
imports only the function(s) specified into the current namespace. Other functions are not available
(even though they are loaded into memory).
Variation 3
from module import *
loads the module, and imports all functions that do not start with an underscore into the current
namespace. This should be used with caution, as it can pollute the current namespace and possibly
overwrite builtin attributes or attributes from a different module.
The first time a module is loaded, the interpreter creates a version compiled for faster
NOTE loading. This version has platform information embedded in the name, and has the
extension .pyc. These .pyc files are put in a folder named __pycache__.
Example
[Link]
#!/usr/bin/env python
def spam():
print("Hello from spam()")
def ham():
print("Hello from ham()")
def _eggs():
print("Hello from _eggs()")
use_samplelib1.py
#!/usr/bin/env python
import samplelib ①
[Link]() ②
[Link]()
① import samplelib module ([Link]) — creates object named samplelib of type "Module"
use_samplelib1.py
use_samplelib2.py
#!/usr/bin/env python
from samplelib import spam, ham ①
spam() ②
ham()
① import functions spam and ham from samplelib module into current namespace — does not create
the module object
use_samplelib2.py
use_samplelib3.py
#!/usr/bin/env python
from samplelib import * ①
spam() ②
ham()
① import all functions (that do not start with _) from samplelib module into current namespace
use_samplelib3.py
use_samplelib4.py
#!/usr/bin/env python
from samplelib import spam as pig, ham as hog ①
pig()
hog()
use_samplelib4.py
Using import * to import all public names from a module has a bit of a risk. While generally harmless,
there is the chance that you will unknowingly import a module that overwrites some previously-
imported module.
To be 100% certain, always import the entire module, or else import names explicitly.
Examples
[Link]
#!/usr/bin/env python
default_amps = 10
default_voltage = 110
default_current = 'AC'
def amps():
return default_amps
def voltage():
return default_voltage
def current():
return default_current
[Link]
#!/usr/bin/env python
def current():
return current_types[0]
why_import_star_is_bad.py
#!/usr/bin/env python
print(current()) ③
print(voltage())
print(amps())
why_import_star_is_bad.py
slow
110
10
how_to_avoid_import_star.py
#!/usr/bin/env python
import electrical as e ①
import navigation as n ②
print([Link]()) ③
print([Link]()) ④
how_to_avoid_import_star.py
AC
slow
When you specify a module to load with the import statement, it first looks in the current directory,
and then searches the directories listed in [Link].
To add locations, put one or more directories to search in the PYTHONPATH environment variable.
Separate multiple paths by semicolons for Windows, or colons for Unix/Linux. This will add them to
[Link], after the current folder, but before the predefined locations.
Windows
set PYTHONPATH=C:\Users\bob\Documents and settings\Python
Linux/OS X
export PYTHONPATH="/home/bob/python"
You can also append to [Link] in your scripts, but this can result in non-portable scripts, and scripts
that will fail if the location of the imported modules changes.
import sys
[Link]("/usr/dev/python/libs","/home/bob/pylib")
import module1
import module2
It is sometimes convenient to have a module also be a runnable script. This is handy for testing and
debugging, and for providing modules that also can be used as standalone utilities.
Since the interpreter defines its own name as '__main__', you can test the current namespace’s name
attribute. If it is '__main__', then you are at the main (top) level of the interpreter, and your file is being
run as a script; it was not loaded as a module.
Any code in a module that is not contained in function or method is executed when the module is
imported.
This can include data assignments and other startup tasks, for example connecting to a database or
opening a file.
Example
using_main.py
#!/usr/bin/env python
import sys
# main function
def main(args): ①
function1()
function2()
# other functions
def function1():
print("hello from function1()")
def function2():
print("hello from function2()")
if __name__ == '__main__':
main([Link][1:]) ②
① Program entry point. While main is not a reserved word, it is a strong convention
② Call main() with the command line parameters (omitting the script itself)
Packages
A package may have an initialization script named __init__.py. If present, this script is executed when
the package or any of its contents are loaded. (In Python 2, __init__.py was required).
Modules in packages are accessed by prefixing the module with the package name, using the dot
notation used to access module attributes.
Thus, if Module eggs is in package spam, to call the scramble() function in eggs, you would say
[Link]().
By default, importing a package name by itself has no effect; you must explicitly load the modules in
the packages. You should usually import the module using its package name, like from spam import
eggs, to import the eggs module from the spam package.
Example
django [Link]
[Link] [Link]
[Link] [Link]
[Link].i18n [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link].password_validation [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link].geoip2 [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link].module_loading
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link].jinja2 [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link]
[Link] [Link].i18n
The docstring in __init__.py is used to document the package itself. This is used by IDEs as well as
pydoc.
For convenience, you can put import statements in a package’s __init__.py to autoload the modules into
the package namespace, so that import PKG imports all the (or just selected) modules in the package.
If the variable __all__ in __init__.py is set to a list of module names, then only these modules will be
loaded when the import is
__init__.py can also be used to setup data or other resources that will be used by multiple modules
within a package.
Given the following package and module layout, the table on the next page describes how __init__.py
affects imports.
my_package
|------__init__.py
|------module_a.py
| function_a()
|------module_b.py
| function_b()
|------module_c.py
function_c()
If __init__.py is empty
import my_package Imports my_package only, but not contents. No modules are
imported. This is not useful.
import my_package.module_a Imports module_a into my_package namespace. Objects in
module_a must be prefixed with my_package.module_a
from my_package import module_a Imports module_a into main namespace. Objects in
module_a must be prefixed with module_a
from my_package import module_a, Imports module_a and module_b into main namespace.
module_b
from my_package import * Does not import anything!
from my_package.module_a import * Imports all contents of module_a (that do not start with an
underscore) into main namespace. Not generally
recommended.
If __init__.py contains:
all = ['module_a', 'module_b']
import my_package Imports my_package only, but not contents. No modules are
imported. This is still not useful.
from my_package import module_a As before, imports module_a into main namespace. Objects
in module_a must be prefixed with module_a
from my_package import * Imports module_a and module_b, but not module_c into
main namespace.
If __init__.py contains:
all = ['module_a', 'module_b'] import
module_a
import module_b
import my_package Imports module_a and module_b into the my_package
namespace. Objects in module_a must be prefixed with
my_package.module_a. Now this is useful.
from my_package import module_a Imports module_a into main namespace. Objects in
module_a must be prefixed with module_a
from my_package import * Only imports module_a and module_b into main
namespace.
from my_package import module_c Imports module_c into the main namespace.
• Use docstrings
In addition to comments, which are for the maintainer of your code, you should add docstrings, which
provide documentation for the user of your code.
If the first statement in a module, function, or class is an unassigned string, it is assigned as the
docstring of that object. It is stored in the special attribute _doc_, and so is available to code.
The docstring can use any form of literal string, but triple double quotes are preferred, for consistency.
Tools such as pydoc, and many IDEs will use the information in docstrings. In addition, the Sphinx tool
will gather docstrings from an entire project and format them as a single HTML, PDF, or EPUB
document.
Python style
• Comment thoroughly to explain why and how code works when not obvious
• Use docstrings to explain how to use modules, classes, methods, and functions
Guido van Rossum, Python’s BDFL (Benevolent Dictator For Life), once said that code is read much
more often than it is written. This means that once code is written, it may be read by the original
developer, users, subsequent developers who inherit your code. Do them a favor and make your code
readable. This in turn makes your code more maintainable.
To make your code readable, it is import to write your code in a consistent manner. There are several
Python style guides available, including PEP (Python Enhancement Proposal) 8, Style Guide for Python
Code, and PEP 257, Docstring Conventions.
If you are part of a development team, it is a good practice to put together a style guide for the team.
The team will save time not having to figure out each other’s style.
Chapter 2 Exercises
Exercise 2-1 ([Link], potus_main.py)
Create a module named potus ([Link]) to provide information from the [Link] file. It should
provide the following function:
'youngest' and 'oldest' refer to age at beginning of first term and age at end of last term.
Random historic note: if I hadn’t chosen the __dunder__ naming scheme for
Python language internals long ago, dunders would have been an obscure
feature of the C preprocessor.
What is a class?
• Represents a thing
A class is definition that represents a thing. The thing could be a file, a process, a database record, a
strategy, a string, a person, or a truck.
The class describes both data, which represents one instance of the thing, and methods, which are
functions that act upon the data. There can be both class data, which is shared by all instances, and
instance data, which is only accessible from the instance.
Classes are a very powerful tool to organize code. However, there are some circumstances
in Python where classes are not needed. If you just need some functions, and they don’t
TIP need to share or remember data, just put the functions in a module. If you just need some
data, but you don’t need functions to process it, just used a nested data structure built out
of dictionaries, lists, and tuples, as needed.
Defining Classes
• Syntax
class ClassName(base_class,...):
# class body – methods and data
class ClassName():
pass
Normally, the contents of a class definition will be method definitions and shared data.
A class definition creates a new local namespace. All variable assignments go into this new namespace.
All methods are called via the instance or the class name.
A list of base classes may be specified in parentheses after the class name.
Object Instances
• Syntax
obj = ClassName(args...)
An object instance is an object created from a class. Each object instance has its own private attributes,
which are usually created in the __init__() method.
Instance attributes
An instance of a class (AKA object) normally contains methods and data. To access these attributes, use
"dot notation": [Link].
Instance attributes are dynamic; they can be accessed directly from the object. You can create, update,
and delete attributes in this way.
Attributes cannot be made private, but names that begin with an underscore are understood by
convention to be for internal use only. Users of your class will not consider methods that begin with an
underscore to be part of your class’s API.
Example
class Spam():
def eggs(self):
pass
s = Spam()
[Link]()
[Link] = 'buttered'
print([Link])
Note that you can just create an attribute named toast without defining it anywhere. However, in most
cases, it is better to use properties (described later) to access data attributes.
Instance Methods
An instance method is a function defined in a class. When a method is called from an object, the object
is passed in as the implicit first parameter, named self by strong convention.
Example
[Link]
#!/usr/bin/env python
class Rabbit:
def threaten(self): ②
print("I am a {} bunny with {}!".format(self._size, self._danger))
[Link]
Constructors
• Named __init__()
If a class defines a method named __init__(), it will be automatically called when an object instance is
created. This is the constructor.
The object being created is implicitly passed as the first parameter to __init__() . This parameter is
named self by very strong convention. Data attributes can be assigned to self. These attributes can
then be accessed by other methods.
Example
class Rabbit:
Getter and setter methods can be used to access an object’s data. These are traditional in object-
oriented programming.
A getter method retrieves data (e.g., from a private variable) from self. A setter method assigns a value
to a variable.
NOTE Most Python developers use properties, described next, instead of getters and setters.
Example
class Knight(object):
def __init__(self,name):
self._name = name
def set_name(self,name):
self._name = name
def get_name(self):
return self._name
k = Knight("Lancelot")
print( k.get_name() )
Properties
• Can be read-only
While object attributes can be accessed directly, in many cases the class needs to exercise some control
over the attributes.
A more elegant approach is to use properties. A property is a kind of managed attribute. Properties are
accessed directly, like normal attributes (variables), but getter, setter, and deleter functions are
implicitly called, so that the class can control what values are stored or retrieved from the attributes.
To create the getter property (which must be created first), apply the @property decorator to a method
with the name you want. It receives no parameters other than self.
To create the setter property, create another function with the property name (yes, there will be two
function definitions with the same name). Decorate this with the property name plus ".setter". In other
words, if the property is named "spam", the decorator will be "@[Link]". The setter method will
take one parameter (other than self), which is the value assigned to the property.
It is common for a setter property to raise an error if the value being assigned is invalid.
While you seldom need a deleter property, creating it is the same as for a setter property, but use
"@[Link]".
Example
[Link]
#!/usr/bin/env python
class Knight():
def __init__(self, name, title, color):
self._name = name
self._title = title
self._color = color
@property ①
def name(self): ②
return self._name
@property
def color(self):
return self._color
@[Link] ③
def color(self, color):
self._color = color
@property
def title(self):
return self._title
if __name__ == '__main__':
k = Knight("Lancelot", "Sir", 'blue')
# Bridgekeeper's question
print('Sir {}, what is your...favorite color?'.format([Link])) ④
# Knight's answer
print("red, no -- {}!".format([Link]))
[Link] = 'red' ⑤
④ use property
⑤ set property
[Link]
Class Data
Data can be attached to the class itself, and shared among all instances. Class data can be accessed via
the class name from inside or outside of the class.
Any class attribute not overwritten by an instance attribute is also available through the instance.
Example
class_data.py
#!/usr/bin/env python
class Rabbit:
LOCATION = "the Cave of Caerbannog" ①
def display(self):
print("This rabbit guarding {} uses {} as a weapon".
format([Link], [Link])) ②
① class data
class_data.py
This rabbit guarding the Cave of Caerbannog uses a nice cup of tea as a weapon
This rabbit guarding the Cave of Caerbannog uses big pointy teeth as a weapon
Class Methods
If a method only needs class attributes, it can be made a class method via the @classmethod decorator.
This alters the method so that it gets a copy of the class object rather than the instance object. This is
true whether the method is called from the class or from an instance.
Example
class_methods_and_data.py
#!/usr/bin/env python
class Rabbit:
LOCATION = "the Cave of Caerbannog" ①
def display(self):
print("This rabbit guarding {} uses {} as a weapon".
format([Link], [Link])) ②
@classmethod ③
def get_location(cls): ④
return [Link] ⑤
② instance method
③ the @classmethod decorator makes a function receive the class object, not the instance object
class_methods_and_data.py
Inheritance
Any language that supports classes supports inheritance. One or more base classes may be specified as
part of the class definition. All of the previous examples in this chapter have used the default base
class, object.
The base class must already be imported, if necessary. If a requested attribute is not found in the class,
the search looks in the base class. This rule is applied recursively if the base class itself is derived from
some other class. For instance, all classes inherit the implementation from object, unless a class
explicitly implements it.
Classes may override methods of their base classes. (For Java and C++ programmers: all methods in
Python are effectively virtual.)
To extend rather than simply replace a base class method, call the base class method directly:
[Link](self, arguments).
Using super()
• Syntax:
super().method()
The super() function can be used in a class to invoke methods in base classes. It searches the base
classes and their bases, recursively, from left to right until the method is found.
The advantage of super() is that you don’t have to specify the base class explicitly, so if you change the
base class, it automatically does the right thing.
For classes that have a single inheritance tree, this works great. For classes that have a diamond-
shaped tree, super() may not do what you expect. In this case, using the explicit base class name is best.
class Foo(Bar):
def __init__(self):
super().__init__() # same as Bar._init__(self)
Example
[Link]
class Animal():
count = 0 ①
@property
def species(self):
return self._species
@classmethod
def kill(cls):
[Link] -= 1
@property
def name(self):
return self._name
def make_sound(self):
print(self._sound)
@classmethod
def remove(cls):
[Link] -= 1 ②
@classmethod
def zoo_size(cls): ③
return [Link]
if __name__ == "__main__":
leo = Animal("African lion", "Leo", "Roarrrrrrr")
garfield = Animal("cat", "Garfield", "Meowwwww")
felix = Animal("cat", "Felix", "Meowwwww")
① class data
[Link]
#!/usr/bin/env python
class Insect(Animal):
'''
An animal with 2 sets of wings and 3 pairs of legs
'''
@property
def can_fly(self): ③
return self._can_fly
if __name__ == '__main__':
mon = Insect('monarch butterfly', 'Mary', None) ④
scar = Insect('scarab beetle', 'Rupert', 'Bzzz', False)
③ "getter" property
[Link]
Multiple Inheritance
Python classes can inherit from more than one base class. This is called "multiple inheritance".
Classes designed to be added to a base class are sometimes called "mixin classes", or just "mixins".
Methods are searched for in the first base class, then its parents, then the second base class and
parents, and so forth.
Put the "extra" classes before the main base class, so any methods in those classes will override
methods with the same name in the base class.
To find the exact method resolution order (MRO) for a class, call the class’s mro() method.
TIP
Example
multiple_inheritance.py
#!/usr/bin/env python
class AnimalBase(): ①
def __init__(self, name):
self._name = name
def get_id(self):
print(self._name)
class CanBark(): ②
def bark(self):
print("woof-woof")
class CanFly(): ②
def fly(self):
print("I'm flying")
d = Dog('Dennis')
d.get_id() ④
[Link]() ⑤
print()
s = Sparrow('Steve')
s.get_id()
[Link]() ⑥
print()
multiple_inheritance.py
Dennis
woof-woof
Steve
I'm flying
The abc module provides abstract base classes. When a method in an abstract class is designated
abstract, it must be implemented in any derived class. If a method is not marked abstract, it may be
overwritten or extended.
To create an abstract class, import ABCMeta and abstractmethod. Create the base (abstract) class
normally, but assign ABCMeta to the class option metaclass. Then decorated any desired abstract
methods with *@abstractmethod.
Now, any classes that inherit from the base class must implement any abstract methods. Non-abstract
methods do not have to be implemented, but of course will be inherited.
NOTE abc also provides decorators for abstract properties and abstract class methods.
Example
abstract_base_classes.py
#!/usr/bin/env python
#
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta): ①
@abstractmethod ②
def speak(self):
pass
class Dog(Animal): ③
def speak(self): ④
print("woof! woof!")
class Cat(Animal): ③
def speak(self): ④
print("Meow meow meow")
class Duck(Animal): ③
pass ⑤
d = Dog()
[Link]()
c = Cat()
[Link]()
try:
d = Duck() ⑥
[Link]()
except TypeError as err:
print(err)
① metaclasses control how classes are created; ABCMeta adds restrictions to classes that inherit from
Animal
abstract_base_classes.py
woof! woof!
Meow meow meow
Can't instantiate abstract class Duck with abstract methods speak
Special Methods
• Override operators
Python has a set of special methods that can be used to make user-defined classes emulate the
behavior of builtin classes. These methods can be used to define the behavior for builtin functions such
as str(), len() and repr(); they can also be used to override many Python operators, such as +, *, and ==.
These methods expect the self parameter, like all instance methods. They frequently take one or more
additional methods. self. Is the object being called from the builtin function, or the left operand of a
binary operator such as ==.
For instance, if your object represented a database connection, you could have str() return the
hostname, port, and maybe the connection string. The default for str() is to call repr(), which returns
something like <[Link] object at 0xb7828c6c>, which is not nearly so user-friendly.
__eq__(self, other) Implement comparison operators ==, !=, >, <, >=,
__ne__(self, other) and ⇐. self is object on the left.
__gt__(self, other)
__lt__(self, other)
__ge__(self, other)
__le__(self, other)
[Link]
#!/usr/bin/env python
class Special():
def __str__(self): ④
return self._value.upper()
if __name__ == '__main__':
s = Special('spam')
t = Special('eggs')
u = Special\
('spam')
v = Special(5) ⑥
w = Special(22)
print("s + s", s + s) ⑦
print("s + t", s + t)
print("t + t", t + t)
print("s * 10", s * 10) ⑧
print("t * 3", t * 3)
print("str(s)={} str(t)={}".format(str(s), str(t)))
print("id(s)={} id(t)={} id(u)={}".format(id(s), id(t), id(u)))
print("s == s", s == s)
print("s == t", s == t)
print("s == u", s == u)
print("v + v", v + v)
print("v + w", v + w)
print("w + w", w + w)
print("v * 10", v * 10)
print("w * 3", w * 3)
② define what happens when a Special instance is added to another Special object
[Link]
s + s spamspam
s + t spameggs
t + t eggseggs
s * 10 spamspamspamspamspamspamspamspamspamspam
t * 3 eggseggseggs
str(s)=SPAM str(t)=EGGS
id(s)=140335238330768 id(t)=140335238330832 id(u)=140335238331344
s == s True
s == t False
s == u True
v + v 55
v + w 522
w + w 2222
v * 10 5555555555
w * 3 222222
Static Methods
A static method is a utility method that is related to the class, but does not need the instance or class
object. Thus, it has no automatic parameter.
One use case for static methods is to factor some kind of logic out of several methods, when the logic
doesn’t require any of the data in the class.
Chapter 3 Exercises
Exercise 3-1 ([Link], president_main.py)
Create a module that implements a President class. This class has a constructor that takes the index
number of the president (1-45) and creates an object containing the associated information from the
[Link] file.
Write a main script to exercise some or all of the properties. It could look something like
Chapter 4: Metaprogramming
Objectives
• Create metaclasses
Metaprogramming
Metaprogramming is writing code that generates or modifies other code. It includes fetching, changing,
or deleting attributes, and writing functions that return functions (AKA factories).
Metaprogramming is easier in Python than many other languages. Python provides explicit access to
objects, even the parts that are hidden or restricted in other languages.
For instance, you can easily replace one method with another in a Python class, or even in an object
instance. In Java, this would be deep magic requiring many lines of code.
The globals() builtin function returns a dictionary of all global objects. The keys are the object names,
and the values are the objects values. The dictionary is "live" — changes to the dictionary affect global
variables.
Example
globals_locals.py
#!/usr/bin/env python
from pprint import pprint ①
spam = 42 ②
ham = 'Smithfield'
def eggs(fruit): ③
name = 'Lancelot' ④
idiom = 'swashbuckling' ④
print("Globals:")
pprint(globals()) ⑤
print()
print("Locals:")
pprint(locals()) ⑥
eggs('mango')
② global variable
④ local variable
globals_locals.py
Globals:
{'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__cached__': None,
'__doc__': None,
'__file__': '/Users/jstrick/curr/courses/python/examples3/globals_locals.py',
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f9bf00ddcd0>,
'__name__': '__main__',
'__package__': None,
'__spec__': None,
'eggs': <function eggs at 0x7f9bf01a87a0>,
'ham': 'Smithfield',
'pprint': <function pprint at 0x7f9bf01a8290>,
'spam': 42}
Locals:
{'fruit': 'mango', 'idiom': 'swashbuckling', 'name': 'Lancelot'}
The inspect module provides user-friendly functions for accessing Python metadata.
Example
inspect_ex.py
#!/usr/bin/env python
import inspect
class Spam: ①
pass
print()
① define a class
② define a function
inspect_ex.py
Current frame:
Traceback(filename='/Users/jstrick/curr/courses/python/examples3/inspect_ex.py',
lineno=26, function='<module>', code_context=['print("Current frame:",
[Link]([Link]())) # <7>\n'], index=0)
Function(s) Description
• Syntax
All Python objects are essentially dictionaries of attributes. There are four special builtin functions for
managing attributes. These may be used to programmatically access attributes when you have the
name as a string.
getattr() returns the value of a specified attribute, or raises an error if the object does not have that
attribute. getattr(a,'spam') is the same as [Link]. An optional third argument to getattr() provides a
default value for nonexistent attributes (and does not raise an error).
hasattr() returns the value of a specified attribute, or None if the object does not have that attribute.
Example
[Link]
#!/usr/bin/env python
class Spam():
s = Spam()
[Link]("fried")
e = getattr(s, 'eggs') ③
e("scrambled")
[Link]("buttered!")
delattr(Spam, 'eggs') ⑤
try:
[Link]("shirred")
except AttributeError as err: ⑥
print(err)
① create attribute
③ retrieve attribute
⑤ remove attribute
[Link]
eggs! fried
hasattr() True
eggs! scrambled
toast! buttered!
'Spam' object has no attribute 'eggs'
• Use setattr()
Using setattr(), it is easy to add instance methods to classes. Just add a function object to the class.
Because it is part of the class itself, it will automatically be bound to the instance. Remember that an
instance method expects self as the first parameter. In fact, this is the meaning of a bound instance — it
is "bound" to the instance, and therefore when called, it is passed the instance as the first parameter.
Once added, the method may be called from any existing or new instance of the class.
To add an instance method to an instance takes a little more effort. Because it’s not being added to the
class, it is not automatically bound. The function needs to know what instance it should be bound to.
This can be accomplished with the [Link] function.
Example
adding_instance_methods.py
#!/usr/bin/env python
from types import MethodType
class Dog(): ①
pass
d1 = Dog() ②
def bark(self): ③
print("Woof! woof!")
d2 = Dog() ⑤
[Link]() ⑥
[Link]()
def wag(self): ⑦
print("Wagging...")
[Link]() ⑨
try:
[Link]() ⑩
except AttributeError as err:
print(err)
adding_instance_methods.py
Woof! woof!
Woof! woof!
Wagging...
'Dog' object has no attribute 'wag'
Callable classes
• Implement __call__
Any class instance may be made callable by implementing the special method call. This means that
rather than saying:
sc = SomeClass()
sc.some_method()
sc = SomeClass()
sc()
What’s the advantage? Really, not too much. It just saves having to call a method from the instance,
letting you call the instance itself. The use case is for classes that only have one method.
You can think of a callable class as a function that can also keep some state. As with many object-
oriented features, its main purpose is to simplify the user interface.
One good use of callable classes is for implementing decorators as classes, rather than functions.
Example
callable_class.py
#!/usr/bin/env python
class TagWrapper():
def __init__(self, tag):
self._tag = tag
class HTMLWrapper():
if __name__ == '__main__':
# non-callable class
t = TagWrapper('h1')
print([Link]('foo'))
print([Link]('bar'))
print()
# callable class
h1 = HTMLWrapper('h1') ②
print(h1('spam')) ③
div = HTMLWrapper('div')
print(div('ham'))
print(div('toast'))
print(div('jam'))
callable_class.py
<h1>foo</h1>
<h1>bar</h1>
<h1>spam</h1>
<div>ham</div>
<div>toast</div>
<div>jam</div>
Decorators
In Python, many decorators are provided by the standard library, such as property() or classmethod()
A decorator is a component that modifies some other component. The purpose is typically to add
functionality, but there are no real restrictions on what a decorator can do. Many decorators register a
component with some other component. For instance, the @[Link]() decorator in Flask maps a URL
to a view function.
As another example, unittest provides decorators to skip tests. A very common decorator is
@property, which converts a class method into a property object.
A decorator can be any callable, which means it can be a normal function, a class method, or a class
which implements the __call__() method (AKA callable class, as discussed earlier).
A simple decorator expects the item being decorated as its parameter, and returns a replacement.
Typically, the replacement is a new function, but there is no restriction on what is returned. If the
decorator itself needs parameters, then the decorator returns a wrapper function that expects the item
being decorated, and then returns the replacement.
Decorator Description
@classmethod Indicate class method (receives class object, not instance object)
@[Link] Define factory function for with statement context managers (no
need to create __enter__() and __exit__() methods)
@functools.total_ordering Supply all other comparison methods if class defines at least one.
@staticmethod Indicate static method (passed neither instance nor class object).
@[Link] Patch target with a new object. When the function/with statement
exits patch is undone.
Applying decorators
• Use @ symbol
• Multiple decorators OK
The @ sign is used to apply a decorator to a function or class. A decorator only applies to the next
definition in the script.
The most important thing to know about the decorators is the following syntax:
@spam
def ham():
pass
ham = spam(ham)
and
@spam(a, b, c)
def ham():
pass
Once you understand this, then creating decorators is just a matter of writing functions or classes and
having them return the appropriate thing.
Trivial Decorator
A decorator does not have to be elaborate. It can return anything, though typically decorators return
the same type of object they are decorating.
In this example, the decorator returns the integer value 42. This is not particularly useful, but
illustrates that the decorator always replaces the object being decorated with something.
Example
deco_trivial.py
#!/usr/bin/env python
def void(thing_being_decorated):
return 42 ①
name = "Guido"
x = void(name)
@void ②
def hello():
print("Hello, world")
@void
def howdy():
print("Howdy, world")
print(hello, type(hello)) ③
print(howdy, type(howdy)) ③
print(x, type(x))
deco_trivial.py
42 <class 'int'>
42 <class 'int'>
42 <class 'int'>
Decorator functions
• Purposes
◦ Add functionality
◦ Register
◦ ??? (open-ended)
• Optional parameters
A decorator function acts as a wrapper around some object (usually function or class). It allows you to
add features to a function without changing the function itself. For instance, the @property,
@classmethod, and @staticmethod decorators are used in classes.
A simple decorator function expects only one argument – the function to be modified. It should return
a new function, which will replace the original. The replacement function typically calls the original
function as well as some new code. More complex decorators expect parameters to the decorator itself.
In this case the decorator returns a function that expects the original function, and returns the
replacement function.
The new function should be defined with generic arguments (*args, **kwargs) so it can handle any
combination of arguments for the original function.
The wraps decorator from the functools module in the standard library should be used with the
function that returns the replacement function. This makes sure the replacement function keeps the
same properties (especially the name) as the original (target) function. Otherwise, the replacement
function keeps all of its own attributes.
Example
deco_debug.py
#!/usr/bin/env python
def debugger(old_func): ①
@wraps(old_func) ②
def new_func(*args, **kwargs): ③
print("*" * 40) ④
print("** function", old_func.__name__, "**") ④
if args: ④
print("\targs are ", args)
if kwargs: ④
print("\tkwargs are ", kwargs)
print("*" * 40) ④
return new_func ⑥
@debugger ⑦
def hello(greeting, whom='world'):
print("{}, {}".format(greeting, whom))
hello('hello', 'world') ⑧
print()
hello('hi', 'Earth')
print()
hello('greetings')
deco_debug.py
****************************************
** function hello **
args are ('hello', 'world')
****************************************
hello, world
****************************************
** function hello **
args are ('hi', 'Earth')
****************************************
hi, Earth
****************************************
** function hello **
args are ('greetings',)
****************************************
greetings, world
Decorator Classes
◦ No parameters
◦ Expects parameters
A class can also be used to implement a decorator. The advantage of using a class for a decorator is that
a class can keep state, so that the replacement function can update information stored at the class
level.
If the decorator does not need parameters, the class must implement two methods: __init__() is passed
the original function, and can perform any setup needed. The __call__ method replaces the original
function. In other word, after the function is decorated, calling the function is the same as calling
CLASS.__call__.
If the decorator does need parameters, __init__() is passed the parameters, and __call__() is passed the
original function, and must return the replacement function.
A good use for a decorator class is to log how many times a function has been called, or even keep
track of the arguments it is called with (see example for this).
Example
deco_debug_class.py
#!/usr/bin/env python
class debugger(): ①
function_calls = []
# print("*" * 40) ④
# print("function {}()".format(self._func.__name__)) ④
# print("\targs are ", args) ④
# print("\tkwargs are ", kwargs) ④
#
# print("*" * 40) ④
self.function_calls.append( ⑤
(self._func.__name__, args, kwargs)
)
@classmethod
def get_calls(cls): ⑧
return cls.function_calls
@debugger ⑨
def hello(greeting, whom="world"):
print("{}, {}".format(greeting, whom))
@debugger ⑨
def bark(bark_word, *, repeat=2):
print("{0}! ".format(bark_word) * repeat)
hello('hello', 'world') ⑩
print()
hello('hi', 'Earth')
print()
hello('greetings')
bark("woof", repeat=3)
bark("yip", repeat=4)
bark("arf")
hello('hey', 'girl')
print('-' * 60)
deco_debug_class.py
hello, world
hi, Earth
greetings, world
woof! woof! woof!
yip! yip! yip! yip!
arf! arf!
hey, girl
------------------------------------------------------------
1. hello ('hello', 'world') {}
2. hello ('hi', 'Earth') {}
3. hello ('greetings',) {}
4. bark ('woof',) {'repeat': 3}
5. bark ('yip',) {'repeat': 4}
6. bark ('arf',) {}
7. hello ('hey', 'girl') {}
Decorator parameters
For decorators implemented as functions, the decorator itself is passed the parameters; it contains a
nested function that is passed the decorated function (the target), and it returns the replacement
function.
For decorators implemented as classes, init is passed the parameters, __call__() is passed the decorated
function (the target), and __call__ returns the replacement function.
There are many combinations of decorators (8 total, to be exact). This is because decorators can be
implemented as either functions or classes, they may take parameters, or not, and they can decorate
either functions or classes. For an example of all 8 approaches, see the file [Link] in the
EXAMPLES folder.
Example
deco_params.py
#!/usr/bin/env python
#
def multiply(multiplier): ②
def deco(old_func): ③
@wraps(old_func) ④
def new_func(*args, **kwargs): ⑤
result = old_func(*args, **kwargs) ⑥
return result * multiplier ⑦
return new_func ⑧
return deco ⑨
@multiply(4)
def spam():
return 5
@multiply(10)
def ham():
return 8
a = spam()
b = ham()
print(a, b)
deco_params.py
20 80
A class can be created programmatically, without the use of the class statement. The syntax is
The first argument is the name of the class, the second is a tuple of base classes (use object if you are
not inheriting from a specific class), and the third is a dictionary of the class’s attributes.
Example
creating_classes.py
#!/usr/bin/env python
def function_1(self): ①
print("Hello from f1()")
def function_2(self): ①
print("Hello from f2()")
n1 = NewClass() ③
n1.hello1() ④
n1.hello2()
print([Link]) ⑤
print()
② create class using type() — parameters are class name, base classes, dictionary of attributes
creating_classes.py
Monkey Patching
"Monkey patching" refers to technique of changing the behavior of an object by adding, replacing, or
deleting attributes from outside the object’s class definition.
If you are not careful when creating monkey patches, some hard-to-debug problems can arise
• If the object being patched changes after a software upgrade, the monkey patch can fail in
unexpected ways.
• Conflicts may occur if two different modules monkey-patch the same object.
• Users of a monkey-patched object may not realize which behavior is original and which comes
from the monkey patch.
Decorators are a convenient way to monkey-patch a class. The decorator can just add a method to the
decorated class.
Example
meta_monkey.py
#!/usr/bin/env python
class Spam(): ①
def eggs(self): ②
print("Good morning, {}. Here are your delicious fried eggs.".format(self._name,
))
s = Spam('Mrs. Higgenbotham') ③
[Link]() ④
def scrambled(self): ⑤
print("Hello, {}. Enjoy your scrambled eggs".format(self._name, ))
[Link]() ⑦
④ call method
meta_monkey.py
Good morning, Mrs. Higgenbotham. Here are your delicious fried eggs.
Hello, Mrs. Higgenbotham. Enjoy your scrambled eggs
• Deep magic
Before we cover the details of metaclasses, a disclaimer: you will probably never need to use a
metaclass. When you think you might need a metaclass, consider using inheritance or a class
decorator. However, metaclasses may be a more elegant approach to certain kinds of tasks, such as
registering classes when they are defined.
There are two use cases where metaclasses are always an appropriate solution, because they must be
done before the class is created:
Several popular frameworks use metaclasses, Django in particular. In Django they are used for models,
forms, form fields, form widgets, and admin media.
Remember that metaclasses can be a more elegant way to accomplish things that can also be done with
inheritance, composition, decorators, and other techniques that are less "magic".
About metaclasses
• Metaclass:Class::Class:Object
The primary reason for a metaclass is to provide extra functionality at class creation time, not instance
creation time. Just as a class can share state and actions across many instances, a metaclass can share
(or provide) data and state across many classes.
The metaclass might modify the list of base classes, or register the class for later retrieval.
As we saw earlier ,you can create a class from a metaclass by passing in the new class’s name, a tuple
of base classes (which can be empty), and a dictionary of class attributes (which also can be empty).
class Spam(Ham):
id = 1
is exactly equivalent to
Replacing "type" with the name of any other metaclass works the same.
Mechanics of a metaclass
• Can implement
◦ __init__
◦ __prepare__
◦ __call__
To create a metaclass, define a normal class. Most metaclasses implement the __new__ method. This
method is called with the type, name, base classes, and attribute dictionary (if any) of the new class. It
should return a new class, typically using super().__new__(), which is very similar to how normal
classes create instances. This is one place you can modify the class being created. You can add or
change attributes, methods, or properties.
For instance, the Django framework uses metaclasses for Models. When you create an instance of a
Model, the metaclass code automatically creates methods for the fields in the model. This is called
"declarative programming", and is also used in SqlAlchemy’s declarative model, in a way pretty similar
to Django.
class SomeClass(metaclass=SomeMeta):
pass
META(name, bases, attrs) is executed, where META is the metaclass (normally type()). Then,
obj = SomeClass()
Example
metaclass_generic.py
#!/usr/bin/env python
class Meta(type):
:param cls: The class being created (compare with 'self' in normal class)
:param args: Any arguments to the class
"""
print("in metaclass (class={}) __init__()".format(cls.__name__), end=' ==> ')
print("params: cls={}, args={}".format(cls, args))
super().__init__(cls)
:param args:
:param args:
:param kwargs:
:return:
"""
print("in metaclass (class={})__call__()".format(self.__name__))
class MyBase():
pass
print('=' * 60)
def __init__(self):
print("In class A __init__()")
print('=' * 60)
def __init__(self):
print("In class B __init__()")
print('-' * 60)
m1 = A()
print('-' * 60)
m2 = B()
print('-' * 60)
m3 = A()
print('-' * 60)
m4 = B()
print('-' * 60)
print("animal: {} id: {}".format([Link], [Link]))
metaclass_generic.py
============================================================
in metaclass (class=A) __prepare__() ==> params: name=A, bases=(<class
'__main__.MyBase'>,)
in metaclass (class=A) __new__() ==> params: type=<class '__main__.Meta'> name=A
bases=(<class '__main__.MyBase'>,) attrs={'animal': 'wombat', 'id': 5, '__module__':
'__main__', '__qualname__': 'A', '__init__': <function A.__init__ at 0x7f972802c710>}
in metaclass (class=A) __init__() ==> params: cls=<class '__main__.A'>, args=('A',
(<class '__main__.MyBase'>,), {'animal': 'wombat', 'id': 5, '__module__': '__main__',
'__qualname__': 'A', '__init__': <function A.__init__ at 0x7f972802c710>})
============================================================
in metaclass (class=B) __prepare__() ==> params: name=B, bases=(<class
'__main__.MyBase'>,)
in metaclass (class=B) __new__() ==> params: type=<class '__main__.Meta'> name=B
bases=(<class '__main__.MyBase'>,) attrs={'animal': 'wombat', 'id': 100, '__module__':
'__main__', '__qualname__': 'B', '__init__': <function B.__init__ at 0x7f972802ca70>}
in metaclass (class=B) __init__() ==> params: cls=<class '__main__.B'>, args=('B',
(<class '__main__.MyBase'>,), {'animal': 'wombat', 'id': 100, '__module__': '__main__',
'__qualname__': 'B', '__init__': <function B.__init__ at 0x7f972802ca70>})
------------------------------------------------------------
in metaclass (class=A)__call__()
------------------------------------------------------------
in metaclass (class=B)__call__()
------------------------------------------------------------
in metaclass (class=A)__call__()
------------------------------------------------------------
in metaclass (class=B)__call__()
------------------------------------------------------------
animal: wombat id: 100
• Classic example
• Simple to implement
One of the classic use cases for a metaclass in Python is to create a singleton class. A singleton is a class
that only has one actual instance, no matter how many times it is instantiated. Singletons are used for
loggers, config data, and database connections, for instance.
To create a single, implement a metaclass by defining a class that inherits from type. The class should
have a class-level dictionary to store each class’s instance. When a new instance of a class is created,
check to see if that class already has an instance. If it does not, call __call__ to create the new instance,
and add the instance to the dictionary.
In either case, then return the instance where the key is the class object.
Example
metaclass_singleton.py
#!/usr/bin/env python
class Singleton(type): ①
_instances = {} ②
return cls._instances[cls] ⑥
class ThingA(metaclass=Singleton): ⑦
def __init__(self, value):
[Link] = value
class ThingB(metaclass=Singleton): ⑦
def __init__(self, value):
[Link] = value
ta1 = ThingA(1) ⑧
ta2 = ThingA(2)
ta3 = ThingA(3)
tb1 = ThingB(4)
tb2 = ThingB(5)
tb3 = ThingB(6)
⑨ Print the type, name, and ID of each thing — only one instance is ever created for each class
metaclass_singleton.py
ThingA 140241554520464 1
ThingA 140241554520464 1
ThingA 140241554520464 1
ThingB 140241554520528 4
ThingB 140241554520528 4
ThingB 140241554520528 4
Chapter 4 Exercises
Exercise 4-1 (pres_attr.py)
Instantiate the President class. Get the first name, last name, and party attributes using getattr().
Monkey-patch the President class to add a method get_full_name which returns a single string
consisting of the first name and the last name, separated by a space.
Without using the class statement, create a class named SillyString, which is initialized with any string.
Include an instance method called every_other which returns every other character of the string.
Instantiate your string and print the result of calling the every_other() method. Your test code should
look like this:
ss = SillyString('this is a test')
print(ss.every_other())
It should output
ti sats
Write a decorator to double the return value of any function. If a function returns 5, after decoration it
should return 10. If it returns "spam", after decoration it should return "spamspam", etc.
Write a decorator, implemented as a class, to register functions that will process a list of words. The
decorated functions will take one parameter — a string — and return the modified string.
The decorator itself takes two parameters — minimum length and maximum length. The class will
store the min/max lengths as the key, and the functions as values, as class data.
The class will also provide a method named process_words, which will open DATA/[Link] and
read it line by line. Each line contains a word.
For every registered function, if the length of the current word is within the min/max lengths, call all
the functions whose key is that min/max pair.
In other words, if the registry key is (5, 8), and the value is [func1, func2], when the current word is
within range, call func1(w) and func2(w), where w is the current word.
Remember all the decorated functions take one argument, which is one of the strings in the word list,
and return the modified word.
• Debug scripts
Program development
◦ Design first
◦ Consistent style
◦ Comments
◦ Debugging
◦ Testing
◦ Documentation
Comments
Comments that contradict the code are worse than no comments. Always make a priority of keeping
the comments up-to-date when the code changes!
Comments should be complete sentences. If a comment is a phrase or sentence, its first word should be
capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of
identifiers!).
Block comments generally apply to some (or all) code that follows them, and are indented to the same
level as that code. Each line of a block comment starts with a # and a single space (unless it is indented
text inside the comment).
Use inline comments sparingly. Inline comments should be separated by at least two spaces from the
statement; they should start with a # and a single space.
Inline comments are unnecessary and in fact distracting if they state the obvious. Don’t do this:
x = x + 1 # Increment x
Only use an inline comment if the reason for the statement is not obvious:
pylint
• Finds mistakes
pylint is a Python source code analyzer which looks for programming errors, helps enforcing a coding
standard and sniffs for some code smells (as defined in Martin Fowler’s Refactoring book)
pylint can be very helpful in identifying errors and pointing out where your code does not follow
standard coding conventions. It was developed by Python coders at Logilab [Link]
It has very verbose output, which can be modified via command line options.
pylint usage:
Customizing pylint
• Redirect to file
• Edit as needed
To customize pylint, run pylint with only the -generate-rcfile option. This will output a well-commented
configuration file to STDOUT, so redirect it to a file.
Edit the file as needed. The comments describe what each part does. You can change the allowed
names of variables, functions, classes, and pretty much everything else. You can even change the rating
algorithm.
Windows
Put the file in a convenient location (name it something like pylintrc). Invoke pylint with the –rcfile
option to specify the location of the file.
pylint will also find a file named pylintrc in the current directory, without needing the -rcfile option.
Non-Windows systems
On Unix-like systems (Unix, Mac OS, Linux, etc.), /etc/pylintrc and ~/.pylintrc will be automatically
loaded, in that order.
Using pyreverse
• Source analyzer
• Part of pylint
pyreverse is a Python source code analyzer. It reads a script, and the modules it depends on, and
generates UML diagrams. It is installed as part of the pylint package.
There are many options to control what it analyzes and what kind of output it produces.
Use -A' to search all ancestors, `-p to specify the project name, -o to specify output type (e.g., pdf,
png, jpg).
pyreverse requires Graphviz, a graphics tool that must be installed separately from
NOTE
Python
Example
packages_MyProject.png
classes_MyProject.png
• Based on gdb
While most IDEs have an integrated debugger, it is good to know how to debug from the command
line. The pdb module provides debugging facilities for Python.
Once the program starts, it will pause at the first executable line of code and provide a prompt, similar
to the interactive Python prompt. There is a large set of debugging commands you can enter at the
prompt to step through your program, set breakpoints, and display the values of variables.
Since you are in the Python interpreter as well, you can enter any valid Python expression.
• Syntax
or
import pdb
[Link]('function')
The debugger provides several commands for stepping through a program. Use s to step through one
line at a time, stepping into functions.
Use n to step over functions; use r to return from a function; use c to continue to next breakpoint or
end of program.
Pressing Enter repeats most commands; if the previous command was list, the debugger lists the next
set of lines.
Setting breakpoints
• Syntax
Breakpoints can be set with the b command. Specify a line number, or a function name, optionally
preceded by the filename that contains it.
Any of the above can be followed by an expression (use comma to separate) to create a conditional
breakpoint.
The tbreak command creates a one-time breakpoint that is deleted after it is hit the first time.
Profiling
Profiling is the technique of discovering the part of your code where your application spends the most
time. It can help you find bottlenecks in your code that might be candidates for revision or refactoring.
This will output a simple report to STDOUT. You can also specify an output file with the -o option, and
the sort order with the -s option. See the docs for more information.
Example
Benchmarking
Use the timeit module to benchmark two or more code snippets. To time code, create a Timer object,
which takes two strings of code. The first is the code to test; the second is setup code, that is only run
once per timer .
Call the timeit() method with the number of times to call the test code, or call the repeat() method
which repeats timeit() a specified number of times.
You can also use the timeit module from the command line. Use the -s option to specify startup code:
Example
bm_range_vs_while.py
#!/usr/bin/env python
from timeit import Timer
setup_code = """
values = []
""" ①
test_code_one = '''
for i in range(10000):
[Link](i)
[Link]()
''' ②
test_code_two = '''
i = 0
while i < 10000:
[Link](i)
i += 1
[Link]()
''' ②
t1 = Timer(test_code_one, setup_code) ③
t2 = Timer(test_code_two, setup_code) ③
print("test one:")
print([Link](1000)) ④
print()
print("test two:")
print([Link](1000)) ④
print()
bm_range_vs_while.py
test one:
0.581884131
test two:
0.8679698449999999
Chapter 5 Exercises
Exercise 5-1
Pick several of your scripts (from class, or from real life) and run pylint on them.
Exercise 5-2
Use the builtin debugger or one included with your IDE to step through any of the scripts you have
written so far.
A unit test is a test which asserts that an isolated piece of code (one function, method, class, or module)
has some expected behavior. It is a way of making sure that code provides repeatable results.
1. Unit tests – individual assertions that an expected condition has been met
4. Test runners – utilities to execute the tests in one or more test cases
Unit tests should each test one aspect of your code, and each test should be independent of all other
tests, including the order in which tests are run.
Unit tests may collected into a test case, which is a related group of unit tests. With pytest, a test case
can be either a module or a class.
The final component is a Test runner, which executes one, some, or all tests and reports on the results.
There are many different test runners for pytest. The builtin runner is very flexible.
• Provides
◦ test runner
◦ fixtures
◦ special assertions
◦ extra tools
1
• Not based on xUnit
The pytest module provides tools for creating, running, and managing unit tests.
Each test supplies one or more assertions. An assertion confirms that some condition is true.
unit test
A normal Python function that uses the assert statement to assert some condition is true
test case
A class or a module than contains unit tests (tests can be grouped with markers).
fixture
A special parameter of a unit test function that provides test resources (fixtures can be nested).
test runner
A text-based test runner is built in, and there are many third-party test runners
pytest is more flexible than classic xUnit implementations. For example, fixtures can be associated
with any number of individual tests, or with a test class. Test cases need not be classes.
1
The builtin unit testing module, unittest, is based on xUnit patterns, as implemented in Java and
other languages.
Creating tests
• Optional message
To create a test, create a function whose name begins with "test". These should normally be in a
separate script, whose name begins with "test_" or ends with "_test". For the simplest cases, tests do not
even need to import pytest.
Each test function should use the builtin assert statement one or more times to confirm that the test
passes. If the assertion fails, the test fails.
pytest will print an appropriate message by introspecting the expression, or you can add your own
message after the expression, separated by a comma
It is a good idea to make test names verbose. This will help when running tests in verbose mode, so you
can see what tests are passing (or failing).
Example
pytests/test_simple.py
#!/usr/bin/env python
def test_two_plus_two_equals_four(): ①
assert 2 + 2 == 4 # ②
① tests should begin with "test" (or will not be found automatically)
To actually run tests, you need a test runner. A test runner is software that runs one or more tests and
reports the results.
You can run a single test, a test case, a module, or all tests in a folder and all its subfolders.
pytest test_…py
pytest -v test_…py
By default, pytest captures (and does not display) anything written to stdout/stderr. If you want to see
the output of print() statements in your tests, add the -s option, which turns off output capture.
pytest -s …
In older versions of pytest, the test runner script was named [Link]. While newer
NOTE
versions support that name, the developers recommend only using pytest.
PyCharm automatically detects a script containing test cases. When you run the script the
first time, PyCharm will ask whether you want to run it normally or use its builtin test
TIP runner. Use Edit Configurations to modify how the script is run. Note: in PyCharm’s
settings, you can select the default test runner to be pytest, Unittest, or other test
runners.
Special assertions
• Special cases
◦ [Link]()
◦ [Link]()
[Link]
For testing whether an exception is raised, use [Link](). This should be used with the with
statement:
with [Link](ValueError):
w = Wombat('blah')
The assertion will succeed if the code inside the with block raises the specified error.
[Link]
For testing whether two floating point numbers are close enough to each other, use [Link]():
The default tolerance is 1e-6 (one part in a million). You can specify the relative or absolute tolerance
to any degree. Infinity and NaN are special cases. NaN is normally not equal to anything, even itself,
but you can specify nanok=True as an argument to approx().
Example
pytests/test_special_assertions.py
#!/usr/bin/env python
import pytest
import math
FILE_NAME = '[Link]'
def test_missing_filename():
with [Link](FileNotFoundError): ①
open(FILE_NAME) ②
def test_list():
print()
assert (.1 + .2) == [Link](.3) ③
def test_approximate_pi():
assert 22 / 7 == [Link]([Link], .001) ④
③ fail unless values are within 0.000001 of each other (actual result is 0.30000000000000004)
Fixtures
• Implement as functions
• Scope
◦ Per test
◦ Per class
◦ Per module
• Source of fixtures
◦ Builtin
◦ User-defined
When writing tests for a particular object, many tests might require an instance of the object. This
instance might be created with a particular set of arguments.
What happens if twenty different tests instantiate a particular object, and the object’s API changes?
Now you have to make changes in twenty different places.
To avoid duplicating code across many tests, pytest supports fixtures, which are functions that provide
information to tests. The same fixture can be used by many tests, which lets you keep the fixture
creation in a single place.
A fixture provides items needed by a test, such as data, functions, or class instances.
TIP Use [Link] --fixtures to list all available builtin and user-defined fixtures.
User-defined fixtures
To create a fixture, decorate a function with [Link]. Whatever the function returns is the value
of the fixture.
To use the fixture, pass it to the test function as a parameter. The return value of the fixture will be
available as a local variable in the test.
Fixtures can take other fixtures as parameters as well, so they can be nested to any level.
It is convenient to put fixtures into a separate module so they can be shared across multiple test
scripts.
TIP Add docstrings to your fixtures and the docstrings will be displayed via pytest --fixtures
Example
pytests/test_simple_fixture.py
#!/usr/bin/env python
from collections import namedtuple
import pytest
FIRST_NAME = "Guido"
LAST_NAME = "Von Rossum"
@[Link] ②
def person():
"""
Return a 'Person' named tuple with fields 'first_name' and 'last_name'
"""
return Person(FIRST_NAME, LAST_NAME) ③
def test_first_name(person): ④
assert person.first_name == FIRST_NAME
def test_last_name(person): ④
assert person.last_name == LAST_NAME
Builtin fixtures
• Provide
◦ Logging
◦ STDOUT/STDERR capture
◦ Monkeypatching tools
Pytest provides a large number of builtin fixtures for common testing requirements.
Using a builtin fixture is like using user-defined fixtures. Just specify the fixture name as a parameter
to the test. No imports are needed for this.
Example
pytests/test_builtin_fixtures.py
COUNTER_KEY = 'test_cache/counter'
def test_cache(cache): ①
value = [Link](COUNTER_KEY, 0)
print("Counter before:", value)
[Link](COUNTER_KEY, value + 1) ②
value = [Link](COUNTER_KEY, 0) ②
print("Counter after:", value)
assert True ③
def hello():
print("Hello, pytesting world")
def test_capsys(capsys):
hello() ④
out, err = [Link]() ⑤
print("STDOUT:", out)
def bhello():
print(b"Hello, binary pytesting world\n")
def test_capsysbinary(capsys):
bhello() ⑥
out, err = [Link]() ⑦
print("BINARY STDOUT:", out)
def test_temp_dir1(tmpdir):
print("TEMP DIR:", str(tmpdir)) ⑧
def test_temp_dir2(tmpdir):
print("TEMP DIR:", str(tmpdir))
def test_temp_dir3(tmpdir):
print("TEMP DIR:", str(tmpdir))
② cache fixture is similar to dictionary, but with .set() and .get() methods
record_xml_attribute Add extra xml attributes to the tag for the calling test.
Configuring fixtures
• Create [Link]
• Automatically included
• Provides
◦ Fixtures
◦ Hooks
◦ Plugins
• Directory scope
The [Link] file can be used to contain user-defined fixtures, as well as hooks and plugins.
Subfolders can have their own [Link], which will only apply to tests in that folder.
In a test folder, define one or more fixtures in [Link], and they will be available to all tests in that
folder, as well as any subfolders.
Hooks
Hooks are predefined functions that will automatically be called at various points in testing. All hooks
start with pytest_. A [Link] object, which contains the actual test function, is passed into the
hook.
Plugins
There are many pytest plugins to provide helpers for testing code that uses common libraries, such as
Django or redis.
Example
pytests/stuff/[Link]
#!/usr/bin/env python
from pytest import fixture
@fixture
def common_fixture(): ①
return "DATA"
def pytest_runtest_setup(item): ②
print("Hello from setup,", item)
① user-defined fixture
Example
pytests/stuff/test_stuff.py
#!/usr/bin/env python
import pytest
def test_one(): ①
print("WHOOPEE")
assert(1)
def test_two(common_fixture): ②
assert(common_fixture == "DATA")
if __name__ == '__main__':
[Link]([__file__, "-s"]) ③
pytests/stuff/test_stuff.py
Parametrizing tests
• Use [Link]()
Many tests require testing a method or function against many values. Rather than writing a loop in the
test, you can automatically repeat the test for a set of inputs via parametrizing.
Apply the @[Link] decorator to the test. The first argument is a string with the
comma-separated names of the parameters; the second argument is the list of parameters. The test will
be called once for each item in the parameter list. If a parameter list item is a tuple or other multi-
value object, the items will be passed to the test based on the names in the first argument.
For more advanced needs, when you need some extra work to be done before the test,
NOTE you can do indirect parametrizing, which uses a parametrized fixture. See
test_parametrize_indirect.py for an example.
Example
pytests/test_parametrization.py
#!/usr/bin/env python
import pytest
def triple(x): ①
return x * 3
@[Link]("input,result", test_data) ③
def test_triple(input, result): ④
print("input {} result {}:".format(input, result)) ④
assert triple(input) == result ⑤
if __name__ == "__main__":
[Link]([__file__, '-s'])
① Function to test
③ Parametrize the test with the test data; the first argument is a string defining parameters to the test
and mapping them to the test data
④ The test expects two parameters (which come from each element of test data)
pytests/test_parametrization.py
Marking tests
• Use @[Link]()
You can mark tests with labels so that they can be run as a group. Use @[Link](), where
marker is the marker (label), which can be any alphanumeric string.
Then you can run select tests which contain or match the marker, as described in the next topic.
In addition, you can register markers in the [pytest] section of [Link], so they will be listed with
pytest --markers:
[pytest]
markers =
internet: test requires internet connection
slow: tests that take more time (omit with '-m "not slow")
pytest -m "mark"
pytest -m "not mark"
Example
pytests/test_mark.py
#!/usr/bin/env python
import pytest
@[Link] ①
def test_one():
assert 1
@[Link] ①
def test_two():
assert 1
@[Link] ②
def test_three():
assert 1
if __name__ == '__main__':
[Link]([__file__, '-m alpha']) ③
③ Only tests marked with alpha will run (equivalent to 'pytest -m alpha' on command line)
pytests/test_mark.py
pytests/test_mark.py .. [100%]
pytests/test_mark.py:8
/Users/jstrick/curr/courses/python/examples3/pytests/test_mark.py:8:
PytestUnknownMarkWarning: Unknown [Link] - is this a typo? You can register
custom marks to avoid this warning - for details, see
[Link]
@[Link] ①
pytests/test_mark.py:12
/Users/jstrick/curr/courses/python/examples3/pytests/test_mark.py:12:
PytestUnknownMarkWarning: Unknown [Link] - is this a typo? You can register
custom marks to avoid this warning - for details, see
[Link]
@[Link] ②
-- Docs: [Link]
================= 2 passed, 1 deselected, 3 warnings in 0.03s ==================
• Run by
◦ function
◦ class
◦ module
◦ name match
◦ group
To run all tests in the current and any descendent directories, use
Use -s to disable capturing, so anything written to STDOUT is displayed. Use -s for verbose output.
pytest
pytest -v
pytest -s
pytest -vs
Running by component
Use the node ID to select by component, such aas module, class, method, or function name:
file::class
file::class::test
file::::test
pytest test_president.py::test_dates
pytest test_president.py::test_dates::test_birth_date
• Decorate with
◦ @[Link]
◦ @[Link]
To skip tests conditionally (or unconditionally), use @[Link](). This is useful if some tests rely
on components that haven’t been developed yet, or for tests that are platform-specific.
To fail on purpose, use @[Link]). This reports the test as "XPASS" or "xfail", but does not
provide traceback. Tests marked with xfail will not fail the test suite. This is useful for testing not-yet-
implemented features, or for testing objects with known bugs that will be resolved later.
Example
pytests/test_skip.py
#!/usr/bin/env python
import sys
import pytest
def test_one(): ①
assert 1
@[Link] ④
def test_four():
assert 1
@[Link] ④
def test_five():
assert 0
if __name__ == '__main__':
[Link]([__file__, '-v'])
① Normal test
pytests/test_skip.py
Mocking data
Some objects have dependencies which can make unit testing difficult. These dependencies may be
expensive in terms of time or resources.
The solution is to use a mock object, which pretends to be the real object. A mock object behaves like
the original object, but is restricted and controlled in its behavior.
For instance, a class may have a dependency on a database query. A mock object may accept the query,
but always returns a hard-coded set of results.
A mock object can record the calls made to it, and assert that the calls were made with correct
parameters.
A mock object can be preloaded with a return value, or a function that provides dynamic (or random)
return values.
A stub is an object that returns minimal information, and is also useful in testing. However, a mock
object is more elaborate, with record/playback capability, assertions, and other features.
pymock objects
• Emulate resources
pytest can use [Link], from the standard library, or the pytest-mock plugin, which provides a
wrapper around [Link]
Once the pytest-mock module is installed, it provides a fixture named mocker, from which you can
create mock objects.
In either case, there are two primary ways of using mock. One is to provide a replacement class,
function, or data object that mimics the real thing.
The second is to monkey-patch a library, which temporarily (just during the test) replaces a component
with a mock version. The [Link]() function replaces a component with a mock object. Any calls
to the component are now recorded.
Example
pytests/test_mock_unittest.py
#!/usr/bin/env python
#
import pytest
from [Link] import Mock
ham = Mock() ①
@property
def value(self): ④
return self._value
def test_spam_calls_ham(): ⑤
_ = Spam(42) ⑥
ham.assert_called_once_with(42) ⑦
if __name__ == '__main__':
[Link]([__file__])
pytests/test_mock_unittest.py
pytests/test_mock_unittest.py . [100%]
Example
pytests/test_mock_pymock.py
#!/usr/bin/env python
import pytest ①
import re ②
class SpamSearch(): ③
def __init__(self, search_string, target_string):
self.search_string = search_string
self.target_string = target_string
def findit(self): ④
return [Link](self.search_string, self.target_string)
def test_spam_search_calls_re_search(mocker): ⑤
[Link]('[Link]') ⑥
s = SpamSearch('bug', 'lightning bug') ⑦
_ = [Link]() ⑧
[Link].assert_called_once_with('bug', 'lightning bug') ⑨
if __name__ == '__main__':
[Link]([__file__, '-s']) ⑩
⑤ Unit test
⑥ Patch [Link] (i.e., replace [Link] with a Mock object that records calls to it)
⑨ Check that method was called just once with the expected parameters
pytests/test_mock_pymock.py
pytests/test_mock_pymock.py . [100%]
Example
pytests/test_mock_play.py
#!/usr/bin/env python
import pytest
from [Link] import Mock
@[Link]
def small_list(): ①
return [1, 2, 3]
def test_m1_returns_correct_list(small_list):
m1 = Mock(return_value=small_list) ②
mock_result = m1('a', 'b') ③
assert mock_result == small_list ④
m2 = Mock() ⑤
[Link]('a', 'b') ⑥
[Link]('wombat') ⑥
[Link](1, 2, 3) ⑥
[Link].assert_called_with('a', 'b') ⑧
⑧ Assert that spam() was called with parameters 'a' and 'b'
pytests/test_mock_play.py
Pytest plugins
• Common plugins
◦ pytest-qt
◦ pytest-django
There are some plugins for pytest that that integrate various frameworks which would otherwise be
difficult to test directly.
The pytest-qt plugin provides a qtbot fixture that can attach widgets and invoke events. This makes it
simpler to test your custom widgets.
The pytest-django plugin allows you to run Django with pytest-style tests rather than the default
unittest style.
The Pytest builtin test runner will detect Unittest-based tests as well. This can be handy for
transitioning legacy code to Pytest.
Chapter 6 Exercises
Exercise 6-1 (test_president_pytest.py)
1
Using pytest, Create some unit tests for the President class you created earlier.
• All 45 presidential terms match the correct last name (use list of last names and parametrize)
1
If there was not an exercise where you created a President class, you can use [Link] in the top-
level folder of the student guide.
• Connect to a database
The DB API
To make database programming simpler, Python has the DB API. This is an API to standardize working
with databases. When a package is written to access a database, it is written to conform to the API, and
thus programmers do not have to learn a new set of methods and functions.
Informix informixdb
Ingres ingmod
MySQL pymysql
ODBC pyodbc
Oracle cx_oracle
PostgreSQL psycopg2
SQLite sqlite3
Sybase Sybase
This list is not comprehensive, and there may be additional interfaces to some of the
NOTE
listed DBMSs.
Connecting to a Server
To connect to a database server, import the package for the specific database. Use the package’s
connect() method to get a database object, specifying the host, initial database, username, and
password. If the username and password are not needed, use None.
Argument names for the connect() method may not be consistent across packages. Most connect()
methods use individual arguments, such as host, database, etc., but some use a single string
argument.
When finished with the connection, call the close() method on the connection object.
Many database modules support the context manager (with statement), and will automatically close
the database when the with block is exited. Check the documentation to see how this is implemented
for a specific database.
Example
import pymysql
import sqlite3
[Link] ( + dsn="DSN", + )
[Link]('DSN=testdsn;PWD=$3cr3t')
note: connect() has one (string) parameter, not multiple parameters
Creating a Cursor
◦ Standard cursor
▪ Returns tuples
◦ Other cursors
▪ Returns dictionaries
Once you have a connection object, you can call cursor() to create a cursor object. A cursor is an object
that can execute SQL code and fetch results. One connection may have one or more active cursors.
The default cursor for most packages returns each row as a tuple of values. There are different types of
cursors that can return data in different formats, or that control whether data is stored on the client or
the server.
See db_*.py for examples using DB2, Postgres, MySQL, and MS-SQL. Most of the sqlite3
NOTE examples in this chapter are also implemented for MySQL, Postgres, and DB2, plus a
few extras.
Example
import sqlite3
conn = [Link]("[Link]")
cursor = [Link]()
Once you have a cursor, you can use it to execute queries via the execute() method. The first argument
to execute() is a string containing one SQL statement.
For queries, __cursor__.execute() returns the number of rows in the result set.
Example
Fetching Data
• Syntax
◦ rec = [Link]()
◦ recs = [Link]()
◦ recs = [Link]()
fetchone() returns the next available row from the query results.
fetchmany(n) returns up to n rows. This is useful when the query returns a large number of rows.
Example
db_sqlite_basics.py
#!/usr/bin/env python
import sqlite3
cursor = [Link]() ②
db_sqlite_basics.py
Non-query statements
• Updates database
As with queries, the first argument is a string containing one SQL statement. The optional second
argument is an iterable of values to fill in placeholders in a parameterized statement.
Example
db_sqlite_add_row.py
#!/usr/bin/env python
from datetime import date
import sqlite3
sql_insert = """
insert into presidents
(termnum, lastname, firstname, birthdate, deathdate, birthplace, birthstate,
termstart, termend, party)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
cursor = [Link]()
try:
[Link](sql_insert, new_row_data)
except ([Link], [Link], [Link]) as err:
print(err)
[Link]()
else:
[Link]()
[Link]()
Example
db_sqlite_delete_row.py
#!/usr/bin/env python
from datetime import date
import sqlite3
sql_delete = """
delete from presidents
where TERMNUM = 47
"""
cursor = [Link]()
try:
[Link](sql_delete)
except ([Link], [Link], [Link]) as err:
print(err)
[Link]()
else:
[Link]()
[Link]()
SQL Injection
One kind of vulnerability in SQL code is called SQL injection. This occurs when an attacker embeds SQL
commands in input data. This can happen when naively using string formatting to build SQL
statements.
Since the programmer is generating the SQL code as a string, there is no way to check for malicious
SQL code. It is best practice to use parameterized statements.
Example
db_sql_injection.py
#!/usr/bin/env python
#
good_input = 'Google'
malicious_input = "'; drop table customers; -- " ①
naive_format = "select * from customers where company_name = '{}' and company_id != 0"
good_query = naive_format.format(good_input) ②
malicious_query = naive_format.format(malicious_input) ②
print("Good query:")
print(good_query) ③
print()
print("Bad query:")
print(malicious_query) ④
② string formatting naively adds the user input to a field, expecting only a customer name
db_sql_injection.py
Good query:
select * from customers where company_name = 'Google' and company_id != 0
Bad query:
select * from customers where company_name = ''; drop table customers; -- ' and
company_id != 0
Parameterized Statements
◦ Placeholders vary by DB
For efficiency, you can iterate over of sequence of input datasets when performing a non-query SQL
statement. The execute() method takes a query, plus an iterable of values to fill in the placeholders. The
database manager will only parse the query once, then reuse it for subsequent calls to execute().
Different database modules use different placeholders. To see what kind of placeholder a module uses,
check [Link]. Types include pyformat, meaning %s, and qmark, meaning ?.
The executemany() method takes a query, plus an iterable of iterables. It will call execute() once for
each nested iterable.
pymysql %s
cx_oracle :param_name
pyodbc ?
Psychopg %s or %(param_name)s
sqlite3 ? or :param_name
TIP with the exception of pymssql the same placeholder is used for all column types.
Example
db_sqlite_parameterized.py
#!/usr/bin/env python
import sqlite3
party_query = '''
select firstname, lastname
from presidents
where party = ?
''' ①
① ? is SQLite3 placeholder for SQL statement parameter; different DBMSs use different placeholders
② second argument to execute() is iterable of values to fill in placeholders from left to right
db_sqlite_parameterized.py
Federalist
[('John', 'Adams')]
Whig
[('William Henry', 'Harrison'), ('John', 'Tyler'), ('Zachary', 'Taylor'), ('Millard',
'Fillmore')]
Example
db_sqlite_bulk_insert.py
#!/usr/bin/env python
import sqlite3
import os
import csv
DATA_FILE = '../DATA/fruit_data.csv'
DB_NAME = '[Link]'
DB_TABLE = 'fruits'
SQL_CREATE_TABLE = f"""
create table {DB_TABLE} (
id integer primary key,
name varchar(30),
unit varchar(30),
unitprice decimal(6, 2)
)
""" ②
SQL_INSERT_ROW = f'''
insert into {DB_TABLE} (name, unit, unitprice) values (?, ?, ?)
''' ③
SQL_SELECT_ALL = f"""
select name, unit, unitprice from {DB_TABLE}
"""
def main():
"""
Program entry point.
:return: None
"""
conn, cursor = get_connection()
create_database(cursor)
populate_database(conn, cursor)
read_database(cursor)
[Link]()
[Link]()
def get_connection():
"""
conn = [Link](DB_NAME) ⑤
cursor = [Link]()
return conn, cursor
def create_database(cursor):
"""
Create the fruit table
try:
[Link](SQL_INSERT_ROW, fruit_data) ⑦
except [Link] as err:
print(err)
[Link]()
else:
[Link]() ⑧
def read_database(cursor):
[Link](SQL_SELECT_ALL)
for name, unit, unitprice in [Link]():
print('{:12s} {:5.2f}/{}'.format(name, unitprice, unit))
if __name__ == '__main__':
main()
⑦ iterate over list of pairs and add each pair to the database
db_sqlite_bulk_insert.py
pomegranate 0.99/each
cherry 2.25/pound
apricot 3.49/pound
date 1.20/pound
apple 0.55/pound
lemon 0.69/each
kiwi 0.88/each
orange 0.49/each
lime 0.49/each
watermelon 4.50/each
guava 2.88/pound
papaya 1.79/pound
fig 2.29/pound
pear 1.10/pound
banana 0.65/pound
Dictionary Cursors
The standard cursor provided by the DB API returns a tuple for each row. Most DB packages provide
other kinds of cursors, including user-defined versions.
A very common cursor is a dictionary cursor, which returns a dictionary for each row, where the keys
are the column names. Each package that provides a dictionary cursor has its own way of providing
the dictionary cursor, although they all work the same way.
Example
db_sqlite_dict_cursor.py
#!/usr/bin/env python
import sqlite3
s3conn = [Link]("../DATA/[Link]")
# uncomment to make _all_ cursors dictionary cursors
# conn.row_factory = [Link]
NAME_QUERY = '''
select firstname, lastname
from presidents
where termnum < 5
'''
cur = [Link]()
dict_cursor = [Link]() ①
print('-' * 50)
db_sqlite_dict_cursor.py
('George', 'Washington')
('John', 'Adams')
('Thomas', 'Jefferson')
('James', 'Madison')
--------------------------------------------------
George Washington
John Adams
Thomas Jefferson
James Madison
--------------------------------------------------
Metadata
• Fields
◦ name
◦ type_code
◦ display_size
◦ internal_size
◦ precision
◦ scale
◦ null_ok
Once a query has been executed, the cursor’s description attribute is a tuple with metadata about the
columns in the query. It contains one tuple for each column in the query, containing 7 values
describing the column.
For instance, to get the names of the columns, you could say names = [d[0] for d in
[Link]]
The names are based on the query (with possible aliases), and not necessarily on the names in the
table.
▪ Dictionary
▪ Named tuple
▪ Dataclass
Many database modules have a dictionary cursor built in. For those that don’t the iterrows_asdict()
function can be used with a cursor from any DB API-compliant package.
The example uses the metadata from the cursor to get the column names, and forms a dictionary by
zipping the column names with the column values. db_iterrows also provides iterrows_asnamedtuple(),
which returns each row as a named tuple.
The functions in db_iterrows return generator objects. When you loop over the generator object, each
element is a dictionary or a named tuple, depending on which function you called.
Example
db_iterrows.py
#!/usr/bin/env python
"""
Generic functions that can be used with any DB API compliant
package.
def get_column_names(cursor):
return [desc[0] for desc in [Link]]
def iterrows_asdict(cursor):
'''Generate rows as dictionaries'''
column_names = get_column_names(cursor)
for cursor_row in [Link]():
row_dict = dict(zip(column_names, cursor_row))
yield row_dict
def iterrows_asnamedtuple(cursor):
'''Generate rows as named tuples'''
column_names = get_column_names(cursor)
Row = namedtuple('Row', column_names)
for row in [Link]():
yield Row(*row)
def iterrows_asdataclass(cursor):
'''Generate rows as dataclass instances'''
column_names = get_column_names(cursor)
Row = make_dataclass('row_tuple', column_names)
db_iterrows.py
Transactions
• rollback() to discard
Sometimes a database task involves more than one change to your database (i.e., more than one SQL
statement). You don’t want the first SQL statement to succeed and the second to fail; this would leave
your database in a corrupt state.
To be certain of data integrity, use transactions. This lets you make multiple changes to your database
and only commit the changes if all the SQL statements were successful.
For all packages using the Python DB API, a transaction is started when you connect. At any point, you
can call __CONNECTION__.commit() to save the changes, or __CONNECTION__.rollback() to discard the
changes. If you don’t call commit() after modify a table, the data will not be saved.
You can also turn on autocommit, which calls commit() after every statement. See the table below for
how autocommit is implemented in various DB packages.
Package Method/Attribute
ibm_db_api __conn__.set_autocommit(True)
NOTE pymysql only supports transaction processing when using the InnoDB engine
Example
try:
for info in list_of_tuples:
[Link](query,info)
except SQLError:
[Link]()
else:
[Link]()
Object-relational Mappers
• No SQL required
◦ SQLAlchemy
An Object-relational mapper is a module or framework that creates a level of abstraction above the
actual database tables and SQL queries. As the name implies, a Python class (object) is mapped to the
actual table.
The two most popular Python ORMs are SQLAlchemy which is a standalone ORM, and Django ORM.
Django is a comprehensive Web development framework, which provides an ORM as a subpackage.
SQLAlchemy is the most fully developed package, and is the ORM used by Flask and some other Web
development frameworks.
Instead of querying the database, you call a search method on an object representing a table. To add a
row to the table, you create a new instance of the table class, populate it, and call a method like save().
You can create a large, complex database system, complete with foreign keys, composite indices, and
all the other attributes near and dear to a DBA, without writing the first line of SQL.
One way is to design the database with the ORM. To do this, you create a class for each table in the
database, specifying the columns with predefined classes from the ORM. Then you run an ORM
command which executes the queries needed to build the database. If you need to make changes, you
update the class definitions, and run an ORM command to synchronize the actual DBMS to your
classes.
The second way is to map tables to an existing database. You create the classes to match the schemas
that have already been defined in the database. Both SQLAlchemy and the Django ORM have tools to
automate this process.
NoSQL
• Non-relational database
• Document-oriented
• Examples
◦ MongoDB
◦ Cassandra
◦ Redis
A current trend in data storage are called "NoSQL" or non-relational databases. These databases consist
of documents, which are indexed, and may contain nested data.
While relational databases are great for tabular data, they are not as good a fit for nested data. Geo-
spatial, engineering diagrams, and molecular modeling can have very complex structures. It is possible
to shoehorn such data into a relational database, but a NoSQL database might work much better.
Another advantage of NoSQL is that it can adapt to changing data structures, without having to rebuild
tables if columns are added, deleted, or modified.
Some of the most common NoSQL database systems are MongoDB, Cassandra and Redis.
Example
mongodb_example.py
#!/usr/bin/env python
import re
from pymongo import MongoClient, errors
FIELD_NAMES = (
'termnumber lastname firstname '
'birthdate '
'deathdate birthplace birthstate '
'termstartdate '
'termenddate '
'party'
).split() ①
mc = MongoClient() ②
try:
mc.drop_database("presidents") ③
except [Link] as err:
print(err)
db = mc["presidents"] ④
coll = [Link] ⑤
print(db.list_collection_names()) ⑧
print()
print('-' * 50)
print('-' * 50)
print('-' * 50)
print("removing Millard Fillmore")
result = coll.delete_one({'lastname': 'Fillmore'}) ⑭
print(result)
result = coll.delete_one({'lastname': 'Roosevelt'}) ⑭
print(result)
print('-' * 50)
result = coll.count_documents({}) ⑮
print(result)
animals = [Link]
print(animals, '\n')
⑭ delete record
mongodb_example.py
wombat
ocelot
honey badger
Chapter 7 Exercises
Exercise 7-1 (president_sqlite.py)
For this exercise, you can use the SQLite3 database provided, or use your own DBMS. The [Link]
script is generic and should work with any DBMS to create and populate the presidents table. The
SQLite3 database is named [Link] and is located in the DATA folder of the student files.
Refactor the [Link] module to get its data from this table, rather than from a file. Re-run your
previous scripts that used [Link]; now they should get their data from the database, rather than
from the flat file.
If you created a [Link] module as part of an earlier lab, use that. Otherwise, use
NOTE
the supplied [Link] module in the top folder of the student files.
Add the next president to the presidents database. Just make up the data — let’s keep this non-political.
Don’t use any real-life people.
Chapter 8: Multiprogramming
Objectives
• Understand multiprogramming
Multiprogramming
• Parallel processing
◦ threading
◦ multiple processes
◦ asynchronous communication
Computer programs spend a lot of their time doing nothing. This occurs when the CPU is waiting for
the relatively slow disk subsystem, network stack, or other hardware to fetch data.
Some applications can achieve more throughput by taking advantage of this slack time by seemingly
doing more than one thing at a time. With a single-core computer, this doesn’t really happen; with a
multicore computer, an application really can be executing different instructions at the same time.
This is called multiprogramming.
The three main ways to implement multiprogramming are threading, multiprocessing, and
asynchronous communication:
Threading subdivides a single process into multiple subprocesses, or threads, each of which can be
performing a different task. Threading in Python is good for IO-bound applications, but does not
increase the efficiency of compute-bound applications.
Multiprocessing forks (spawns) new processes to do multiple tasks. Multiprocessing is good for both
CPU-bound and IO-bound applications.
Asynchronous communication uses an event loop to poll multiple I/O channels rather than waiting for
one to finish. Asynch communication is good for IO-bound applications.
Modern operating systems (OSs) use time-sharing to manage multiple programs which appear to the
user to be running simultaneously. Assuming a standard machine with only one CPU, that simultaneity
is only an illusion, since only one program can run at a time, but it is a very useful illusion. Each
program that is running counts as a process. The OS maintains a process table, listing all current
processes. Each process will be shown as currently being in either Run state or Sleep state.
A thread is like a process. A thread might even be a process, depending on the implementation. In fact,
threads are sometimes called “lightweight” processes, because threads occupy much less memory, and
take less time to create, than do processes.
A process can create any number of threads. This is similar to a process calling the fork() function. The
process itself is a thread, and could be considered the "main" thread.
Python “piggybacks” on top of the OS’s underlying threads system. A Python thread is a real OS thread.
If a Python program has three threads, for instance, there will be three entries in the OS’s thread list.
However, Python imposes further structure on top of the OS threads. Most importantly, there is a
global interpreter lock, the famous (or infamous) GIL. It is set up to ensure that (a) only one thread
runs at a time, and (b) that the ending of a thread’s turn is controlled by the Python interpreter rather
than the external event of the hardware timer interrupt.
The fact that the GIL allows only one thread to execute Python bytecode at a time simplifies the Python
implementation by making the object model (including critical built-in types such as dict) implicitly
safe against concurrent access. Locking the entire interpreter makes it easier for the interpreter to be
multi-threaded, at the expense of much of the parallelism afforded by multi-processor machines. The
takeaway is that Python does not currently take advantage of multi-processor hardware.
For a thorough discussion of the GIL and its implications, see [Link]
[Link].
◦ Subclass Thread
The threading module provides basic threading services for Python programs. The usual approach is to
subclass [Link] and provide a run() method that does the thread’s work.
For many threading tasks, all you need is a run() method and maybe some arguments to pass to it.
For simple tasks, you can just create an instance of Thread, passing in positional or keyword
arguments.
Example
thr_noclass.py
#!/usr/bin/env python
import threading
import random
import time
def doit(num): ①
[Link]([Link](1, 3))
print("Hello from thread {}".format(num))
for i in range(10):
t = [Link](target=doit, args=(i,)) ②
[Link]() ③
print("Done.") ④
② create thread
③ launch thread
thr_noclass.py
Done.
Hello from thread 0
Hello from thread 6
Hello from thread 2
Hello from thread 3
Hello from thread 7
Hello from thread 8
Hello from thread 9
Hello from thread 1
Hello from thread 4
Hello from thread 5
• Subclass Thread
A thread class is a class that starts a thread, and performs some task. Such a class can be repeatedly
instantiated, with different parameters, and then started as needed.
The class can be as elaborate as the business logic requires. There are only two rules: the class must
call the base class’s __init__(), and it must implement a run() method. Other than that, the run() method
can do pretty much anything it wants to.
The best way to invoke the base class __init__() is to use super().
The run() method is invoked when you call the start() method on the thread object. The start() method
does not take any parameters, and thus run() has no parameters as well.
Any per-thread arguments can be passed into the constructor when the thread object is created.
Example
thr_simple.py
#!/usr/bin/env python
class SimpleThread(Thread):
def __init__(self, num):
super().__init__() ①
self._threadnum = num
def run(self): ②
[Link]([Link](1, 3))
print("Hello from thread {}".format(self._threadnum))
for i in range(10):
t = SimpleThread(i) ③
[Link]() ④
print("Done.")
thr_simple.py
Done.
Hello from thread 1
Hello from thread 3
Hello from thread 4
Hello from thread 9
Hello from thread 0
Hello from thread 2
Hello from thread 7
Hello from thread 5
Hello from thread 6
Hello from thread 8
Variable sharing
A major difference between ordinary processes and threads how variables are shared.
Each thread has its own local variables, just as is the case for a process. However, variables that existed
in the program before threads are spawned are shared by all threads. They are used for
communication between the threads.
Example
thr_locking.py
#!/usr/bin/env python
import threading ①
import random
import time
WORDS = 'apple banana mango peach papaya cherry lemon watermelon fig elderberry'.split()
MAX_SLEEP_TIME = 3
WORD_LIST = [] ②
WORD_LIST_LOCK = [Link]() ③
STDOUT_LOCK = [Link]() ③
class SimpleThread([Link]):
def __init__(self, num, word): ④
super().__init__() ⑤
self._word = word
self._num = num
def run(self): ⑥
[Link]([Link](1, MAX_SLEEP_TIME))
with STDOUT_LOCK: ⑦
print("Hello from thread {} ({})".format(self._num, self._word))
with WORD_LIST_LOCK: ⑦
WORD_LIST.append(self._word.upper())
all_threads = [] ⑧
for i, random_word in enumerate(WORDS, 1):
t = SimpleThread(i, random_word) ⑨
all_threads.append(t) ⑩
[Link]() ⑪
for t in all_threads:
[Link]() ⑫
print(WORD_LIST)
③ generic locks
④ thread constructor
⑨ create thread
⑪ start thread
thr_locking.py
Using queues
• Sequence is FIFO
Threaded applications often have some sort of work queue data structure. When a thread becomes
free, it will pick up work to do from the queue. When a thread creates a task, it will add that task to the
queue.
The queue must be guarded with locks. Python provides the Queue module to take care of all the lock
creation, locking and unlocking, and so on, so that you don’t have to bother with it.
Example
thr_queue.py
#!/usr/bin/env python
import random
import queue
from threading import Thread, Lock as tlock
import time
NUM_ITEMS = 25000
POOL_SIZE = 100
q = [Link](0) ①
shared_list = []
shlist_lock = tlock() ②
stdout_lock = tlock() ②
class RandomWord(): ③
def __init__(self):
with open('../DATA/[Link]') as words_in:
self._words = [[Link]('\n\r') for word in words_in.readlines()]
self._num_words = len(self._words)
def __call__(self):
return self._words[[Link](0, self._num_words)]
class Worker(Thread): ④
def run(self): ⑥
while True:
try:
s1 = [Link](block=False) ⑦
s2 = [Link]() + '-' + [Link]()
with shlist_lock: ⑧
shared_list.append(s2)
except [Link]: ⑨
break
⑩
random_word = RandomWord()
for i in range(NUM_ITEMS):
w = random_word()
[Link](w)
start_time = [Link]()
⑪
pool = []
for i in range(POOL_SIZE):
worker_name = "Worker {:c}".format(i + 65)
w = Worker(worker_name) ⑫
[Link]() ⑬
[Link](w)
for t in pool:
[Link]() ⑭
end_time = [Link]()
print(shared_list[:20])
print(start_time)
print(end_time)
② create locks
④ worker thread
⑤ thread constructor
thr_queue.py
• Use [Link]
Debugging is always tough with parallel programs, including threads programs. It’s especially difficult
with pre-emptive threads; those accustomed to debugging non-threads programs find it rather jarring
to see sudden changes of context while single-stepping through code. Tracking down the cause of
deadlocks can be very hard. (Often just getting a threads program to end properly is a challenge.)
Another problem which sometimes occurs is that if you issue a “next” command in your debugging
tool, you may end up inside the internal threads code. In such cases, use a “continue” command or
something like that to extricate yourself.
Unfortunately, threads debugging is even more difficult in Python, at least with the basic PDB
debugger.
[Link] [Link]
This is because the child threads will not inherit the PDB process from the main thread. You can still
run PDB in the latter, but will not be able to set breakpoints in threads.
What you can do, though, is invoke PDB from within the function which is run by the thread, by calling
[Link] trace() at one or more points within the code:
import pdb
pdb.set_trace()
import pdb
while True:
pdb.set_trace() # app will stop here and enter debugger
k = [Link](1)
if k == ’’:
break
You then run the program as usual, NOT through PDB, but then the program suddenly moves into
debugging mode on its own. At that point, you can then step through the code using the n or s
commands, query the values of variables, etc.
PDB’s c (“continue”) command still works. Can you still use the b command to set additional
breakpoints? Yes, but it might be only on a one-time basis, depending on the context.
The multiprocessing module can be used as a replacement for threading. It uses processes rather than
threads to spread out the work to be done. While the entire module doesn’t use the same API as
threading, the [Link] object is a drop-in replacement for a [Link] object.
Both use run() as the overridable method that does the work, and both use start() to launch. The syntax
is the same to create a process without using a class:
def myfunc(filename):
pass
p = Process(target=myfunc, args=('/tmp/[Link]', ))
This solves the GIL issue, but the trade-off is that it’s slightly more complicated for tasks (processes) to
communicate. However, the module does the heavy lifting of creating pipes to share data.
The Manager class provided by multiprocessing allows you to create shared variables, as well as locks
for them, which work across processes.
On windows, processes must be started in the "if __name__ == __main__" block, or they
NOTE
will not work.
Example
multi_processing.py
#!/usr/bin/env python
import sys
import random
from multiprocessing import Manager, Lock, Process, Queue, freeze_support
from queue import Empty
import time
NUM_ITEMS = 25000 ①
POOL_SIZE = 100
class RandomWord(): ②
def __init__(self):
with open('../DATA/[Link]') as words_in:
self._words = [[Link]('\n\r') for word in words_in]
self._num_words = len(self._words)
def __call__(self): ③
return self._words[[Link](0, self._num_words)]
class Worker(Process): ④
def run(self): ⑥
while True:
try:
word = [Link](block=False) ⑦
word = [Link]() ⑧
with [Link]:
[Link](word) ⑨
except Empty: ⑩
break
if __name__ == '__main__':
if [Link] == 'win32':
freeze_support()
word_queue = Queue() ⑪
manager = Manager() ⑫
shared_result = [Link]() ⑬
result_lock = Lock() ⑭
random_word = RandomWord() ⑮
for i in range(NUM_ITEMS):
w = random_word()
word_queue.put(w) ⑯
start_time = [Link]()
pool = [] ⑰
for i in range(POOL_SIZE): ⑱
worker_name = "Worker {:03d}".format(i)
w = Worker(worker_name, word_queue, result_lock, shared_result) ⑲
#
[Link]() ⑳
[Link](w)
for t in pool:
[Link]()
end_time = [Link]()
print((shared_result[-50:]))
print(len(shared_result))
print(start_time)
print(end_time)
⑧ modify data
⑭ create locks
⑳ actually start the process — note: in Windows, should only call [Link]() from main(), and may not
multi_processing.py
Using pools
• Provided by multiprocessing
For many multiprocessing tasks, you want to process a list (or other iterable) of data and do something
with the results. This is easily accomplished with the Pool object provided by the multiprocessing
module.
This object creates a pool of n processes. Call the .map() method with a function that will do the work,
and an iterable of data. map() will return a list the same size as the list that was passed in, containing
the results returned by the function for each item in the original list.
For a thread pool, import Pool from [Link]. It works exactly the same, but creates
threads.
Example
proc_pool.py
#!/usr/bin/env python
import random
from multiprocessing import Pool
POOL_SIZE = 30 ①
[Link](WORDS) ③
def my_task(word): ④
return [Link]()
if __name__ == '__main__':
ppool = Pool(POOL_SIZE) ⑤
print(WORD_LIST[:20]) ⑦
print("Processed {} words.".format(len(WORD_LIST)))
① number of processes
④ actual task
⑥ pass wordlist to pool and get results; map assigns values from input list to processes as needed
proc_pool.py
Example
thr_pool.py
#!/usr/bin/env python
import random
from [Link] import Pool ①
POOL_SIZE = 30 ②
[Link](WORDS) ④
def my_task(word): ⑤
return [Link]()
tpool = Pool(POOL_SIZE) ⑥
print(WORD_LIST[:20]) ⑧
print("Processed {} words.".format(len(WORD_LIST)))
thr_pool.py
Example
thr_pool_mw.py
#!/usr/bin/env python
from [Link] import Pool ①
from pprint import pprint
import requests
POOL_SIZE = 4
BASE_URL = '[Link] ②
API_KEY = 'b619b55d-faa3-442b-a119-dd906adc79c8' ③
search_terms = [ ④
'wombat',
'frog', 'muntin', 'automobile', 'green', 'connect',
'vial', 'battery', 'computer', 'sing', 'park',
'ladle', 'ram', 'dog', 'scalpel'
]
def fetch_data(term): ⑤
try:
response = [Link](
BASE_URL + term,
params={'key': API_KEY},
) ⑥
except [Link] as err:
print(err)
return []
else:
data = [Link]() ⑦
parts_of_speech = []
for entry in data: ⑧
if isinstance(entry, dict):
meta = [Link]("meta")
if meta:
part_of_speech = [Link]("fl")
if part_of_speech:
parts_of_speech.append(part_of_speech)
return sorted(set(parts_of_speech)) ⑨
p = Pool(POOL_SIZE) ⑩
④ terms to search for; each thread will search some of these terms
⑤ function invoked by each thread for each item in list passed to map()
Alternatives to multiprogramming
• asyncio
• Twisted
Threading and forking are not the only ways to have your program do more than one thing at a time.
Another approach is asynchronous programming. This technique putting events (typically I/O events)
in a list, or queue, and starting an event loop that processes the events one at a time. If the granularity
of the event loop is small, this can be as efficient as multiprogramming.
Asynchronous programming is only useful for improving I/O throughput, such as networking clients
and servers, or scouring a file system. Like threading (in Python), it will not help with raw computation
speed.
The asyncio module in the standard library provides the means to write asynchronous clients and
servers.
The Twisted framework is a large and well-supported third-party module that provides support for
many kinds of asynchronous communication. It has prebuilt objects for servers, clients, and protocols,
as well as tools for authentication, translation, and many others. Find Twisted at
[Link]/trac.
Chapter 8 Exercises
For each exercise, ask the questions: Should this be multi-threaded or multi-processed? Distributed or
local?
Using a thread pool ([Link]), calculate the age at inauguration of the presidents. To
do this, read the [Link] file into an array of tuples, and then pass that array to the mapping
function of the thread pool. The result of the map function will be the array of ages. You will need to
convert the date fields into actual dates, and then subtract them.
Write a program that takes in a directory name on the command line, then traverses all the files in that
directory tree and prints out a count of:
Write a website-spider. Given a domain name, it should crawl the page at that domain, and any other
URLs from that page with the same domain name. Limit the number of parallel requests to the web
server to no more than 4.
Write a function that will take in two large arrays of integers and a target. It should return an array of
tuple pairs, each pair being one number from each input array, that sum to the target value.
The standard library provides the urllib package. It and its friends are powerful libraries, but their
interfaces are complex for non-trivial tasks. There is a lot of code to write if you want to provide
authentication, proxies, headers, or data, among other things.
The requests module is a much easier to use HTTP client module. It is included with the Anaconda
distribution, or is readily available from PyPI.
requests implements GET, POST, PUT, and other HTTP verbs, and takes care of all the protocol
housekeeping needed to send data on the URL, to send a username/password, and to retrieve data in
various formats.
To use requests, import the module and then call [Link], where VERB is "get", "post", "put",
"patch", "delete", or "head". The first argument to any of these methods is the URL, followed by any of
the named parameters for fine-tuning the request.
These methods return an HTTPResponse object, which contains the headers and data returned from
the HTTP server. If the URL refers to a web page, then the text attribute contains the text of the page as
a Python string.
In all cases, the content attribute contains the raw content from the server as a bytes string. If the
returned data is a JSON string, the json() method converts the JSON data into a Python nested list or
dictionary.
The status_code attribute contains the HTTP status code, normally 200 for a successful request.
For GET requests, URL parameters can be specified as a dictionary, using the params parameter.
For POST, PUT, or PATCH requests, the data to be uploaed can be specified as a dictionary using the
data parameter.
Example
read_html_requests.py
#!/usr/bin/env python
import requests
response = [Link]("[Link] ①
print([Link][:200]) ③
print('...')
print([Link][-200:]) ④
③ The text is returned as a bytes object, so it needs to be decoded to a string; print the first 200 bytes
Example
read_pdf_requests.py
#!/usr/bin/env python
import sys
import os
import requests
url =
'[Link]
[Link]' ①
saved_pdf_file = 'nasa_iss.pdf' ②
response = [Link](url) ③
if response.status_code == [Link]: ④
if [Link]('content-type') == 'application/pdf':
with open(saved_pdf_file, 'wb') as pdf_in: ⑤
pdf_in.write([Link]) ⑥
if [Link] == 'win32': ⑦
cmd = saved_pdf_file
elif [Link] == 'darwin':
cmd = 'open ' + saved_pdf_file
else:
cmd = 'acroread ' + saved_pdf_file
[Link](cmd) ⑧
① target URL
⑥ write data to a local file in binary mode; [Link] is data from URL
⑦ select platform and choose the app to open the PDF file
Example
web_content_consumer_requests.py
import sys
import requests
BASE_URL = '[Link] ①
API_KEY = 'b619b55d-faa3-442b-a119-dd906adc79c8' ②
def main(args):
if len(args) < 1:
print("Please specify a search term")
[Link](1)
response = [Link](
BASE_URL + args[0],
params={'key': API_KEY},
# ssl, proxy, cookies, headers, etc.
) ③
else:
print("Sorry, HTTP response", response.status_code)
if __name__ == '__main__':
main([Link][1:])
② credentials
web_content_consumer_requests.py wombat
WOMBAT (noun)
any of several stocky burrowing Australian marsupials (genera Vombatus and Lasiorhinus of
the family Vombatidae) resembling small bears
timeout float or tuple timeout in seconds or (connect timeout, read timeout) tuple
NOTE These can be used with any of the HTTP request types, as appropriate.
Attribute Definition
cookies A CookieJar object with the cookies sent back from the server
elapsed A timedelta object with the time elapsed from sending the request to
the arrival of the response
is_permanent_redirect True if the response is the permanent redirected url, otherwise False
json() A JSON object of the result (if the result was written in JSON format, if
not it raises an error)
status_code A number that indicates the status (200 is OK, 404 is Not Found)
• Read response
The standard library module [Link] includes urlopen() for reading data from web pages.
urlopen() returns a file-like object. You can iterate over lines of HTML, or read all of the contents with
read().
The URL is opened in binary mode ; you can download any kind of file which a URL represents – PDF,
MP3, JPG, and so forth – by using read().
When downloading HTML or other text, a bytes object is returned; use decode() to
NOTE
convert it to a string.
In general, if you can install requests and use it, that is the preferred approach.
Example
read_html_urllib.py
#!/usr/bin/env python
import [Link]
u = [Link]("[Link]
print([Link]()) ①
print()
print([Link](500).decode()) ②
read_html_urllib.py
Connection: close
Content-Length: 50697
Server: nginx
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Via: 1.1 vegur, 1.1 varnish, 1.1 varnish
Accept-Ranges: bytes
Date: Fri, 12 Nov 2021 18:45:07 GMT
Age: 174
X-Served-By: cache-bwi5144-BWI, cache-pdk17882-PDK
X-Cache: HIT, HIT
X-Cache-Hits: 3, 1
X-Timer: S1636742707.472437,VS0,VE7
Vary: Cookie
Strict-Transport-Security: max-age=63072000; includeSubDomains
<!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--><html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
Example
read_pdf_urllib.py
#!/usr/bin/env python
import sys
import os
from [Link] import urlopen
from [Link] import HTTPError
url =
'[Link]
[Link]' ①
saved_pdf_file = 'nasa_iss.pdf' ②
try:
URL = urlopen(url) ③
except HTTPError as e: ④
print("Unable to open URL:", e)
[Link](1)
pdf_contents = [Link]() ⑤
[Link]()
if [Link] == 'win32': ⑦
cmd = saved_pdf_file
elif [Link] == 'darwin':
cmd = 'open ' + saved_pdf_file
else:
cmd = 'acroread ' + saved_pdf_file
[Link](cmd) ⑧
① target URL
⑦ select platform and choose the app to open the PDF file
• Use [Link]
To consume Web services, use the [Link] module from the standard library. Create a
[Link] object, and specify the desired data type for the service to return.
If needed, add a headers parameter to the request. Its value should be a dictionary of HTTP header
names and values.
For URL encoding the query, use [Link](). It takes either a dictionary or an iterable of
key/value pairs, and returns a single string in the format "K1=V1&K2=V2&…" suitable for appending to
a URL.
Pass the Request object to urlopen(), and it will return a file-like object which you can read by calling
its read() method.
The data will be a bytes object, so to use it as a string, call decode() on the data. It can then be parsed as
appropriate, depending on the content type.
the example program on the next page queries the Merriam-Webster dictionary API. It
NOTE
requires a word on the command line, which will be looked up in the online dictionary.
Example
web_content_consumer_urllib.py
#!/usr/bin/env python
"""
Fetch a word definition from Merriam-Webster's API
"""
import sys
from [Link] import Request, urlopen
import json
# from pprint import pprint
DATA_TYPE = 'application/json'
API_KEY = 'b619b55d-faa3-442b-a119-dd906adc79c8'
URL_TEMPLATE =
'[Link] ①
def main(args):
if len(args) < 1:
print("Please specify a word to look up")
[Link](1)
do_query(url)
def do_query(url):
print("URL:", url)
request = Request(url)
response = urlopen(request) ③
raw_json_string = [Link]().decode() ④
data = [Link](raw_json_string) ⑤
# print("RAW DATA:")
# pprint(data)
for entry in data: ⑥
if isinstance(entry, dict):
meta = [Link]("meta") ⑦
if meta:
part_of_speech = '({})'.format([Link]('fl'))
word_id = [Link]("id")
print("{} {}".format(word_id.upper(), part_of_speech))
if "shortdef" in entry:
print('\n'.join(entry['shortdef']))
print()
else:
print(entry)
if __name__ == '__main__':
main([Link][1:])
④ read content from web site and decode() from bytes to str
web_content_consumer_urllib.py dewars
URL: [Link]
faa3-442b-a119-dd906adc79c8
WOMBAT (noun)
any of several stocky burrowing Australian marsupials (genera Vombatus and Lasiorhinus of
the family Vombatidae) resembling small bears
sending e-mail
You can send e-mail messages from Python using the smtplib module. All you really need is one
smtplib object, and one method – sendmail().
Create the smtplib object, then call the sendmail() method with the sender, recipient(s), and the
message body (including any headers).
The recipients list should be a list or tuple, or could be a plain string containing a single recipient.
Example
email_simple.py
#!/usr/bin/env python
from getpass import getpass ①
import smtplib ②
from [Link] import EmailMessage ③
from datetime import datetime
TIMESTAMP = [Link]().ctime() ④
SENDER = 'jstrick@[Link]'
RECIPIENTS = ['jstrickler@[Link]']
MESSAGE_SUBJECT = 'Python SMTP example'
MESSAGE_BODY = """
Hello at {}.
SMTP_USER = 'pythonclass'
SMTP_PASSWORD = getpass("Enter SMTP server password:") ⑤
msg = EmailMessage() ⑧
msg.set_content(MESSAGE_BODY) ⑨
msg['Subject'] = MESSAGE_SUBJECT ⑩
msg['from'] = SENDER ⑪
msg['to'] = RECIPIENTS ⑫
try:
smtpserver.send_message(msg) ⑬
except [Link] as err:
print("Unable to send mail:", err)
finally:
[Link]() ⑭
Email attachments
To send attachments, you need to create a MIME multipart message, then create MIME objects for each
of the attachments, and attach them to the main message. This is done with various classes provided by
the [Link] module.
These modules include multipart for the main message, text for text attachments, image for image
attachments, audio for audio files, and application for miscellaneous binary data.
One the attachments are created and attached, the message must be serialized with the as_string()
method. The actual transport uses smptlib, just like simple email messages described earlier.
Example
email_attach.py
#!/usr/bin/env python
import smtplib
from datetime import datetime
from imghdr import what ①
from [Link] import EmailMessage ②
from getpass import getpass ③
SMTP_SERVER = "[Link]" ④
SMTP_PORT = 2525
SMTP_USER = 'pythonclass'
SENDER = 'jstrick@[Link]'
RECIPIENTS = ['jstrickler@[Link]']
def main():
smtp_server = create_smtp_server()
now = [Link]()
msg = create_message(
SENDER,
RECIPIENTS,
'Here is your attachment',
'Testing email attachments from python class at {}\n\n'.format(now),
)
add_text_attachment('../DATA/[Link]', msg)
add_image_attachment('../DATA/felix_auto.jpeg', msg)
send_message(smtp_server, msg)
def create_smtp_server():
password = getpass("Enter SMTP server password:") ⑫
smtpserver = [Link](SMTP_SERVER, SMTP_PORT) ⑬
[Link](SMTP_USER, password) ⑭
return smtpserver
if __name__ == '__main__':
main()
④ global variables for external information (IRL should be from environment — command line, config
file, etc.)
⑪ add binary attachment to message, including type and subtype (e.g., "image/jpg)"
⑮ send message
Remote Access
For remote access to other computers, you generally use the SSH protocol. Python has several ways to
use SSH.
The current best way is to use paramiko. It is a pure-Python module for connecting to other computers
using SSH. It is not part of the standard library, but is included with the Anaconda distribution.
Auto-adding hosts
• Use set_missing_host_key_policy()
The first time you connect to a new host with SSH, you get the following message:
To avoid the message when using Paramiko, call set_missing_host_key_policy() from the Paramiko
SSH client object:
ssh.set_missing_host_key_policy([Link]())
Remote commands
• Use SSHClient
To run commands on a remote computer, use SSHClient. Once you connect to the remote host, you can
execute commands and access the standard I/O of the remote program.
The exec_command() method executes a command on the remote host, and returns a 3-tuple with the
remote command’s stdin, stdout, and stderr as file-like objects.
You can read from stdout and stderr, and write to stdin.
With some versions of paramiko, the stdin object returned by exec_command() must
NOTE be explicitly set to None, or deleted with DEL after use. Otherwise, an error will be
raised.
Example
paramiko_commands.py
#!/usr/bin/env python
import paramiko
ssh.set_missing_host_key_policy([Link]()) ②
paramiko_commands.py
python
------------------------------------------------------------
total 384
drwx------+ 3 python staff 96 Feb 11 2021 Desktop
drwx------+ 3 python staff 96 Feb 11 2021 Documents
drwx------+ 3 python staff 96 Feb 11 2021 Downloads
drwx------@ 50 python staff 1600 Sep 14 07:12 Library
drwx------+ 3 python staff 96 Feb 11 2021 Movies
drwx------+ 3 python staff 96 Feb 11 2021 Music
drwx------+ 3 python staff 96 Feb 11 2021 Pictures
drwxr-xr-x+ 4 python staff 128 Feb 11 2021 Public
-rw-r--r-- 1 python staff 148544 May 27 16:16 [Link]
drwxr-xr-x 2 python staff 64 May 27 16:00 foo
drwxr-xr-x 2 python staff 64 May 27 16:16 testing
drwxr-xr-x 3 python staff 96 Feb 18 2021 text_files
------------------------------------------------------------
STDOUT:
-rw-r--r-- 1 root wheel 6946 Jun 5 2020 /etc/passwd
STDERR:
ls: /etc/horcrux: No such file or directory
------------------------------------------------------------
• Create transport
To copy files with paramiko, first create a Transport object. Using a with block will automatically close
the Transport object.
From the transport object you can create an SFTPClient. Once you have this, call standard FTP/SFTP
methods on that object.
Some common methods include listdir_iter(), get(), put(), mkdir(), and rmdir().
Example
paramiko_copy_files.py
#!/usr/bin/env python
import os
import paramiko
REMOTE_DIR = 'text_files'
# [Link](local-file)
# [Link](local-file, remote-file)
[Link]('../DATA/[Link]', 'text_files/[Link]') ⑥
[Link]('../DATA/[Link]', '[Link]')
[Link]('../DATA/[Link]', 'text_files')
[Link](remote_file, '[Link]') ⑦
paramiko_copy_files.py
• Write to stdin
To interact with a remote program, write to the stdin object returned by ssh_object.exec_command().
[Link]("command input....\n")
Be sure to add a newline (\n) for each line of input you send.
To get the response, read the next line(s) of code with [Link]()
Example
paramiko_interactive.py
#!/usr/bin/env python
import paramiko
# bc is an interactive calculator that comes with Unix-like systems (Linux, Mac, etc.)
[Link]("17 + 25\n") ⑤
result = [Link]() ⑥
print("Result is:", result)
[Link]("scale = 3\n") ⑦
[Link]("738.3/191.9\n")
result = [Link]()
print("Result is:", result)
[Link]("quit\n") ⑧
stdin = None ⑨
paramiko_interactive.py
Result is: 42
Chapter 9 Exercises
Exercise 9-1 (fetch_xkcd_requests.py, fetch_xkcd_urllib.py)
Write a script to fetch the following image from the Internet and display it. [Link]
comics/[Link]
Write a script to count how many links are on the home page of Wikipedia. To do this, read the page
into memory, then look for occurrences of the string "href". (For real screen-scraping, you can use the
Beautiful Soup module.)
You can use the string method find(), which can be called like [Link](text, start, stop), which finds on a
slice of the string, moving forward each time the string is found.
If the class conditions allow it (i.e., if you have access to the Internet, and an SMTP account), send an
email to yourself with the image [Link] (from the DATA folder) attached.
Using glob
• Expands wildcards
When executing external programs, sometimes you want to specify a list of files using a wildcard. The
glob function in the glob module will do this. Pass one string containing a wildcard (such as *.txt) to
glob(), and it returns a sorted list of the matching files. If no files match, it returns an empty list.
Example
glob_example.py
#!/usr/bin/env python
files = glob('../DATA/*.txt') ①
print(files, '\n')
no_files = glob('../JUNK/*.avi')
print(no_files, '\n')
glob_example.py
['../DATA/presidents_plus_biden.txt', '../DATA/columns_of_numbers.txt',
'../DATA/poe_sonnet.txt', '../DATA/computer_people.txt', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/world_airport_codes.txt', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/us_airport_codes.txt', '../DATA/[Link]',
'../DATA/http_status_codes.txt', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]', '../DATA/world_median_ages.txt',
'../DATA/phone_numbers.txt', '../DATA/sales_by_month.txt', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/example_data.txt', '../DATA/[Link]', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/float_values.txt', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]', '../DATA/Pride_and_Prejudice.txt',
'../DATA/nsfw_words.txt', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/nc_counties_avg_wage.txt', '../DATA/[Link]', '../DATA/[Link]',
'../DATA/[Link]', '../DATA/world_airports_codes_raw.txt',
'../DATA/[Link]']
[]
Using [Link]()
• Splits string
If you have an external command you want to execute, you should split it into individual words. If
your command has quoted whitespace, the normal split() method of a string won’t work.
For this you can use [Link](), which preserves quoted whitespace within a string.
Example
shlex_split.py
#!/usr/bin/env python
#
import shlex
print([Link]()) ②
print()
print([Link](cmd)) ③
shlex_split.py
• Convenience methods
◦ run()
◦ call(), check_call()
The subprocess module spawns and manages new processes. You can use this to run local non-Python
programs, to log into remote systems, and generally to execute command lines.
subprocess implements a low-level class named Popen; However, the convenience methods run(),
check_call(), and check_output(), which are built on top of Popen(), are commonly used, as they
have a simpler interface. You can capture *stdout and stderr, separately. If you don’t capture them,
they will go to the console.
In all cases, you pass in an iterable containing the command split into individual words, including any
file names. This is why this chapter starts with [Link]() and [Link]().
Attribute Description
args The arguments used to launch the process. This may be a list or a string.
returncode Exit status of the child process. Typically, an exit status of 0 indicates that it ran
successfully.
A negative value -N indicates that the child was terminated by signal N (POSIX
only).
stdout Captured stdout from the child process. A bytes sequence, or a string if run() was
called with an encoding or errors. None if stdout was not captured.
If you ran the process with stderr=[Link], stdout and stderr will be
combined in this attribute, and stderr will be None. stderr
Run command with arguments. Wait for command to complete, then return a CompletedProcess
instance.
subprocess.check_call(cmd, ...)
Run command with arguments. Wait for command to complete. If the exit code was zero then return,
otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the
returncode attribute.
check_output(cmd, ...)
Run command with arguments and return its output as a byte string. If the exit code was non-zero it
raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode
attribute and output in the output attribute.
Example
subprocess_conv.py
#!/usr/bin/env python
import sys
from subprocess import check_call, check_output, CalledProcessError
from glob import glob
import shlex
if [Link] == 'win32':
CMD = 'cmd /c dir'
FILES = r'..\DATA\t*'
else:
CMD = 'ls -ld'
FILES = '../DATA/t*'
cmd_words = [Link](CMD)
cmd_files = glob(FILES)
try:
check_call(full_cmd)
except CalledProcessError as err:
print("Command failed with return code", [Link])
print('-' * 60)
try:
output = check_output(full_cmd)
print("Output:", [Link](), sep='\n')
except CalledProcessError as e:
print("Process failed with return code", [Link])
print('-' * 50)
subprocess_conv.py
--------------------------------------------------
(Windows only) The following commands are internal to [Link], and must be preceded
by cmd /c or they will not work: ASSOC, BREAK, CALL ,CD/CHDIR, CLS, COLOR, COPY,
DATE, DEL, DIR, DPATH, ECHO, ENDLOCAL, ERASE, EXIT, FOR, FTYPE, GOTO, IF, KEYS,
TIP
MD/MKDIR, MKLINK (vista and above), MOVE, PATH, PAUSE, POPD, PROMPT, PUSHD,
REM, REN/RENAME, RD/RMDIR, SET, SETLOCAL, SHIFT, START, TIME, TITLE, TYPE, VER,
VERIFY, VOL
• Assign [Link]
To capture stdout and stderr with the subprocess module, import PIPE from subprocess and assign it
to the stdout and stderr parameters to run(), check_call(), or check_output(), as needed.
For check_output(), the return value is the standard output; for run(), you can access the stdout and
stderr attributes of the CompletedProcess instance returned by run().
NOTE output is returned as a bytes object; call decode() to turn it into a normal Python string.
Example
subprocess_capture.py
#!/usr/bin/env python
import sys
from subprocess import check_output, Popen, CalledProcessError, STDOUT, PIPE ①
from glob import glob
import shlex
if [Link] == 'win32':
CMD = 'cmd /c dir'
FILES = r'..\DATA\t*'
else:
CMD = 'ls -ld'
FILES = '../DATA/t*'
cmd_words = [Link](CMD)
cmd_files = glob(FILES)
②
try:
output = check_output(full_cmd) ③
print("Output:", [Link](), sep='\n') ④
except CalledProcessError as e:
print("Process failed with return code", [Link])
print('-' * 50)
⑤
try:
cmd = cmd_words + cmd_files + ['[Link]']
proc = Popen(cmd, stdout=PIPE, stderr=STDOUT) ⑥
stdout, stderr = [Link]() ⑦
print("Output:", [Link]()) ⑧
except CalledProcessError as e:
print("Process failed with return code", [Link])
print('-' * 50)
try:
cmd = cmd_words + cmd_files + ['[Link]']
proc = Popen(cmd, stdout=PIPE, stderr=PIPE) ⑨
stdout, stderr = [Link]() ⑩
print("Output:", [Link]()) ⑪
print("Error:", [Link]()) ⑪
except CalledProcessError as e:
print("Process failed with return code", [Link])
print('-' * 50)
⑥ assign PIPE to stdout, so it is captured; assign STDOUT to stderr, so both are captured together
⑦ call communicate to get the input streams of the process; it returns two bytes objects representing
stdout and stderr
⑨ assign PIPE to stdout and PIPE to stderr, so both are captured individually
subprocess_capture.py
Output:
-rw-r--r-- 1 jstrick staff 3178541 Nov 2 2020 ../DATA/tate_data.zip
-rwxr-xr-x 1 jstrick staff 297 Nov 17 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 2198 Feb 14 2016 ../DATA/[Link]
-rw-r--r-- 1 jstrick staff 106960 Jul 26 2017 ../DATA/[Link]
-rw-r--r--@ 1 jstrick staff 284160 Jul 26 2017 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 73808 Feb 14 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 834 Feb 14 2016 ../DATA/[Link]
--------------------------------------------------
Output: -rw-r--r-- 1 jstrick staff 3178541 Nov 2 2020 ../DATA/tate_data.zip
-rwxr-xr-x 1 jstrick staff 297 Nov 17 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 2198 Feb 14 2016 ../DATA/[Link]
-rw-r--r-- 1 jstrick staff 106960 Jul 26 2017 ../DATA/[Link]
-rw-r--r--@ 1 jstrick staff 284160 Jul 26 2017 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 73808 Feb 14 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 834 Feb 14 2016 ../DATA/[Link]
-rw-r--r-- 1 jstrick students 22 Nov 11 11:26 [Link]
--------------------------------------------------
Output: -rw-r--r-- 1 jstrick staff 3178541 Nov 2 2020 ../DATA/tate_data.zip
-rwxr-xr-x 1 jstrick staff 297 Nov 17 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 2198 Feb 14 2016 ../DATA/[Link]
-rw-r--r-- 1 jstrick staff 106960 Jul 26 2017 ../DATA/[Link]
-rw-r--r--@ 1 jstrick staff 284160 Jul 26 2017 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 73808 Feb 14 2016 ../DATA/[Link]
-rwxr-xr-x 1 jstrick staff 834 Feb 14 2016 ../DATA/[Link]
-rw-r--r-- 1 jstrick students 22 Nov 11 11:26 [Link]
Error:
--------------------------------------------------
Permissions
• Simplest is [Link]()
Each entry in a Unix filesystem has a inode. The inode contains low-level information for the file,
directory, or other filesystem entity. Permissions are stored in the mode, which is a 16-bit unsigned
integer. The first 4 bits indicate what kind of entry it is, and the last 12 bits are the permissions.
To see if a file or directory is readable, writable, or executable use [Link](). To test for specific
permissions, use the [Link]() method to return a tuple of inode data, and use the S_IMODE () method
to get the mode information as a number. Then use predefined constants such as stat.S_IRUSR,
stat.S_IWGRP, etc. to test for permissions.
Example
file_access.py
#!/usr/bin/env python
import sys
import os
if len([Link]) < 2:
start_dir = "."
else:
start_dir = [Link][1]
② [Link]() returns True if file has specified permissions (can be os.W_OK, os.R_OK, or os.X_OK,
combined with | (OR))
file_access.py ../DATA
../DATA/[Link] is writable
../DATA/[Link] is writable
../DATA/Bicycle_Counts.csv is writable
../DATA/wetprf is writable
../DATA/[Link] is writable
../DATA/[Link] is writable
../DATA/[Link] is writable
../DATA/pokemon_data.csv is writable
../DATA/presidents_plus_biden.txt is writable
../DATA/baby_names is writable
Using shutil
• Create archives
• Misc utilities
The shutil module provides portable functions for copying, moving, renaming, and deleting files.
There are several variations of each command, depending on whether you need to copy all the
attributes of a file, for instance.
The module also provides an easy way to create a zip file or compressed tar archive of a folder.
Example
shutil_ex.py
#!/usr/bin/env python
#
import shutil
import os
[Link]('../DATA/[Link]', '[Link]') ①
[Link]('[Link]', '[Link]') ②
print("[Link] exists:", [Link]('[Link]'))
print("[Link] exists:", [Link]('[Link]'))
new_folder = 'remove_me'
[Link](new_folder) ③
[Link]('[Link]', new_folder)
[Link](new_folder) ⑤
① copy file
② rename file
shutil_ex.py
• Log results
A good system administration script is more than just some lines of code hacked together. It needs to
gather data, apply the appropriate business logic, and, if necessary, output the results of the business
logic to the desired destination.
Python has two tools in the standard library to help create professional command line scripts. One of
these is the argparse module, for parsing options and parameters on the script’s command line. The
other is fileinput, which simplifies processing a list of files specified on the command line.
We will also look at the logging module, which can be used in any application to output to a variety of
log destinations, including a plain file, syslog on Unix-like systems or the NTLog service on Windows,
or even email.
Creating filters
• Filter reads files or STDIN and writes to STDOUT
Common on Unix systems Well-known filters: awk, sed, grep, head, tail, cat Reads command line
arguments as files, otherwise STDIN use [Link]()
A common kind of script iterates over all lines in all files specified on the command line. The algorithm
is
Many Unix utilities are written to work this way – sed, grep, awk, head, tail, sort, and many more. They
are called filters, because they filter their input in some way and output the modified text. Such filters
read STDIN if no files are specified, so that they can be piped into.
The [Link]() class provides a shortcut for this kind of file processing. It implicitly loops through
[Link][1:], opening and closing each file as needed, and then loops through the lines of each file. If
[Link][1:] is empty, it reads [Link]. If a filename in the list is -, it also reads [Link].
To loop through a different list of files, pass an iterable object as the argument to [Link]().
There are several methods that you can call from fileinput to get the name of the current file, e.g.
Method Description
Example
file_input.py
#!/usr/bin/env python
import fileinput
../DATA/[Link]: At that point, the guy is so mad that he throws the bird into the
../DATA/[Link]: For the first few seconds there is a terrible din. The bird kicks
../DATA/[Link]: bird may be hurt. After a couple of minutes of silence, he's so
../DATA/[Link]: The bird calmly climbs onto the man's out-stretched arm and says,
../DATA/[Link]: with the birds and animals that had fallen into it: there were a
../DATA/[Link]: bank--the birds with draggled feathers, the animals with their
../DATA/[Link]: some of the other birds tittered audibly.
../DATA/[Link]: and confusion, as the large birds complained that they could not
• Use argparse
◦ Flexible
Many command line scripts need to accept options and arguments. In general, options control the
behavior of the script, while arguments provide input. Arguments are frequently file names, but can
be anything. All arguments are available in Python via [Link]
There are at least three modules in the standard library to parse command line options. The oldest
module is getopt (earlier than v1.3), then optparse ( introduced 2.3, now deprecated), and now,
argparse is the latest and greatest. (Note: argparse is only available in 2.7 and 3.0+).
To get started with argparse, create an ArgumentParser object. Then, for each option or argument, call
the parser’s add_argument() method.
The add_argument() method accepts the name of the option (e.g. -count) or the argument (e.g.
filename), plus named parameters to configure the option.
Once all arguments have been described, call the parser’s parse_args() method. (By default, it will
process [Link], but you can pass in any list or tuple instead.) parse_args() returns an object
containing the arguments. You can access the arguments using either the name of the argument or the
name specified with dest.
One useful feature of argparse is that it will convert command line arguments for you to the type
specified by the type parameter. You can write your own function to do the conversion, as well.
Another feature is that argparse will automatically create a help option, -h, for your application, using
the help strings provided with each option or parameter.
parameter description
Example
parsing_args.py
#!/usr/bin/env python
import re
import fileinput
import argparse
from glob import glob ①
from itertools import chain ②
arg_parser.add_argument(
'-i',
dest='ignore_case', action='store_true',
help='ignore case'
) ④
arg_parser.add_argument(
'pattern', help='Pattern to find (required)'
) ⑤
arg_parser.add_argument(
'filenames', nargs='*',
help='filename(s) (if no files specified, read STDIN)'
) ⑥
args = arg_parser.parse_args() ⑦
print('-' * 40)
print(args)
print('-' * 40)
⑨ for each filename argument, expand any wildcards; this returns list of lists
⑩ flatten list of lists into a single list of files to process (note: both filename_gen and filenames are
generators; these two lines are only needed on Windows — non-Windows systems automatically
expand wildcards)
⑪ loop over list of file names and read them one line at a time
parsing_args.py
----------------------------------------
Namespace(filenames=['../DATA/[Link]', '../DATA/[Link]'], ignore_case=True,
pattern='\\bbil')
----------------------------------------
The Rabbit Sends in a Little Bill
Bill's got the other--Bill! fetch it here, lad!--Here, put 'em up
Here, Bill! catch hold of this rope--Will the roof bear?--Mind
crash)--`Now, who did that?--It was Bill, I fancy--Who's to go
then!--Bill's to go down--Here, Bill! the master says you're to
`Oh! So Bill's got to come down the chimney, has he?' said
Alice to herself. `Shy, they seem to put everything upon Bill!
I wouldn't be in Bill's place for a good deal: this fireplace is
above her: then, saying to herself `This is Bill,' she gave one
Bill!' then the Rabbit's voice along--`Catch him, you by the
Last came a little feeble, squeaking voice, (`That's Bill,'
The poor little Lizard, Bill, was in the middle, being held up by
end of the bill, "French, music, AND WASHING--extra."'
Bill, the Lizard) could not make out at all what had become of
Lizard as she spoke. (The unfortunate little Bill had left off
42:Clinton:William Jefferson 'Bill':1946-08-19:NONE:Hope:Arkansas:1993-01-20:2001-01-
20:Democratic
parsing_args.py -h
positional arguments:
pattern Pattern to find (required)
filenames filename(s) (if no files specified, read STDIN)
optional arguments:
-h, --help show this help message and exit
-i ignore case
Simple Logging
For simple logging, just configure the log file name and minimum logging level with the basicConfig()
method. Then call one of the per-level methods, such as [Link] or [Link], to output a log
message for that level. If the message is at or above the minimal level, it will be added to the log file.
The file will continue to grow, and must be manually removed or truncated. If the file does not exist, it
will be created.
The logger module provides 5 levels of logging messages, from DEBUG to CRITICAL. When you set up a
logger, you specify the minimum level of messages to be logged. If you set up the logger with the
minimum level set to ERROR, then only messages at ERROR and CRITICAL levels will be logged. Setting
the minimum level to DEBUG allows all messages to be logged.
Level Value
CRITICAL 50
FATAL
ERROR 40
WARN 30
WARNING
INFO 20
DEBUG 10
UNSET 0
Example
logging_simple.py
#!/usr/bin/env python
import logging
[Link](
filename='../TEMP/[Link]',
level=[Link],
) ①
[Link]('This is a warning') ②
[Link]('This message is for debugging') ③
[Link]('This is an ERROR') ④
[Link]('This is ***CRITICAL***') ⑤
[Link]('The capital of North Dakota is Bismark') ⑥
[Link]
WARNING:root:This is a warning
ERROR:root:This is an ERROR
CRITICAL:root:This is ***CRITICAL***
To format log entries, provide a format parameter to the basicConfig() method. This format will be a
string contain special directives (i.e. Placeholders) and, optionally, other text. The directives are
replaced with logging information; other data is left as-is.
Directives are in the form %(item)type, where item is the data field, and type is the data type.
Example
logging_formatted.py
#!/usr/bin/env python
import logging
[Link](
format='%(name)s %(asctime)s %(levelname)s %(message)s', ①
filename='../TEMP/[Link]',
level=[Link],
)
[Link]("this is information")
[Link]("this is a warning")
[Link]("this is information")
[Link]("this is critical")
[Link]
Directive Description
%(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING,
ERROR, CRITICAL)
%(levelname)s Text logging level for the message ("DEBUG", "INFO", "WARNING",
"ERROR", "CRITICAL")
%(pathname)s Full pathname of the source file where the logging call was issued (if
available)
%(lineno)d Source line number where the logging call was issued (if available)
%(created)f Time when the LogRecord was created ([Link]() return value)
%(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the
time the logging module was loaded (typically at application startup
time)
• Use [Link]()
The [Link]() function will add exception information to the log message. It should only be
called in an except block.
Example
logging_exception.py
#!/usr/bin/env python
import logging
[Link]( ①
filename='../TEMP/[Link]',
level=[Link], ②
)
for i in range(3):
try:
result = i/0
except ZeroDivisionError:
[Link]('Logging with exception info') ③
① configure logging
② minimum level
[Link]
The logging module provides some preconfigured log handlers to send log messages to destinations
other than a file.
Each handler has custom configuration appropriate to the destination. Multiple handlers can be added
to the same logger, so a log message will go to a file and to email, for instance, and each handler can
have its own minimum level. Thus, all messages could go to the message file, but only CRITICAL
messages would go to email.
Be sure to read the documentation for the particular log handler you want to use
Example
logging_altdest.py
#!/usr/bin/env python
import sys
import logging
import [Link]
logger = [Link]('ThisApplication') ①
[Link]([Link]) ②
if [Link] == 'win32':
eventlog_handler = [Link]("Python Log Test") ③
[Link](eventlog_handler) ④
else:
syslog_handler = [Link]() ⑤
[Link](syslog_handler) ⑥
[Link](email_handler) ⑧
[Link]('this is debug') ⑨
[Link]('this is critical') ⑨
[Link]('this is a warning') ⑨
Chapter 10 Exercises
Exercise 10-1 (copy_files.py)
Write a script to find all text files (only the files that end in ".txt") in the DATA folder of the student files
and copy them to C:\TEMP (Windows) or /tmp (non-windows). On Windows, create the C:\TEMP folder
if it does not already exist.
Add logging to the script, and log each filename at level INFO.
• Use [Link]
When you are ready to process Python with XML, you turn to the standard library, only to find a
number of different modules with confusing names.
To cut to the chase, use [Link], which is based on ElementTree with some nice extra features,
such as pretty-printing. While not part of the core Python library, it is provided by the Anaconda
bundle.
If [Link] is not available, you can use [Link] from the core library.
ElementTree is part of the Python standard library; lxml is included with the Anaconda distribution.
Since putting "[Link]" in front of its methods requires a lot of extra typing , it is typical
to alias [Link] to just ET when importing it: import [Link] as ET
You can check the version of ElementTree via the VERSION attribute:
import [Link] as ET
print([Link])
In ElementTree, an XML document consists of a nested tree of Element objects. Each Element
corresponds to an XML tag.
An ElementTree object serves as a wrapper for reading or writing the XML text.
If you are parsing existing XML, use [Link](); this creates the ElementTree wrapper and
the tree of Elements. You can then navigate to, or search for, Elements within the tree. You can also
insert and delete new elements.
If you are creating a new document from scratch, create a top-level (AKA "root") element, then create
child elements as needed.
element = [Link]('sometag')
for subelement in element:
print([Link])
print([Link]('someattribute'))
Elements
• Element has
◦ Tag name
◦ Text
◦ Tail
When creating a new Element, you can initialize it with the tag name and any attributes. Once created,
you can add the text that will be contained within the element’s tags, or add other attributes.
When you are ready to save the XML into a file, initialize an ElementTree with the root element.
The Element class is a hybrid of list and dictionary. You access child elements by treating it as a list.
You access attributes by treating it as a dictionary. (But you can’t use subscripts for the attributes – you
must use the get() method).
The Element object also has several useful properties: tag is the element’s tag; text is the text
contained inside the element; tail is any text following the element, before the next element.
TIP Only the tag property of an Element is required; other properties are optional.
Property Description
Property Description
find(path) Finds the first toplevel element with given tag; shortcut for
getroot().find(path).
findall(path) Finds all toplevel elements with the given tag; shortcut for
getroot().findall(path).
findtext(path) Finds element text for first toplevel element with given tag; shortcut
for getroot().findtext(path).
getiterator(path) Returns an iterator over all descendants of root node matching path.
(All nodes if path not specified)
To create a new XML document, first create the root (top-level) element. This will be a container for all
other elements in the tree. If your XML document contains books, for instance, the root document
might use the "books" tag. It would contain one or more "book" elements, each of which might contain
author, title, and ISBN elements.
Once the root element is created, use SubElement to add elements to the root element, and then nested
Elements as needed. SubElement returns the new element, so you can assign the contents of the tag to
the text attribute.
Once all the elements are in place, you can create an ElementTree object to contain the elements and
allow you to write out the XML text. From the ElementTree object, call write.
To output an XML string from your elements, call [Link](), passing the root of the element tree as a
parameter. It will return a bytes object (pure ASCII), so use .decode() to convert it to a normal Python
string.
For an example of creating an XML document from a data file, see xml_create_knights.py in the
EXAMPLES folder
Example
xml_create_movies.py
#!/usr/bin/env python
movie_data = [
('Jaws', 'Spielberg, Stephen'),
('Vertigo', 'Alfred Hitchcock'),
('Blazing Saddles', 'Brooks, Mel'),
('Princess Bride', 'Reiner, Rob'),
('Avatar', 'Cameron, James'),
]
movies = [Link]('movies')
print([Link](movies, pretty_print=True).decode())
doc = [Link](movies)
[Link]('[Link]')
xml_create_movies.py
<movies>
<movie name="Jaws">
<director>Spielberg, Stephen</director>
</movie>
<movie name="Vertigo">
<director>Alfred Hitchcock</director>
</movie>
<movie name="Blazing Saddles">
<director>Brooks, Mel</director>
</movie>
<movie name="Princess Bride">
<director>Reiner, Rob</director>
</movie>
<movie name="Avatar">
<director>Cameron, James</director>
</movie>
</movies>
• Use [Link]()
Use the parse() method to parse an existing XML document. It returns an ElementTree object, from
which you can find the root, or any other element within the document.
Example
import [Link] as ET
doc = [Link]('[Link]')
root = [Link]()
To find the first child element with a given tag, use find(tag). This will return the first matching
element. The findtext(tag) method is the same, but returns the text within the tag.
To get all child elements with a given tag, use the findall(tag) method, which returns a list of elements.
if node is None:
if len(node) > 0:
A node with no children tests as false because it is an empty list, but it is not None.
The ElementTree object also supports the find() and findall() methods of the Element
TIP
object, searching from the root object.
Example
xml_planets_nav.py
#!/usr/bin/env python
'''Use etree navigation to extract planets from [Link]'''
import [Link] as ET
def main():
'''Program entry point'''
doc = [Link]('../DATA/[Link]') ①
solar_system = [Link]() ②
print(solar_system)
print()
inner = solar_system.find('innerplanets') ③
print('Inner:')
outer = solar_system.find('outerplanets')
print('Outer:')
plutoids = solar_system.find('dwarfplanets')
print('Dwarf:')
if __name__ == '__main__':
main()
xml_planets_nav.py
Inner:
Mercury
Venus
Earth
Mars
Outer:
Jupiter
Saturn
Uranus
Neptune
Dwarf:
Pluto
Example
xml_read_movies.py
#!/usr/bin/env python
# import [Link] as ET
import [Link] as ET
movies_doc = [Link]('[Link]') ①
movies = movies_doc.getroot() ②
xml_read_movies.py
Using XPath
When a simple tag is specified, the find* methods only search for subelements of the current element.
For more flexible searching, the find* methods work with simplified XPath patterns. To find all tags
named spam, for instance, use .//spam.
.//movie
presidents/president/name/last
Example
xml_planets_xpath1.py
#!/usr/bin/env python
# import [Link] as ET
import [Link] as ET
doc = [Link]('../DATA/[Link]') ①
inner_nodes = [Link]('innerplanets/planet') ②
outer_nodes = [Link]('outerplanets/planet') ③
print('Inner:')
for planet in inner_nodes: ④
print('\t', [Link]("planetname")) ⑤
print('Outer:')
for planet in outer_nodes: ④
print('\t', [Link]("planetname")) ⑤
② find all elements (relative to root element) with tag "planet" under "innerplanets" element
xml_planets_xpath1.py
Inner:
Mercury
Venus
Earth
Mars
Outer:
Jupiter
Saturn
Uranus
Neptune
Example
xml_planets_xpath2.py
#!/usr/bin/env python
# import [Link] as ET
import [Link] as ET
doc = [Link]('../DATA/[Link]')
jupiter = [Link]('.//planet[@planetname="Jupiter"]')
xml_planets_xpath2.py
Metis
Adrastea
Amalthea
Thebe
Io
Europa
Gannymede
Callista
Themisto
Himalia
Lysithea
Elara
Syntax Meaning
tag Selects all child elements with the given tag. For example, “spam” selects all child
elements named “spam”, “spam/egg” selects all grandchildren named “egg” in all
child elements named “spam”. You can use universal names (“{url}local”) as tags.
* Selects all child elements. For example, “*/egg” selects all grandchildren named
“egg”.
. Select the current node. This is mostly useful at the beginning of a path, to
indicate that it’s a relative path.
// Selects all subelements, on all levels beneath the current element (search the
entire subtree). For example, “.//egg” selects all “egg” elements in the entire tree.
[@attrib] Selects all elements that have the given attribute. For example, “.//a[@href]”
selects all “a” elements in the tree that has a “href” attribute.
[@attrib=’value’] Selects all elements for which the given attribute has the given value. For
example, “.//div[@class=’sidebar’]” selects all “div” elements in the tree that has
the class “sidebar”. In the current release, the value cannot contain quotes.
parent_tag[child_ta Selects all parent elements that has a child element named child_tag. In the
g] current version, only a single tag can be used (i.e. only immediate children are
supported). Parent tag can be *.
About JSON
JSON is a lightweight and human-friendly format for sharing or storing data. It was developed and
popularized by Douglas Crockford starting in 2001.
A JSON file contains objects and arrays, which correspond exactly to Python dictionaries and lists.
Data types are Number, String, and Boolean. Strings are enclosed in double quotes (only); numbers
look like integers or floats; Booleans are represented by true or false; null (None in Python) is
represented by null.
Reading JSON
To read a JSON file, import the json module. Use [Link]() to parse a string containing valid JSON.
Use [Link]() to read JSON from a file-like object0.
Both methods return a Python dictionary containing all the data from the JSON file.
Example
json_read.py
#!/usr/bin/env python
import json
# [Link](STRING)
# [Link](FILE_OBJECT)
# print(solar)
print(solar['innerplanets']) ③
print('*' * 60)
print(solar['innerplanets'][0]['name'])
print('*' * 60)
for planet in solar['innerplanets'] + solar['outerplanets']:
print(planet['name'])
print("*" * 60)
for group in solar:
if [Link]('planets'):
for planet in solar[group]:
print(planet['name'])
json_read.py
[{'name': 'Mercury', 'moons': None}, {'name': 'Venus', 'moons': None}, {'name': 'Earth',
'moons': ['Moon']}, {'name': 'Mars', 'moons': ['Deimos', 'Phobos']}]
************************************************************
Mercury
************************************************************
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
************************************************************
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
Pluto
Writing JSON
To output JSON to a string, use [Link](). To output JSON to a file, pass a file-like object to
[Link](). In both cases, pass a Python data structure as the data to be output.
Example
json_write.py
#!/usr/bin/env python
import json
george = [
{
'num': 1,
'lname': 'Washington',
'fname': 'George',
'dstart': [1789, 4, 30],
'dend': [1797, 3, 4],
'birthplace': 'Westmoreland County',
'birthstate': 'Virginia',
'dbirth': [1732, 2, 22],
'ddeath': [1799, 12, 14],
'assassinated': False,
'party': None,
},
{
'spam': 'ham',
'eggs': [1.2, 2.3, 3.4],
'toast': {'a': 5, 'm': 9, 'c': 4},
}
] ①
js = [Link](george, indent=4) ②
print(js)
json_write.py
[
{
"num": 1,
"lname": "Washington",
"fname": "George",
"dstart": [
1789,
4,
30
],
"dend": [
1797,
3,
4
],
"birthplace": "Westmoreland County",
"birthstate": "Virginia",
"dbirth": [
1732,
2,
22
],
"ddeath": [
1799,
12,
14
],
"assassinated": false,
"party": null
},
{
"spam": "ham",
"eggs": [
1.2,
2.3,
3.4
],
"toast": {
"a": 5,
"m": 9,
"c": 4
}
}
]
Customizing JSON
The JSON spec only supports a limited number of datatypes. If you try to dump a data structure
contains dates, user-defined classes, or many other types, the json encoder will not be able to handle it.
You can a custom encoder for various data types. To do this, write a function that expects one Python
object, and returns some object that JSON can parse, such as a string or dictionary. The function can be
called anything. Specify the function with the default parameter to [Link]().
The function should check the type of the object. If it is a type that needs special handling, return a
JSON-friendly version, otherwise just return the original object.
Python JSON
dict object
list array
str string
True true
False false
None null
Example
json_custom_encoding.py
#!/usr/bin/env python
#
import json
from datetime import date
class Parrot(): ①
def __init__(self, name, color):
self._name = name
self._color = color
@property
def name(self): ②
return self._name
@property
def color(self):
return self._color
parrots = [ ③
Parrot('Polly', 'green'), #
Parrot('Peggy', 'blue'),
Parrot('Roger', 'red'),
]
def encode(obj): ④
if isinstance(obj, date): ⑤
return [Link]() ⑥
elif isinstance(obj, Parrot): ⑦
return {'name': [Link], 'color': [Link]} ⑧
return obj ⑨
data = { ⑩
'spam': [1, 2, 3],
'ham': ('a', 'b', 'c'),
'toast': date(2014, 8, 1),
'parrots': parrots,
}
⑨ if not processed, return object for JSON to parse with default parser
⑪ convert Python data to JSON data; default parameter specifies function for custom encoding; indent
parameter says to indent and add newlines for readability
json_custom_encoding.py
{
"spam": [
1,
2,
3
],
"ham": [
"a",
"b",
"c"
],
"toast": "Fri Aug 1 00:00:00 2014",
"parrots": [
{
"name": "Polly",
"color": "green"
},
{
"name": "Peggy",
"color": "blue"
},
{
"name": "Roger",
"color": "red"
}
]
}
YAML is a structured data format which is a superset of JSON. However, YAML allows for a more
compact and readable format.
Reading and writing YAML uses the same syntax as JSON, other than using the yaml module, which is
NOT in the standard library. To install the yaml module:
To read a YAML file (or string) into a Python data structure, use [Link](__file_object__) or
[Link](__string__).
Example
yaml_read_solar.py
import yaml
star = solar_data['star']
print("Our star is {}\n".format(star))
yaml_read_solar.py
Mercury
None
Venus
None
Earth
Moon
Mars
Deimos
Phobos
Metis
Jupiter
Adrastea
Amalthea
Thebe
Io
Europa
Gannymede
Callista
Themisto
Himalia
Lysithea
Elara
Saturn
Rhea
Hyperion
Titan
Iapetus
Mimas
Example
yaml_create_file.py
import sys
from datetime import date
import yaml
potus = {
'presidents': [
{
'lastname': 'Washington',
'firstname': 'George',
'dob': date(1732, 2, 22),
'dod': date(1799, 12, 14),
'birthplace': 'Westmoreland County',
'birthstate': 'Virginia',
'term': [ date(1789, 4, 30), date(1797, 3, 4) ],
'assassinated': False,
'party': None,
},
{
'lastname': 'Adams',
'firstname': 'John',
'dob': date(1735, 10, 30),
'dod': date(1826, 7, 4),
'birthplace': 'Braintree, Norfolk',
'birthstate': 'Massachusetts',
'term': [date(1797, 3, 4), date(1801, 3, 4)],
'assassinated': False,
'party': 'Federalist',
}
]
}
[Link](potus, [Link])
yaml_create_file.py
presidents:
- assassinated: false
birthplace: Westmoreland County
birthstate: Virginia
dob: 1732-02-22
dod: 1799-12-14
firstname: George
lastname: Washington
party: null
term:
- 1789-04-30
- 1797-03-04
- assassinated: false
birthplace: Braintree, Norfolk
birthstate: Massachusetts
dob: 1735-10-30
dod: 1826-07-04
firstname: John
lastname: Adams
party: Federalist
term:
- 1797-03-04
- 1801-03-04
To read CSV data, use the reader() method in the csv module.
To create a reader with the default settings, use the reader() constructor. Pass in an iterable – typically,
but not necessarily, a file object.
You can also add parameters to control the type of quoting, or the output delimiters.
Example
csv_read.py
#!/usr/bin/env python
import csv
csv_read.py
Nonstandard CSV
You can customize how the CSV parser and generator work by passing extra parameters to [Link]()
or [Link](). You can change the field and row delimiters, the escape character, and for output, what
level of quoting.
You can also create a "dialect", which is a custom set of CSV parameters. The csv module includes one
extra dialect, excel, which handles CSV files generated by Microsoft Excel. To use it, specify the dialect
parameter:
Parameter Meaning
skipinitialspace If True, skip white space after field separator (default: False)
lineterminator The character sequence which terminates rows (default: depends on OS)
doublequote Control quote handling inside fields. When True, two consecutive quotes are read
as one, and one quote is written as two. (default: True)
Example
csv_nonstandard.py
#!/usr/bin/env python
import csv
csv_nonstandard.py
Using [Link]
Instead of the normal reader, you can create a dictionary-based reader by using the DictReader class.
If the CSV file has a header, it will parse the header line and use it as the field names. Otherwise, you
can specify a list of field names with the fieldnames parameter. For each row, you can look up a field
by name, rather than position.
Example
csv_dictreader.py
#!/usr/bin/env python
import csv
② create reader, passing in field names (if not specified, uses first row as field names)
csv_dictreader.py
• Use [Link]()
To output data in CSV format, first create a writer using [Link](). Pass in a file-like object.
For each row to write, call the writerow() method of the writer, passing in an iterable with the values
for that row.
Example
csv_write.py
#!/usr/bin/env python
import sys
import csv
chicago_data = [
['Name', 'Position Title', 'Department', 'Employee Annual Salary'],
['BONADUCE, MICHAEL J', 'POLICE OFFICER', 'POLICE', '$80724.00'],
['MELLON, MATTHEW J "Matt"', 'POLICE OFFICER', 'POLICE', '$75372.00'],
['FIERI, JOHN J', 'FIREFIGHTER-EMT', 'FIRE', '$75342.00'],
['GALAHAD, MERLE S', 'CLERK III', 'BUSINESS AFFAIRS', '$45828.00'],
['ORCATTI, JENNIFER L', 'FIRE COMMUNICATIONS OPERATOR I', 'OEMC', '$63121.68'],
['ASHE, JOHN W', 'FOREMAN OF MACHINISTS', 'AVIATION', '$96553.60'],
['SADINSKY BLAKE, MICHAEL G', 'POLICE OFFICER', 'POLICE', '$78012.00'],
['GRANT, CRAIG A', 'SANITATION LABORER', 'STREETS & SAN', '$69576.00'],
['MILLER, JONATHAN D', 'POLICE OFFICER', 'POLICE', '$75372.00'],
['FRANK, ARTHUR R',
'POLICE OFFICER/EXPLSV DETECT, K9 HNDLR',
'POLICE',
'$87918.00'],
['POVOTTI, JAMES S "Jimmy P"', 'TRAFFIC CONTROL AIDE-HOURLY', 'OEMC', '$19167.20'],
['TRAWLER, DANIEL J', 'POLICE OFFICER', 'POLICE', '$75372.00'],
['SCUBA, ANDREW G', 'POLICE OFFICER', 'POLICE', '$75372.00'],
['SWINE, MATTHEW W', 'SERGEANT', 'POLICE', '$99756.00'],
['''RYDER, MYRTA T "Lil'Myrt"''', 'POLICE OFFICER', 'POLICE', '$83706.00'],
['KORSHAK, ROMAN', 'PARAMEDIC', 'FIRE', '$75372.00']
]
① create CSV writer from file object that is opened for writing; on windows, need to set output line
terminator to \n
Pickle
To create pickled data, use either [Link]() or [Link](). Both functions take a data structure
as the first argument. dumps() returns the pickled data as a string. dump () writes the data to a file-like
object which has been specified as the second argument. The file-like object must be opened for
writing.
To read pickled data, use [Link](), which takes a file-like object that has been open for writing, or
[Link]() which reads from a string. Both functions return the original data structure that had
been pickled.
NOTE The syntax of the json module is based on the pickle module.
Example
[Link]
#!/usr/bin/env python
import pickle
from pprint import pprint
①
airports = {
'RDU': 'Raleigh-Durham', 'IAD': 'Dulles', 'MGW': 'Morgantown',
'EWR': 'Newark', 'LAX': 'Los Angeles', 'ORD': 'Chicago'
}
colors = [
'red', 'blue', 'green', 'yellow', 'black',
'white', 'orange', 'brown', 'purple'
]
data = [ ②
colors,
airports,
]
pprint(pickled_data) ⑦
[Link]
[['red',
'blue',
'green',
'yellow',
'black',
'white',
'orange',
'brown',
'purple'],
{'EWR': 'Newark',
'IAD': 'Dulles',
'LAX': 'Los Angeles',
'MGW': 'Morgantown',
'ORD': 'Chicago',
'RDU': 'Raleigh-Durham'}]
Chapter 11 Exercises
Exercise 11-1 ([Link])
Using ElementTree, create a new XML file containing all the words that start with x from [Link].
The root tag should be named words, and each word should be contained in a word tag. The finished
file should look like this:
<words>
<word>xanthan</word>
<word>xanthans</words>
and so forth
</words>
Use ElementTree to parse [Link]. Loop through and print out each president’s first and last
names and their state of birth.
Write a script which reads the data from [Link] into an dictionary where the key is the term
number, and the value is another dictionary of data for one president.
Using the pickle module, Write the entire dictionary out to a file named [Link].
Write a script to open [Link], and restore the data back into a dictionary.
Then loop through the array and print out each president’s first name, last name, and party.
Data Science
Practical Data Science Cookbook Tony Ojeda, Sean Patrick Packt Publishing
Murphy, Benjamin Bengfort,
Abhijit Dasgupta
Design Patterns
Head First Design Patterns Eric Freeman, Elisabeth Robson, O’Reilly Media
Bert Bates, Kathy Sierra
Learning Python, 2nd Ed. Mark Lutz, David Asher O’Reilly & Assoc.
Python Cookbook, 3nd. Ed. David Beazley, Brian K. Jones O’Reilly & Assoc.
Python Programming on Win32 Mark Hammond, Andy Robinson O’Reilly & Assoc.
Misc
Networking
Testing
Web Development
Full Stack Python (e-book only) Matt Makai Gumroad (or free download)
Full Stack Python Guide to Matt Makai Gumroad (or free download)
Deployments (e-book only)
Two Scoops of Django: Best Daniel Roy Greenfeld, Audrey Two Scoops Press
Practices for Django 1.11 Roy Greenfeld
Index
@ connection object, 226
@[Link], 203 constructors, 86
__call__(, 156 context manager, 224
__init__(, 156 creating Unix-style filters, 338
__init__.py, 73 CSV, 392
__new__(, 156 nonstandard, 393
__prepare__(, 156 csv
__pycache__, 60 DictReader, 395
0, 224, 227, 231, 236 [Link](), 392
[Link](, 397
A cursor, 226
abstract base classes, 103 cursor object, 226
Anaconda, 359 [Link], 245
API, 222 cx_oracle, 223
argparse, 341
D
assert, 186
assertions, 185 database programming, 222
asynchronous communication, 260 database server, 224
asyncio, 285 DB API, 222
attributes, 123 debugger
autocommit, 249 setting breakpoints, 176
starting, 174
B stepping through a program, 175
benchmarking, 179 decorator class, 141
binary mode, 295 decorator function, 138
decorator parameters, 145
C decorators, 132
callable, 132 decorators in the standard library, 133
Cassandra, 252 delattr(), 123
class dictionary comprehension, 31
defining at runtime, 148 dictionary cursor, 241
class data, 92 emulating, 247
class method, 93 Django, 197, 251
classes, 80 Django framework, 156
constructors, 86 Django ORM, 251
defining, 81 Douglas Crockford, 376
inheritance, 95
E
collection vs generator, 33
command line scripts, 337 Element, 360-361
comments, 167 ElementTree, 359
commit, 249 find(), 368
[Link], 197 findall(), 368
Response super(), 99
attributes, 294 Sybase, 223
rollback, 249 [Link], 66
running tests, 206
by component, 206 T
by mark, 206 test case, 184
by name, 206 test cases, 184
test runner, 185, 187
S test runners, 184
SAP DB, 223 tests
sapdbapi, 223 messages, 186
scope thread, 261
builtin, 56 thread class
global, 56 creating, 266
nonlocal, 56 threading, 260
sendmail(, 303 threading module, 263
set comprehension, 32 [Link], 263
setattr(), 123 threads
setter methods, 87 debugging, 275
SFTP, 315 locks, 268
[Link](), 324 queue, 271
shutil, 334 simple, 264
singledispatch, 383 variable sharing, 268
smtplib, 303 Tim Peters, 10
sorted(), 22 timeit, 179
sorting timsort, 10
custom key, 24 tolerance
special methods, 106 [Link], 188
SQL code, 226 transactions, 249
SQL data integrity, 249 tuple, 11
SQL injection, 234 Twisted, 285
SQL queries, 227 type, 155
SQLAlchemy, 251 type(), 148
SQLite, 223
sqlite3, 223 U
SSH protocol, 310 unit test, 184
static method, 112 unit test components, 184
SubElement, 361 unit tests
subprocess, 325-326 failing, 207
capturing stdout/stderr, 329 mock objects, 211
check_call(), 326 running, 187
check_output(), 326 skipping, 207
run(), 326 [Link], 210-211
super(, 96 unpacking function parameters, 17
[Link](, 300
V
variable scope, 56
W
web services
consuming, 300
X
xfail, 207
XML, 358
root element, 364
[Link], 358-359
XPASS, 207
XPath, 372
xUnit, 185
Y
yield, 34
Z
Zen of Python, 10