===================== Python Course Outline ===================== :author: Dave Kuhlman :address: dkuhlman@rexx.com http://www.rexx.com/~dkuhlman :revision: 1.0a :date: July 5, 2006 :copyright: Copyright (c) 2005 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the MIT License http://www.opensource.org/licenses/mit-license.php. :abstract: This document provides an outline of an introductory course on programming in Python. .. sectnum:: :depth: 4 :start: 1 .. contents:: :depth: 4 Introductions Etc ================= Introductions Practical matters Starting the Python interactive interpreter. IPython. Pyrepl/pythoni (requires Zope). Running scripts Editors -- Choose an editor which you can configure so that it uses indent 4 spaces, not tab characters. For a list of editors for Python, see: http://wiki.python.org/moin/PythonEditors. A few possible editors: - PFE -- Programmer File Editor -- http://www.lancs.ac.uk/staff/steveb/cpaap/pfe/. - UltraEdit -- http://www.ultraedit.com/. - Jed -- See http://www.jedsoft.org/jed/. - Emacs -- For MS Windows, see http://www.gnu.org/software/emacs/windows/ntemacs.html. - Etc. Interactive interpreters: - ``python`` - ``ipython`` IDEs: - Idle - PyWin -- Available at: http://sourceforge.net/projects/pywin32/. - BlackAdder -- See http://www.thekompany.com/products/blackadder/. - WingIDE -- See http://wingware.com/wingide/. - Eclipse -- http://eclipse.org/. - Kdevelop -- Linux/KDE -- See http://www.kdevelop.org/. Resources --------- Where else to get help: - http://www.python.org - Python standard documentation -- http://www.python.org/doc/. You will also find links to tutorials there. - FAQs -- http://www.python.org/doc/faq/. - Special interest groups (SIGs) -- http://www.python.org/sigs/ - Other mailing lists for specific applications: Zope, Twisted, etc. - http://sourceforge.net -- Lots of projects. Search for "python". - USENET -- comp.lang.python Local documentation: - ``pydoc``. Example, on the command line, type: ``pydoc re``. - Import a module, then view its ``.__doc__`` attribute. - At the interactive prompt, use ``help(obj)``. You might need to import it first. Example:: >>> import urllib >>> help(urllib) - Download and install the standard documentation set from http://www.python.org/doc/. A general description of Python: - A scripting language -- Python is suitable (1) for embedding, (2) for writing small unstructured scripts, (3) for "quick and dirty" programs. - *Not* a scripting language -- (1) Python scales. (2) Python encourages us to write code that is clear and well-structures. - Interpreted, but also compiled to byte-code. Modules are automatically compiled (to .pyc) when imported, but may also be explicitly compiled. - Provides an interactive command line and interpreter shell. In fact, there are several. - Dynamic -- For example: - Types are bound to values, not to variables. - Function and method lookup is done at runtime. - Values are inspectable. - There is an interactive interpreter, more than one, in fact. - You can list the methods supported by any given object. - Reasonably high level -- High level built-in data types; high level structures (for walking lists and iterators, for example). - Object-oriented -- Simple object definition. Data hiding by agreement. Multiple inheritance. Interfaces by convention. - Highly structured -- Statements, functions, classes, modules, and packages enable us to write large, well-structured applications. Why structure? Readability, locate-ability, modifiability. - Explicitness - First-class objects: - Definition: Can (1) pass to function; (2) return from function; (3) stuff into a data structure. - Operators can be applied to *values*. Example: ``f(x)[3]`` - Indented block structure -- "Python is pseudo-code that runs." - Embedding and extending Python -- Python provides a well-documented and supported way (1) to embed the Python interpreter in C/C++ applications and (2) to extend Python with modules and objects implemented in C/C++. - In some cases, SWIG can generate wrappers for existing C/C++ code automatically (see http://www.swig.org/). - Pyrex enables us to generate C code from Python (see http://www.cosc.canterbury.ac.nz/~greg/python/Pyrex/). - To embed and extend Python with Java, there is Jython (see http://www.jython.org/). - Comparison with other languages: compiled languages (e.g. C/C++); Java; Perl and Tcl. Development speed. Execution speed. Clarity and maintainability. - Also see `The Zen of Python `_. Lexical matters =============== Lines ----- - Python does what you want it to do *most* of the time so that you only have to add extra characters *some* of the time. - Statement separator is a semi-colon, but is only needed when there is more than one statement on a line. - Continuation lines -- Use a back-slash at the end of the line. But, note that an opening bracket (or parenthesis) make the back-slash unnecessary. - Comments -- Everything after "#" on a line is ignored. No block comments, but doc strings are a comment in quotes at the beginning of a module, class, method or function. Names and tokens ---------------- - Allowed characters: a-z A-Z 0-9 underscore, and must begin with a letter or underscore. - Names and identifiers are case sensitive. - Identifiers can be of unlimited length. - Special names, customizing, etc. -- Usually begin and end in double underscores. - Special name classes -- Single and double underscores. - Leading double underscores -- Name mangling for method names. - Leading single underscore -- Suggests a "private" method name. Not imported by "from module import \*". - Naming conventions -- Not rigid, but: - Modules and packages -- all lower case. - Globals and constants -- Upper case. - Classes -- Bumpy caps with initial upper. - Methods and functions -- All lower case with words separated by underscores. - Local variables -- Lower case or bumpy caps with initial lower or your choice. Blocks and indentation ---------------------- Python represents block structure and nested block structure with indentation, not with begin and end brackets. Benefits of the use of indentation to indicate structure: - Reduces the need for a coding standard. Only need to specify that indentation is 4 spaces and no hard tabs. - Reduces inconsistency. Code from different sources follow the same indentation style. They have to. - Reduces work. Only need to get the indentation correct, not *both* indentation and brackets. - Reduces clutter. Eliminates all the curly brackets. - If it looks correct, it is correct. Indentation cannot fool the reader. Editor considerations -- The standard is 4 spaces (not tabs) for each indentation level. You will need a text editor that helps you respect that. Doc strings ----------- Doc strings are like comments, but they are carried with executing code. Doc strings can be viewed with several tools, e.g. ``help()``, ``obj.__doc__``, and, in IPython, ``?``. We can use triple-quoting to create doc strings that span multiple lines. There are also tools that extract and format doc strings, for example: - `5.1 pydoc -- Documentation generator and online help system `_ - `epydoc`_ Program structure ----------------- - Statements, data structures, functions, classes, modules, packages. - Execution -- def, class, etc are executable statements that add something to the current name-space. Modules can be both executable and import-able. Operators --------- - See: http://docs.python.org/ref/operators.html. Python defines the following operators:: + - * ** / // % << >> & | ^ ~ < > <= >= == != <> The comparison operators ``<>`` and ``!=`` are alternate spellings of the same operator. ``!=`` is the preferred spelling; ``<>`` is obsolescent. - There are also (1) the dot operator, (2) the subscript operator ``[]``, and the function/method call operator ``()``. Later, we will see how these operators can be emulated in classes that you define yourself. Code evaluation --------------- Creating names/variables -- The following all create names (variables): (1) assignment, (2) function definition, (3) class definition, ... First class objects -- Almost all objects in Python are first class. Definition: An object is first class if: (1) we can put it in a structured object; (2) we can pass it to a function; (3) we can return it from a function. References -- Objects (or references to them) can be shared. What does this mean? - The object(s) satisfy the identity test operator ``is``. - The built-in function ``id()`` returns the same value. - The consequences for mutable objects are different from those for immutable objects. - ``del()`` -- The built-in function ``del()`` removes a reference, not (necessarily) the object itself. Built-in datatypes ================== Numeric types ------------- The numeric types are: - Plain integers -- Same precision as a C long, usually a 32-bit binary number. - Long integers -- Define with ``100L``. But, plain integers are automatically promoted when needed. - Floats -- Implemented as a C double. Precision depends on your machine. - Complex numbers -- Define with, for example, ``3j`` or ``complex(3.0, 2.0)``. See `2.3.4 Numeric Types -- int, float, long, complex `_. Python does mixed arithmetic. Integer division truncates. Tuples and lists ---------------- Tuple constructor -- ``()``. List constructor -- ``[]``. Tuples are like lists, but are not mutable. Notes on sequence constructors: - To construct a tuple with a single element, use ``(x,)``; a tuple with a single element requires a comma. - You can spread elements across multiple lines (and no need for continuation character "\"). - A comma can follow the last element. Subscription: - Indexing into a sequence - Negative indexes -- Effectively, length of sequence plus index. - Slicing -- Example: ``data[2:5]``. - Slicing with strides -- Example: ``data[::2]``. Operations on tuples -- No operations that change the tuple, since tuples are immutable. We can do iteration and subscription. Operations on lists -- Operations similar to tuples plus: - Insert -- ``mylist.insert(index, newitem)``. - Append -- ``mylist.append(newitem)``. - Remove -- ``mylist.remove(item)`` and ``mylist.pop()``. Note that ``append()`` together with ``pop()`` implements a stack. - Delete -- ``del mylist[index]``. Exercises: - Create an empty list. Append 4 strings to the list. Then pop one item off the end of the list. Solution:: In [25]: a = [] In [26]: a.append('aaa') In [27]: a.append('bbb') In [28]: a.append('ccc') In [29]: a.append('ddd') In [30]: print a ['aaa', 'bbb', 'ccc', 'ddd'] In [31]: a.pop() Out[31]: 'ddd' - Use the ``for`` statement to print the items in the list. Solution:: In [32]: for item in a: ....: print item ....: aaa bbb ccc - Use the string ``join`` operation to concatenate the items in the list. Solution:: In [33]: '||'.join(a) Out[33]: 'aaa||bbb||ccc' Strings ------- Strings are sequences. They are immutable. They are indexable. For operations on strings, see http://docs.python.org/lib/string-methods.html or use:: >>> help(str) Or:: >>> dir("abc") Constructors/literals: - Quotes: single and double. Escaping quotes and other special characters with a back-slash. - Triple quoting -- Multi-line quotes. - ``str()`` -- The constructor and the name of the type/class. - ``'aSeparator'.join(aList)`` - Many more. String formatting -- See: `2.3.6.2 String Formatting Operations `_. Examples:: In [18]: name = 'dave' In [19]: size = 25 In [20]: factor = 3.45 In [21]: print 'Name: %s Size: %d Factor: %3.4f' % (name, size, factor, ) Name: dave Size: 25 Factor: 3.4500 In [25]: print 'Name: %s Size: %d Factor: %08.4f' % (name, size, factor, ) Name: dave Size: 25 Factor: 003.4500 If the right-hand argument to the formatting operator is a dictionary, then you can (actually, must) use the names of keys in the dictionary in your format strings. Examples:: In [115]: values = {'vegetable': 'chard', 'fruit': 'nectarine'} In [116]: 'I love %(vegetable)s and I love %(fruit)s.' % values Out[116]: 'I love chard and I love nectarine.' Also consider using the right justify and left justify operations. Examples: ``mystring.rjust(20)``, ``mystring.ljust(20, ':')``. Exercises: - Use a literal to create a string containing (1) a single quote, (2) a double quote, (3) both a single and double quote. Solutions:: "Some 'quoted' text." 'Some "quoted" text.' 'Some "quoted" \'extra\' text.' - Write a string literal that spans multiple lines. Solution:: """This string spans several lines because it is a little long. """ - Use the string ``join`` operation to create a string that contains a colon as a separator. Solution:: >>> content = [] >>> content.append('finch') >>> content.append('sparrow') >>> content.append('thrush') >>> content.append('jay') >>> contentstr = ':'.join(content) >>> print contentstr finch:sparrow:thrush:jay - Use string formatting to produce a string containing your last and first names, separated by a comma. Solution:: >>> first = 'Dave' >>> last = 'Kuhlman' >>> full = '%s, %s' % (last, first, ) >>> print full Kuhlman, Dave Incrementally building up large strings from lots of small strings -- Since strings in Python are immutable, appending to a string requires a reallocation. So, it is faster to append to a list, then use ``join``. Example:: In [25]: strlist = [] In [26]: strlist.append('Line #1') In [27]: strlist.append('Line #2') In [28]: strlist.append('Line #3') In [29]: str = '\n'.join(strlist) In [30]: print str Line #1 Line #2 Line #3 Dictionaries ------------ A dictionary is a sequence, whose values are accessible by key. The order of elements in a dictionary is undefined. But, we can iterate over (1) the keys, (2) the values, and (3) the items (key-value pairs) in a dictionary. Literals for constructing dictionaries:: ``{key1: value1, key2: value2, } Constructor for dictionaries -- ``dict()`` is a factory function that constructs dictionaries. Some examples, which were taken from `2.1 Built-in Functions `_ (http://docs.python.org/lib/built-in-funcs.html) :: dict({'one': 2, 'two': 3}) dict({'one': 2, 'two': 3}.items()) dict({'one': 2, 'two': 3}.iteritems()) dict(zip(('one', 'two'), (2, 3))) dict([['two', 3], ['one', 2]]) dict(one=2, two=3) dict([(['one', 'two'][i-2], i) for i in (2, 3)]) For operations on dictionaries, see http://docs.python.org/lib/typesmapping.html or use:: >>> help({}) Or:: >>> dir({}) Some of the operations produce the keys, the values, and the items (pairs) in a dictionary. Examples:: ->> d = {'aa': 111, 'bb': 222} ->> d.keys() ['aa', 'bb'] ->> d.values() [111, 222] ->> d.items() [('aa', 111), ('bb', 222)] Exercises: - Write a literal that defines a dictionary using both string literals and variables containing strings. Solution:: >>> first = 'Dave' >>> last = 'Kuhlman' >>> name_dict = {first: last, 'Elvis': 'Presley'} >>> print name_dict {'Dave': 'Kuhlman', 'Elvis': 'Presley'} - Write statements that iterate over (1) the keys, (2) the values, and (3) the items in a dictionary. (Note: Requires introduction of the ``for`` statement.) Solutions:: >>> d = {'aa': 111, 'bb': 222, 'cc': 333} >>> for key in d.keys(): ... print key ... aa cc bb >>> for value in d.values(): ... print value ... 111 333 222 >>> for item in d.items(): ... print item ... ('aa', 111) ('cc', 333) ('bb', 222) >>> for key, value in d.items(): ... print key, '::', value ... aa :: 111 cc :: 333 bb :: 222 Additional notes on dictionaries: - You can use ``iterkeys()``, ``itervalues()``, iteritems()`` to obtain iterators over keys, values, and items. - A dictionary itself is iterable: it iterates over its keys. So, the following two lines are equivalent:: for k in myDict: print k for k in myDict.iterkeys(): print k - The ``in`` operator tests for a key in a dictionary. Example:: In [52]: mydict = {'peach': 'sweet', 'lemon': 'tangy'} In [53]: key = 'peach' In [54]: if key in mydict: ....: print mydict[key] ....: sweet Files ----- Open a file with the ``open`` factory method. Example:: In [28]: f = open('mylog.txt', 'w') In [29]: f.write('message #1\n') In [30]: f.write('message #2\n') In [31]: f.write('message #3\n') In [32]: f.close() In [33]: f = file('mylog.txt', 'r') In [34]: for line in f: ....: print line, ....: message #1 message #2 message #3 In [35]: f.close() Notes: - A file object supports the iterator protocol and, therefore, can be used in a ``for`` statement. - ``open`` is a factory method that creates file objects. Use it to open files for reading, writing, and appending. Examples:: infile = open('myfile.txt', 'r') # open for reading outfile = open('myfile.txt', 'w') # open for (over-) writing log = open('myfile.txt', 'a') # open for appending to existing content - ``file`` is the file type and can be used as a constructor to create file objects. *But*, ``open`` is preferred. - Lines read from a text file have a newline. Strip it off with something like: ``line.rstrip('\n')``. - Learn more about file objects and the methods they provide at: `2.3.9 File Objects `_ (http://docs.python.org/lib/bltin-file-objects.html). You can also append to an existing file. Example:: In [39]: f = file('mylog.txt', 'a') In [40]: f.write('message #4\n') In [41]: f.close() In [42]: f = file('mylog.txt', 'r') In [43]: for line in f: ....: print line, ....: message #1 message #2 message #3 message #4 In [44]: f.close() Exercises: - Read all of the lines of a file into a list. Print the 3rd and 5th lines in the file/list. Solution:: In [55]: f = file('tmp1.txt', 'r') In [56]: lines = f.readlines() In [57]: f.close() In [58]: lines Out[58]: ['the\n', 'big\n', 'brown\n', 'dog\n', 'had\n', 'long\n', 'hair\n'] In [59]: print lines[2] brown In [61]: print lines[4] had More notes: - Strip newlines (and other whitespace) from a string with methods ``strip()``, ``lstrip()``, and ``rstrip()``. Functions and Classes -- A Preview ================================== Structured code -- functions, classes, modules, and packages. First-class objects. Functions Object-oriented programming in Python. Modeling "real world" objects. Classes -- (1) encapsulation; (2) data hiding; (3) inheritance. An overview of the structure of a typical class: (1) methods; (2) the constructor; (3) class (static) variables; (4) super/sub-classes. Statements ========== Assignment ---------- Form -- ``target = expression``. Possible targets: - Identifier - Tuple or list -- Can be nested. Left and right sides must have equivalent structure. Example:: >>> x, y, z = 11, 22, 33 >>> [x, y, z] = 111, 222, 333 >>> a, (b, c) = 11, (22, 33) >>> a, B = 11, (22, 33) This feature can be used to simulate an enum:: In [22]: LITTLE, MEDIUM, LARGE = range(1, 4) In [23]: LITTLE Out[23]: 1 In [24]: MEDIUM Out[24]: 2 - Subscription of a sequence, dictionary, etc. - Attribute reference - A slice of a sequence -- Note that the sequence must be mutable. - Attribute reference -- Example:: >>> class MyClass: ... pass ... >>> anObj = MyClass() >>> anObj.desc = 'pretty' >>> print anObj.desc pretty There is also augmented assignment. Examples:: >>> index = 0 >>> index += 1 >>> index += 5 >>> index += f(x) >>> index -= 1 >>> index *= 3 Things to note: - Assignment creates a new variable (if it does not exist in the namespace) and a binding. Specifically, it binds a value to the new name. Calling a function also does this to the (formal) parameters. - In Python, a language with dynamic typing, the data type is associated with the value, not the variable, as in statically typed languages. - Assignment can also cause sharing of an object. Example:: obj1 = A() obj2 = obj1 Check to determine that the same object is shared with ``id(obj)``. - You can also do multiple assignment in a single statement. Example:: a = b = 3 import ------ Make module available. What ``import`` does: - Evaluate the content of a module. - Likely to create variables in the local (module) namespace. - Evaluation only happens once during a given run of the program. - A module is evaluated from top to bottom. Later statements can replace values created earlier. This is true of functions and classes, as well as (other) variables. - Which statements are evaluated? Assignment, ``class``, ``def``, ... Where ``import`` looks for modules:: - ``sys.path`` shows where it looks. - Packages need a file named ``__init__.py``. - Extensions -- To determine what extensions import looks for, do:: >>> import imp >>> imp.get_suffixes() [('.so', 'rb', 3), ('module.so', 'rb', 3), ('.py', 'U', 1), ('.pyc', 'rb', 2)] Forms of the ``import`` statement: - ``import A`` -- Names in the local (module) namespace are accessible with the dot operator. - ``import A1, A2`` -- Not recommended - ``from A import B`` - ``from A import B1, B2`` - ``from A import B as C`` - ``from A import *`` -- Not recommended: clutters and mixes name-spaces. The import statement and packages -- ``__init__.py``. What is made available when you do ``import aPackage``? The use of ``if __name__ == "__main__":`` -- Makes a module both import-able and executable. Exercises: - Import a module from the standard library, for example ``re``. - Import an element from a module from the standard library, for example import ``compile`` from the ``re`` module. - Create a simple Python package with a single module in it. Solution: 1. Create a directory in the current directory. 2. Create an (empty) ``__init__.py`` in the new directory. 3. Create an ``simple.py`` in the new directory. 4. Add a simple function or class in ``simple.py``. print ----- Arguments to ``print``: - Multiple items -- Separated by commas. - End with comma to suppress carriage return. - Use string formatting for more control over output. - Also see various "pretty-printing" functions and methods, in particular, ``pprint``. See `3.27 pprint -- Data pretty printer `_. String formatting -- Arguments are a tuple. Reference: `2.3.6.2 String Formatting Operations `_. Can also use ``sys.stdout``. Note that a carriage return is *not* automatically added. Example:: >>> import sys >>> sys.stdout.write('hello\n') Controlling the destination and format of print -- Replace ``sys.stdout`` with an instance of any class that implements the method ``write`` taking one parameter. Example:: import sys class Writer: def __init__(self, file_name): self.out_file = file(file_name, 'a') def write(self, msg): self.out_file.write('[[%s]]' % msg) def close(self): self.out_file.close() def test(): writer = Writer('outputfile.txt') save_stdout = sys.stdout sys.stdout = writer print 'hello' print 'goodbye' writer.close() # Show the output. tmp_file = file('outputfile.txt') sys.stdout = save_stdout content = tmp_file.read() tmp_file.close() print content test() There is an alternative form of the ``print`` statement that takes a file-like object, in particular an object that has a ``write`` method. For example:: In [1]: outfile = open('tmp.log', 'w') In [2]: print >> outfile, 'Message #1' In [3]: print >> outfile, 'Message #2' In [4]: print >> outfile, 'Message #3' In [5]: outfile.close() In [6]: In [6]: infile = open('tmp.log', 'r') In [7]: for line in infile: ...: print 'Line:', line.rstrip('\n') ...: Line: Message #1 Line: Message #2 Line: Message #3 In [8]: infile.close() if: elif: else: --------------- Conditions -- Expressions -- Anything that returns a value. Compare with ``eval()`` and ``exec``. Truth values: - False -- ``False``, None, numeric zero, the empty string, an empty list or tuple or dictionary. - True -- ``True`` and everything else. Operators: - ``and`` and ``or`` - ``not`` - ``is`` -- The identical object. Cf. ``a is b`` and ``id(a) == id(b)``. Useful to test for ``None``, for example:: if x is None: ... - ``in`` -- Can be used to test for existence of a key in a dictionary. Example:: ->> d {'aa': 111, 'bb': 222} ->> 'aa' in d True ->> 'xx' in d False Exercises: - Write an ``if`` statement with an ``and`` operator. - Write an ``if`` statement with an ``or`` operator. - Write an ``if`` statement containing both ``and`` and ``or`` operators. try: except: ------------ Exceptions are a systematic and consistent way of processing errors and "unusual" events in Python. Caught and un-caught exceptions. The ``try:`` statement catches an exception. Tracebacks -- Also see the ``traceback`` module: http://docs.python.org/lib/module-traceback.html Exceptions are classes. Exception classes -- Sub-classing, args. An exception class in an ``except:`` clause catches instances of that exception class and all sub-classes, but *not* super-classes. Built-in exception classes -- See: - Module ``exceptions``. - Built-in exceptions -- http://docs.python.org/lib/module-exceptions.html. User defined exception classes -- Sub-classes of ``Exception``. Example:: try: raise RuntimeError('this silly error') except RuntimeError, e: print "[[[%s]]]" % e Reference: http://docs.python.org/lib/module-exceptions.html Why would you define your own exception class? One answer: You want a user of your code to catch your exception and no others. Catching an exception by exception class catches exceptions of that class and all its subclasses. So:: except SomeExceptionClass, e: matches and catches an exception if SomeExceptionClass is the exception class or a base class (superclass) of the exception class. So:: class MyE(ValueError): pass try: raise MyE except ValueError: print 'caught exception' will print "caught exception", because ``ValueError`` is a base class of ``MyE``. Exercises: - Write a *very* simple, empty exception sub-class. Solution:: class MyE(Exception): pass - Write a ``try:except:`` statement that raises your exception and also catches it. Solution:: try: raise MyE('hello there dave') except MyE, e: print e raise ----- Throw or raise an exception. Forms: - ``raise instance`` - ``raise MyExceptionClass, value`` - ``raise MyExceptionClass(value)`` The ``raise`` statement takes: - An instance of class ``Exception`` or - An instance of a built-in sub-class of class ``Exception`` or - An instance of a user-defined sub-class of class ``Exception`` or - One of the above classes and (optionally) a value (for example, a string or a tuple). See http://docs.python.org/ref/raise.html. For a list of built-in exceptions, see http://docs.python.org/lib/module-exceptions.html. The following example defines an exception sub-class and throws an instance of that sub-class. It also shows how to pass and catch multiple arguments to the exception:: class NotsobadError(Exception): pass def test(x): try: if x == 0: raise NotsobadError('a moderately bad error', 'not too bad') except NotsobadError, e: print 'Error args: %s' % (e.args, ) test(0) The following example does a small amount of processing of the arguments:: class NotsobadError(Exception): """An exception class. """ def get_args(self): return '::::'.join(self.args) def test(x): try: if x == 0: raise NotsobadError('a moderately bad error', 'not too bad') except NotsobadError, e: print 'Error args: {{{%s}}}' % (e.get_args(), ) test(0) for --- Iterate over a sequence or an "iterable" object. Form -- ``for x in y:``. Iterator -- Some notes on what it means to be iterable: - An iterable is something that can be used in an iterator context, for example, in a ``for:`` statement, in a list comprehension, and in a generator expression. - Sequences are iterable. Examples: tuples, lists, strings, dictionaries. - Instances of classes that obey the iterator protocol are iterable. See http://docs.python.org/lib/typeiter.html. - We can create an iterator with built-in functions such as ``iter()`` and ``enumerate()``. - Functions that use the ``yield`` statement, produce an iterator. - An iterable implements the iterator interface and satisfies the iterator protocol. The iterator protocol: ``__iter__()`` and ``next()`` methods. See `2.3.5 Iterator Types `_ (http://docs.python.org/lib/typeiter.html). Some ways to produce iterators (see http://docs.python.org/lib/built-in-funcs.html): - ``iter()`` - ``enumerate()`` - ``some_dict.iterkeys()``, ``some_dict.itervalues()``, ``some_dict.iteritems()``. - Use a sequence in an iterator context, for example in a ``for`` statement. Lists, tuples, dictionaries, and strings can be used in an iterator context to produce an iterator. - Generator expressions -- Latest Python only. Syntactically like list comprehensions (surrounded by parens instead of square brackets), but use lazy evaluation. - A class that implements the iterator protocol -- Example:: class A: def __init__(self): self.data = [11,22,33] self.idx = 0 def __iter__(self): return self def next(self): if self.idx < len(self.data): x = self.data[self.idx] self.idx +=1 return x else: raise StopIteration def test(): a = A() for x in a: print x test() Helpful functions with ``for``: - ``enumerate(iterable)`` -- Returns an iterable that produces pairs (tuples) containing count and value. Example:: for count, value in enumerate([11,22,33]): print count, value - ``range([start,] stop[, step])`` and ``xrange([start,] stop[, step])``. List comprehensions revisited -- Since list comprehensions create lists, they are useful in ``for`` statements, although you should consider using a generator expression instead. Two forms: - ``[f(x) for x in iterable]`` - ``[f(x) for x in iterable if t(x)]`` Exercises: - Write a list comprehension that returns all the keys in a dictionary whose associated values are greater than zero. - The dictionary: ``{'aa': 11, 'cc': 33, 'dd': -55, 'bb': 22}`` - Solution: ``[x[0] for x in my_dict.iteritems() if x[1] > 0]`` - Write a list comprehension that produces even integers from 0 to 10. Use a ``for`` statement to iterate over those values. Solution:: for x in [y for y in range(10) if y % 2 == 0]: print 'x: %s' % x But, note that in the previous exercise, a generator expression would be better. A generator expression is like a list comprehension, except that, instead of creating the entire list, it produces a generator that can be used to produce all the elements. while ----- Form:: while condition: block Exercises: - Write a ``while`` statement that prints integers from zero to 5. Solution:: count = 0 while count < 5: count += 1 print count The ``break`` and ``continue`` statements are often useful in a ``while`` statement. continue and break ------------------ The ``break`` statement exits from a loop. The ``continue`` statement causes execution to immediately continue at the start of the loop. Can be used in ``for`` and ``while``. Exercises: - Using ``break``, write a ``while`` statement that prints integers from zero to 5. Solution:: count = 0 while True: count += 1 if count > 5: break print count - Using ``continue``, write a ``while`` statement that processes only even integers from 0 to 10. Note: ``%`` is the modulo operator. Solution:: count = 0 while count < 10: count += 1 if count % 2 == 0: continue print count del --- Removes names from namespace. If name is listed in a ``global`` statement, then ``del`` removes name from the global namespace. Names can be a (nested) list. Examples:: >>> del a >>> del a, b, c We can also delete items of a list or dictionary. Examples:: In [9]:d = {'aa': 111, 'bb': 222, 'cc': 333} In [10]:print d {'aa': 111, 'cc': 333, 'bb': 222} In [11]:del d['bb'] In [12]:print d {'aa': 111, 'cc': 333} In [13]: In [13]:a = [111, 222, 333, 444] In [14]:print a [111, 222, 333, 444] In [15]:del a[1] In [16]:print a [111, 333, 444] And, we can delete an attribute from an instance. Example:: In [17]:class A: ....: pass ....: In [18]:a = A() In [19]:a.x = 123 In [20]:dir(a) Out[20]:['__doc__', '__module__', 'x'] In [21]:print a.x 123 In [22]:del a.x In [23]:dir(a) Out[23]:['__doc__', '__module__'] In [24]:print a.x ---------------------------------------------- exceptions.AttributeError Traceback (most recent call last) /home/dkuhlman/a1/Python/Test/ AttributeError: A instance has no attribute 'x' Functions, Modules, Packages, and Debugging =========================================== Functions --------- The def statement ~~~~~~~~~~~~~~~~~ The ``def`` statement is used to define functions and methods. The ``def`` statement is evaluated. It produces a function/method (object) and binds it to a variable in the current name-space. Returning values ~~~~~~~~~~~~~~~~ The ``return`` statement is used to return values from a function. The ``return`` statement takes zero or more values (separated by commas). The default value is ``None``. To return multiple values, use an expression list. Don't forget that (assignment) unpacking can be used to capture multiple values. Example:: In [8]: def test(x, y): ...: return x * 3, y * 4 ...: In [9]: a, b = test(3, 4) In [10]: print a 9 In [11]: print b 16 Arguments ~~~~~~~~~ Default values -- Example:: In [53]: def t(max=5): ....: for val in range(max): ....: print val ....: ....: In [54]: t(3) 0 1 2 In [55]: t() 0 1 2 3 4 Note: If a function has an argument with a default value, then all "normal" arguments must proceed the arguments with default values. More completely, arguments must be given from left to right in the following order: 1. Normal arguments. 2. Arguments with default values. 3. Argument list (``*args``). 4. Keyword arguments (``**kwargs``). List arguments -- ``*args``. It's a tuple. Keyword arguments and default values -- ``**kwargs``. It's a dictionary. Passing lists to a function as multiple arguments -- ``some_func(*aList)``. Effectively, this syntax causes Python to unroll the arguments. Return values: - You can return any value (or list of values) from a function. - The default return value, if no return statement is executed, is None Local variables: - Creating local variables. Contrast with accessing a variable. - Variable look-up. - The ``global`` statement -- Must use ``global`` when we want to set the value of a global variable. Things to know about functions: - Functions are first-class -- You can store them in a structure, pass them to a function, and return them from a function. - Function calls can take keyword arguments. Example:: >>> test(size=25) - Formal arguments to a function can have default values. Example:: >>> def test(size=0): ... - You can "capture" remaining arguments with ``*args``, and ``**kwargs``. Example:: In [13]: def test(size, *args, **kwargs): ....: print size ....: print args ....: print kwargs ....: ....: In [14]: test(32, 'aa', 'bb', otherparam='xyz') 32 ('aa', 'bb') {'otherparam': 'xyz'} - A function that does not explicitly return a value, returns ``None``. - In order to *set* the value of a global variable, declare the variable with ``global``. Exercises: - Write a function that takes a single argument, prints the value of the argument, and returns the argument as a string. Solution:: >>> def t(x): ... print 'x: %s' % x ... return '[[%s]]' % x ... >>> t(3) x: 3 '[[3]]' - Write a function that takes a variable number of arguments and prints them all. Solution:: >>> def t(*args): ... for arg in args: ... print 'arg: %s' % arg ... >>> t('aa', 'bb', 'cc') arg: aa arg: bb arg: cc - Write a function that prints the names and values of keyword arguments passed to it. Solution:: >>> def t(**kwargs): ... for key in kwargs.keys(): ... print 'key: %s value: %s' % (key, kwargs[key], ) ... >>> t(arg1=11, arg2=22) key: arg1 value: 11 key: arg2 value: 22 Global variables and the global statement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, assignment in a function or method creates local variables. Reference (not assignment) to a variable, accesses a local variable if it has already been created, else accesses a global variable. In order to assign a value to a global variable, declare the variable as global at the beginning of the function or method. If in a function or method, you both reference and assign to a variable, then you must either: 1. Assign to the variable first, or 2. Declare the variable as global. The ``global`` statement declares one or more variables, separated by commas, to be global. Some examples:: ->> X = 3 />> def t(): |.. print X \__ ->> t() 3 # # No effect on global X. />> def s(): |.. X = 4 \__ ->> s() ->> t() 3 />> def u(): |.. global X |.. X = 5 \__ ->> u() ->> t() 5 # # Error # Must assign value before reference or declare as global. />> def v(): |.. x = X |.. X = 6 |.. return x \__ ->> v() Traceback (most recent call last): File "", line 2, in ? File "", line 3, in v UnboundLocalError: local variable 'X' referenced before assignment # # This time, declare X as global. />> def w(): |.. global X |.. x = X |.. X = 7 |.. return x \__ ->> w() 5 ->> X 7 Doc strings for functions ~~~~~~~~~~~~~~~~~~~~~~~~~ Add docstrings as a triple-quoted string beginning with the first line of a function or method. See `epydoc`_ for a suggested format. lambda ------ Use a lambda, as a convenience, when you need a function that both: - is anonymous and - contains only an expression and no statements. Suggestion: In some cases, a lambda may be useful as an event handler. Example:: class Test: def __init__(self, first='', last=''): self.first = first self.last = last def test(self, formatter): """ Test for lambdas. formatter is a function taking 2 arguments, first and last names. It should return the formatted name. """ msg = 'My name is %s' % (formatter(self.first, self.last),) print msg def test(): t = Test('Dave', 'Kuhlman') t.test(lambda first, last: '%s %s' % (first, last, )) t.test(lambda first, last: '%s, %s' % (last, first, )) test() Reference: http://docs.python.org/ref/lambdas.html Iterators and generators ------------------------ Concepts: iterator And iterator is something that satisfies the iterator protocol. generator A generator is a class or function that implements an iterator, i.e. that implements the iterator protocol. the iterator protocol An object satisfies the iterator protocol if it does the following: - It implements a ``__iter__`` method, which returns an iterator object. - It implements a ``next`` function, which returns the next item from the collection, sequence, stream, etc of items to be iterated over - It raises the ``StopIteration`` exception when the items are exhausted and the ``next()`` method is called. For more information on iterators, see `the section on iterator types in the Python Library Reference `_. A function or method containing a ``yield`` statement implements a generator. Adding the ``yield`` statement to a function or method turns that function or method into one which, when called, returns a generator, i.e. an object that implements the iterator protocol. An instance of a class which implements the ``__iter__`` method, returning an iterator, is iterable. For example, it can be used in a ``for`` statement or in a list comprehension, or in a generator expression, or as an argument to the ``iter()`` built-in method. But, notice that the class most likely implements a generator method which can be called directly. Examples -- The following code implements an iterator that produces all the objects in a tree of objects:: class Node: def __init__(self, data, children=None): self.initlevel = 0 self.data = data if children is None: self.children = [] else: self.children = children def set_initlevel(self, initlevel): self.initlevel = initlevel def get_initlevel(self): return self.initlevel def addchild(self, child): self.children.append(child) def get_data(self): return self.data def get_children(self): return self.children def show_tree(self, level): self.show_level(level) print 'data: %s' % (self.data, ) for child in self.children: child.show_tree(level + 1) def show_level(self, level): print ' ' * level, # # Generator method #1 # This generator turns instances of this class into iterable objects. # def walk_tree(self, level): yield (level, self, ) for child in self.get_children(): for level1, tree1 in child.walk_tree(level+1): yield level1, tree1 def __iter__(self): return self.walk_tree(self.initlevel) # # Generator method #2 # This generator uses a support function (walk_list) which calls # this function to recursively walk the tree. # If effect, this iterates over the support function, which # iterates over this function. # def walk_tree(tree, level): yield (level, tree) for child in walk_list(tree.get_children(), level+1): yield child def walk_list(trees, level): for tree in trees: for tree in walk_tree(tree, level): yield tree # # Generator method #3 # This generator is like method #2, but calls itself (as an iterator), # rather than calling a support function. # def walk_tree_recur(tree, level): yield (level, tree,) for child in tree.get_children(): for level1, tree1 in walk_tree_recur(child, level+1): yield (level1, tree1, ) def show_level(level): print ' ' * level, def test(): a7 = Node('777') a6 = Node('666') a5 = Node('555') a4 = Node('444') a3 = Node('333', [a4, a5]) a2 = Node('222', [a6, a7]) a1 = Node('111', [a2, a3]) initLevel = 2 a1.show_tree(initLevel) print '=' * 40 for level, item in walk_tree(a1, initLevel): show_level(level) print 'item:', item.get_data() print '=' * 40 for level, item in walk_tree_recur(a1, initLevel): show_level(level) print 'item:', item.get_data() print '=' * 40 a1.set_initlevel(initLevel) for level, item in a1: show_level(level) print 'item:', item.get_data() iter1 = iter(a1) print iter1 print iter1.next() print iter1.next() print iter1.next() print iter1.next() print iter1.next() print iter1.next() print iter1.next() ## print iter1.next() return a1 if __name__ == '__main__': test() Notes: - An instance of class ``Node`` is "iterable". It can be used directly in a ``for`` statement, a list comprehension, etc. So, for example, when an instance of ``Node`` is used in a ``for`` statement, it produces an iterator. - We could also call the ``Node.walk_method`` directly to obtain an iterator. - Method ``Node.walk_tree`` and functions ``walk_tree`` and ``walk_tree_recur`` are generators. When called, they return an iterator. They do this because they each contain a ``yield`` statement. - These methods/functions are recursive. They call themselves. Since they are generators, they must call themselves in a context that uses an iterator, for example in a ``for`` statement. Modules ------- A module is a Python source code file. A module can be imported. A module can be run. To make a module both import-able and run-able, use the following idiom (at the end of the module):: def main(): o o o if __name__ == '__main__': main() Doc strings for modules ~~~~~~~~~~~~~~~~~~~~~~~ Add docstrings as a triple-quoted string at or near the top of the file. See `epydoc`_ for a suggested format. Packages -------- A package is a directory on the file system which contains a file named ``__init__.py``. The ``__init__.py`` file: - Why is it there? -- It makes modules in the directory "import-able". - Can ``__init__.py`` be empty? -- Yes. Or, just include a comment. - When is it evaluated? -- It is evaluated the first time that an application imports anything from that directory/package. - What can you do with it? -- Any code that should be executed exactly once and during import. For example: - Perform initialization needed by the package. - Make variables, functions, classes, etc available. For example, when the package is imported rather than modules in the package. You can also expose objects defined in modules contained in the package. - Define a variable named ``__all__`` to specify the list of names that will be imported by ``from my_package import *``. For example, if the following is present in ``my_package/__init__.py``:: __all__ = ['func1', 'func2',] Then, ``from my_package import *`` will import ``func1`` and ``func2``, but not other names defined in ``my_package``. Note that ``__all__`` can be used at the module level, as well as at the package level. Debugging tools --------------- ``pdb`` -- The Python debugger: - Start the debugger by running an expression:: pdb.run('expression') Example:: if __name__ == '__main__': import pdb pdb.run('main()') - Start up the debugger at a specific location with the following:: import pdb; pdb.set_trace() Example:: if __name__ == '__main__': import pdb pdb.set_trace() main() - Get help from within the debugger. For example:: (Pdb) help (Pdb) help next Can also embed IPython into your code. See http://ipython.scipy.org/doc/manual/manual.html. Inspecting: - ``import inspect`` - See http://docs.python.org/lib/module-inspect.html. - Don't forget to try ``dir(obj)``, first. Miscellaneous tools: - ``id(obj)`` - ``globals()``, ``locals()``. - ``dir(obj)`` -- Returns interesting names, but list is not necessarily complete. - ``obj.__class__`` - ``cls.__bases__`` - ``obj.__class__.__bases__`` - ``obj.__doc__`` - Customize the representation of your class. Define the following methods in your class: - ``__repr__()`` -- Called by (1) ``repr()``, (2) interactive interpreter when representation is needed. - ``__str__()`` -- Called by (1) ``str()``, (2) string formatting. Classes ======= Classes model the behavior of objects in the "real" world. Methods implement the behaviors of these types of objects. Member variables hold (current) state. A simple class -------------- :: In [104]: class A: .....: pass .....: In [105]: a = A() Defining methods ---------------- A method is a function defined in class scope and with first parameter ``self``:: In [106]: class B: .....: def show(self): .....: print 'hello from B' .....: In [107]: b = B() In [108]: b.show() hello from B The constructor --------------- The constructor is a method named ``__init__``. Exercise: Define a class with a member variable ``name`` and a ``show`` method. Use ``print`` to show the name. Solution:: In [109]: class A: .....: def __init__(self, name): .....: self.name = name .....: def show(self): .....: print 'name: "%s"' % self.name .....: In [111]: a = A('dave') In [112]: a.show() name: "dave" Notes: - The ``self`` variable is explicit. Member variables ---------------- Defining member variables -- Member variables are created with assignment. Example:: class A: def __init__(self, name): self.name = name A small gotcha -- Do this:: In [28]: class A: ....: def __init__(self, items=None): ....: if items is None: ....: self.items = [] ....: else: ....: self.items = items Do *not* do this:: In [29]: class B: ....: def __init__(self, items=[]): # wrong. list ctor evaluated only once. ....: self.items = items In the second example, the ``def`` statement and the list constructor are evaluated only once. Therefore, the item member variable of all instances of class B, will share the same value, which is most likely *not* what you want. Methods ------- Defining methods Calling methods: - Use the instance and the dot operator. - Calling a method defined in the same class or a super-class. Same class: use self. Super-class: use the class (name). - Calling a method defined in a specific super-class. Adding inheritance ------------------ Referencing super-classes -- Use the name of the super-class, for example:: In [39]: class B(A): ....: def __init__(self, name, size): ....: A.__init__(self, name) ....: self.self = size Calling the constructor of the super-class. You can also use multiple inheritance. Example:: class C(A, B): ... Python searches super-classes in left-to-right depth-first order. For more information on inheritance, see the tutorial in the standard Python documentation set: `9.5 Inheritance `_ and `9.5.1 Multiple Inheritance `_. Watch out for problems with inheriting from classes that have a common base class. Class variables --------------- - Also called static data. - Define at class level with assignment. Example:: class A: size = 5 def get_size(self): return A.size - Reference with ``classname.variable``. - Caution: ``self.variable = x`` creates a new member variable. Class methods ------------- - Also called static methods. - For new-style classes, use ``staticmethod``. Example:: class B: def dup_string(x): s1 = '%s%s' % (x, x,) return s1 dup_string = staticmethod(dup_string) B.dup_string('abcd') 'abcdabcd' For more on new-style classes, see: http://www.python.org/doc/newstyle.html. An alternative way to implement static methods (without new-style classes). Use a "plain", module-level function. For example:: >>> class A: ... count = 0 ... >>> def inc_count(): ... A.count = A.count + 1 ... >>> def dec_count(): ... A.count = A.count - 1 ... >>> a = A() >>> a.count 0 >>> inc_count() >>> a.count 1 >>> b = A() >>> b.count 1 >>> inc_count() >>> inc_count() >>> inc_count() >>> a.count 4 >>> b.count 4 Or, with a newer version of Python:: />> def inc_count(): |.. A.count += 1 \__ />> def dec_count(): |.. A.count -= 1 \__ />> class A: |.. count = 0 |.. def get_count(self): |.. return A.count \__ ->> ->> a = A() ->> a.get_count() 0 ->> inc_count() ->> inc_count() ->> a.get_count() 2 ->> b = A() ->> b.get_count() 2 Interfaces ---------- In Python, to implement an interface is to implement a method with a specific name and a specific arguments. One way to define an "interface" is to define a class containing methods that have a header and a doc string but no implementation. Additional notes on interfaces: - Interfaces are not enforced. - A class does not have to implement *all* of an interface. New-style classes ----------------- A new-style class is one that sub-class ``object`` or a class that sub-classes ``object``. You can sub-class Python's built-in datatypes. - A simple example -- the following class extends the list datatype:: class C(list): def get_len(self): return len(self) c = C((11,22,33)) c.get_len() c = C((11,22,33,44,55,66,77,88)) print c.get_len() # Prints "8". - A slightly more complex example -- the following class extends the dictionary datatype:: class D(dict): def __init__(self, data=None, name='no_name'): if data is None: data = {} dict.__init__(self, data) self.name = name def get_len(self): return len(self) def get_keys(self): content = [] for key in self: content.append(key) contentstr = ', '.join(content) return contentstr def get_name(self): return self.name def test(): d = D({'aa': 111, 'bb':222, 'cc':333}) # Prints "3" print d.get_len() # Prints "'aa, cc, bb'" print d.get_keys() # Prints "no_name" print d.get_name() Some things to remember about new-style classes: - In order to be new-style, a class must inherit (directly or indirectly) from ``object``. Note that if you inherit from a built-in type, you get this automatically. - New-style classes unify types and classes. - You can sub-class (built-in) types such as dict, str, list, file, etc. - The built-in types now provide factory functions: ``dict()``, ``str()``, ``int()``, ``file()``, etc. - The built-in types are introspect-able -- Use ``x.__class__``, ``dir(x.__class__)``, ``isinstance(x, list)``, etc. - New-style classes give you properties and descriptors. (Must be a new-style class.) - New-style classes enable you to define static methods. Actually, all classes enable you to do this. For more on new-style classes, see: http://www.python.org/doc/newstyle.html. Exercises: - Write a class and a sub-class of this class. - Give the super-class one member variable, a name, which can be entered when an instance is constructed. - Give the sub-class one member variable, a description; the sub-class constructor should allow entry of both name and description. - Put a ``show()`` method in the super-class and override the ``show()`` method in the sub-class. Solution:: class A: def __init__(self, name): self.name = name def show(self): print 'name: %s' % (self.name, ) class B(A): def __init__(self, name, desc): A.__init__(self, name) self.desc = desc def show(self): A.show(self) print 'desc: %s' % (self.desc, ) Doc strings for functions ------------------------- Add docstrings as a triple-quoted string beginning with the first line of a class. See `epydoc`_ for a suggested format. .. _`epydoc`: http://epydoc.sourceforge.net/ Special Tasks ============= File input and output ---------------------- Create a file object. Use ``file()`` (or ``open()`` in Jython prior to Jython 2.2). This example reads and prints each line of a file:: def test(): f = file('tmp.py', 'r') for line in f: print 'line:', line.rstrip() f.close() test() Notes: - A text file is an iterable. It iterates over the lines in a file. The following is a common idiom:: infile = file(filename, 'r') for line in infile: process_a_line(line) infile.close() - ``string.rstrip()`` strips new-line and other whitespace from the right side of each line. To strip new-lines only, but not other whitespace, try ``rstrip('\n')``. - Other ways of reading from a file/stream object: ``my_file.read()``, ``my_file.readline()``, ``my_file.readlines()``, This example writes lines of text to a file:: def test(): f = file('tmp.txt', 'w') for ch in 'abcdefg': f.write(ch * 10) f.write('\n') f.close() test() Notes: - The ``write`` method, unlike the ``print`` statement, does not automatically add new-line characters. - Must close file in order to flush output. Or, use ``my_file.flush()``. Unit tests ---------- For more information, see `5.3 unittest -- Unit testing framework `_. Here is a simple example:: #!/usr/bin/env python import sys, popen2 import getopt import unittest class GenTest(unittest.TestCase): def test_1_generate(self): cmd = 'python ../generateDS.py -f -o out2sup.py -s out2sub.py people.xsd' outfile, infile = popen2.popen2(cmd) result = outfile.read() outfile.close() infile.close() self.failUnless(len(result) == 0) def test_2_compare_superclasses(self): cmd = 'diff out1sup.py out2sup.py' outfile, infile = popen2.popen2(cmd) outfile, infile = popen2.popen2(cmd) result = outfile.read() outfile.close() infile.close() #print 'len(result):', len(result) # Ignore the differing lines containing the date/time. #self.failUnless(len(result) < 130 and result.find('Generated') > -1) self.failUnless(check_result(result)) def test_3_compare_subclasses(self): cmd = 'diff out1sub.py out2sub.py' outfile, infile = popen2.popen2(cmd) outfile, infile = popen2.popen2(cmd) result = outfile.read() outfile.close() infile.close() # Ignore the differing lines containing the date/time. #self.failUnless(len(result) < 130 and result.find('Generated') > -1) self.failUnless(check_result(result)) def check_result(result): flag1 = 0 flag2 = 0 lines = result.split('\n') len1 = len(lines) if len1 <= 5: flag1 = 1 s1 = '\n'.join(lines[:4]) if s1.find('Generated') > -1: flag2 = 1 return flag1 and flag2 # Make the test suite. def suite(): # The following is obsolete. See Lib/unittest.py. #return unittest.makeSuite(GenTest) loader = unittest.TestLoader() # or alternatively # loader = unittest.defaultTestLoader testsuite = loader.loadTestsFromTestCase(GenTest) return testsuite # Make the test suite and run the tests. def test(): testsuite = suite() runner = unittest.TextTestRunner(sys.stdout, verbosity=2) runner.run(testsuite) USAGE_TEXT = """ Usage: python test.py [options] Options: -h, --help Display this help message. Example: python test.py """ def usage(): print USAGE_TEXT sys.exit(-1) def main(): args = sys.argv[1:] try: opts, args = getopt.getopt(args, 'h', ['help']) except: usage() relink = 1 for opt, val in opts: if opt in ('-h', '--help'): usage() if len(args) != 0: usage() test() if __name__ == '__main__': main() #import pdb #pdb.run('main()') Notes: - ``GenTest`` is our test suite class. It inherits from ``unittest.TestCase``. - Each method in ``GenTest`` whose name begins with "test" will be run as a test. - The tests are run in alphabetic order by method name. - Defaults in class ``TestLoader`` for the test name prefix and sort comparison function can be overridden. See `5.3.8 TestLoader Objects `_. - A test case class may also implement methods named ``setUp()`` and ``tearDown()`` to be run before and after tests. See `5.3.5 TestCase Objects `_. Actually, the first test method in our example should, perhaps, be a ``setUp()`` method. - The tests use calls such as ``self.failUnless()`` to report errors. These are inherited from class ``TestCase``. See `5.3.5 TestCase Objects `_. - Function ``suite()`` creates an instance of the test suite. - Function ``test()`` runs the tests. Why should we use unit tests? Many reasons, including: - Without unit tests, corner cases may not be checked. This is especially important, since Python does relatively little compile time error checking. - Unit tests facilitate a frequent and short design and implement and release development cycle. See `ONLamp.com -- Extreme Python `_ and `What is XP `_. - Designing the tests before writing the code is "a good idea". doctest ------- For simple test harnesses, consider using ``doctest``. With ``doctest`` you can (1) run a test at the Python interactive prompt, then (2) copy and paste that test into a doc string in your module, and then (3) run the tests automatically from within your module under ``doctest``. There are examples and explanation in the standard Python documentation set: `5.2 doctest -- Test interactive Python examples `_. A simple way to use ``doctest`` in your module: 1. Run several tests in the Python interactive interpreter. Note that because ``doctest`` looks for the interpreter's ">>>" prompt, you must use the standard interpreter, and not, for example, IPython. Also, make sure that you include a line with the ">>>" prompt after each set of results; this enables ``doctest`` to determine the extent of the test results. 2. Use copy and paste, to insert the tests and their results from your interactive session into the docstrings. 3. Add the following code at the bottom of your module:: def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() Installing Python packages -------------------------- Simple:: $ python setup.py build $ python setup.py install # as root More complex: - Look for a ``README`` or ``INSTALL`` file at the root of the package. - Type the following for help:: $ python setup.py cmd --help $ python setup.py --help-commands $ python setup.py --help [cmd1 cmd2 ...] - And, for even more details, see `Installing Python Modules `_. More Python Features and Exercises ================================== [As time permits, explain more features and do more exercises as requested by class members.]