Chapter 2. Introduction to Python

This chapter covers

  • The Python datatypes

  • Basic language constructs

  • Exception handling, list comprehensions, and closures

  • Python project structure: modules and packages

  • The Python standard library

Programming is a craft. Knowing the rules of a programming language is the science behind the art of creating well-structured and elegant programs. Your programs may be small and functional, or they may be vast cathedrals adorned with the intricate masonry of recursive algorithms and data structures. The joy of programming is found in the process of creation as much as in the final result.

In the first chapter, you had a taste of Python, which is essential to programming with IronPython; luckily, Python is easy to learn. This chapter is a fast-paced Python tutorial that will give you a great foundation in Python the language, both for following the examples in this book and for creating your own applications.

This chapter does assume some experience in programming, but not in any particular language, so it should be easy to follow. Even if you’re already fluent in Python, you should skim this chapter because there are interesting details that are specific to IronPython.[1] If you’re not already a fluent Python speaker, the goal of this chapter is for you to learn enough Python so that you can confidently tackle creating a well-structured IronPython application in the coming chapters.

Learning a programming language is itself like a work of construction—say, building a house. The basic datatypes, like mortar, bind programs together, and they get everywhere! Next come the larger elements—the bricks and blocks and supporting beams; in Python, these are the functions, classes, and modules that give your program shape and structure. You need to understand these things before moving on to the wiring, the floorboards, and the thousand-and-one other details. (It’s no wonder they call it software architecture!) This order is roughly the one that the tutorial will follow. We won’t get to all the decorations and furnishings; we’ll leave you with plenty more to learn.

Before you dive in, it’s a good idea to have a good programmer’s editor on hand, such as a text editor or an IDE that supports Python.

A proper IDE will give you additional features such as autocomplete, source code navigation, smart indent, project browsing, and a whole lot more. Some programmers get by without these features and stick to using a text editor (my boss included), but I (Michael) find that the tools provided by a good IDE are invaluable. My personal favorite IDE for Python is a commercial one called Wing,[2] shown in figure 2.1. The autocomplete is the most accurate[3] I’ve seen in a Python editor. Plenty of other good Python IDEs, both free and commercial, are available.

Wing IDE, with its built-in project browser and interactive interpreter, is a great IDE for Python.

Figure 2.1. Wing IDE, with its built-in project browser and interactive interpreter, is a great IDE for Python.

An overview of Python

As we mentioned earlier, Python supports several different programming techniques. For simple tasks, which there may be many of in a complex program, functions can be more appropriate than making everything a class. Functional programming can make it hard for others to follow your code if it’s overused, but it can also be extremely powerful. With Python, you’re free to mix procedural, functional, and object-oriented programming to your heart’s delight; and, because they all have their place, you probably will. First things first, though—to do this, you need to know the language.

The core object model of Python is similar to imperative languages such as C#, Java, and VB.NET.[4] If you’ve used any of these languages before, even if aspects of the syntax are unfamiliar, you’ll find learning Python easy. Python does differ from these languages in how it delimits blocks of code. Instead of using curly braces, Python uses indentation to mark blocks of code. Here’s a simple example using an if block:

if condition == True:
   do_something()

The statements to be executed if the condition is True are indented relative to the if statement. You can use any indentation you want (tabs or two spaces or four spaces) as long as you’re consistent (and never mix tabs and spaces in the same file). Because we use a lot of Python code in this chapter, you’ll be seeing a lot more of Python’s indentation rules.

Note

Two simple things to know about Python: Python is case sensitive, and the comment symbol is #.

In object-oriented programming languages, everything is an object. Every object has a type, and the simplest (conceptually) of these are the ones built into Python, such as strings and numbers. These are the basic datatypes; and, as the mortar of the house we’re constructing, we look at them first. Different kinds of Python objects are shown in figure 2.2 and are arranged in layers from the most simple to the more complex.

This Python object pyramid shows some of the Python types. The layers are arranged in approximate order of complexity (from top to bottom).

Figure 2.2. This Python object pyramid shows some of the Python types. The layers are arranged in approximate order of complexity (from top to bottom).

In terms of the basic datatypes, the creators of IronPython have gone to great lengths to ensure that IronPython is a faithful implementation of Python. The structure you learn about here is just as true for IronPython as it is for the regular Python distribution, CPython.

Python datatypes

Python boasts a rich set of built-in datatypes that you can use without importing any modules. Most of these, with the exception of the set that we introduce shortly, have syntax for including them within your code. To understand the examples in this book or to write Python yourself, you’re going to need to recognize them. Table 2.1 contains a guide to all the basic datatypes and their syntaxes.

Table 2.1. The built-in datatypes and their syntaxes

Datatype

Type

Syntax examples

Notes

Byte string

str

'hello world'
"also hello world"
"""A triple quoted
multiline string"""
'''Another triple
quoted multiline string'''

In IronPython, byte strings and Unicode strings are the same datatype.

Unicode string

unicode

u'A Unicode string'
u"Another Unicode string"
u"""Yet another Unicode
string."""
 

Integer

int

3
-3
 

Long integer

long

9L
9999999999999999999999L
 

Floating point

float

0.0
-3.1
2e100
2.765e-10
 

Complex numbers

complex

2 + 3j
8j
 

List

list

[]
[1, 2, 3, 4]
[1, "a", 3.0]

An empty list.

A populated list.

A list containing members of different types.

Tuple

tuple

()
(1,)
(1, 2, 3, 4)

An empty tuple.

Tuple with a single member.[a]

  

1, 2, 3, 4

Although tuples are usually created with parentheses, they’re only needed to disambiguate in situations where commas have other significance (such as argument lists in function calls). It’s the commas that are significant in creating a tuple.

Dictionary

dict

{}
{'key': 'value',
'key2': 'value2'}

An empty dictionary.

A populated dictionary.

Set

set

set()
set([1, 2, 3, 4])

Creates an empty set.

Creates a set from a list.

None

NoneType

None

Equivalent of NULL in other languages.

Boolean

bool

True
False
 

[a] That trailing comma is important! In the case of a tuple with only one member, it’s needed to disambiguate a tuple from an ordinary expression surrounded by parentheses.

This table isn’t exhaustive. For example, it doesn’t include things like the slightly exotic frozenset or the built-in exceptions.

In addition to the information in the table, you need to know some additional things about these datatypes.

Strings

You include Python string literals in your code with quotes. You can use single quotes or double quotes—there’s no difference. If you want to include literals with newline characters, you can use triple-quoted strings.

You can also use normal Unix-type escape characters.[5] For example, here’s a string with a hard tab separating the two words and terminated with a newline character:

'hello	world
'

The standard string type is often referred to as the byte string; the contents are stored internally as a sequence of bytes representing the characters. Byte strings can also be used for storing binary data.

Python has a second string type, the Unicode string. You create a Unicode string literal by prefixing your string with a u.

u'This is a Unicode string'

Note

Because IronPython is built on top of .NET, the Unicode and string types are the same type. This is better than having two string types (and is the position that Python will be in by Python 3.0), and you should have less encoding-related problems with IronPython than you would with CPython.

Both str and unicode strings have a common base class: basestring. If you want to check if an object is a string and you don’t care which type of string, you can use isinstance(someObject, basestring). This isn’t important for IronPython, but can be useful for compatibility with CPython.

You may stumble across a few other syntax permutations for strings. For example, by prefixing a string with an r, you make it a raw string. Backslashes aren’t interpreted as escape characters in raw strings.[6] Raw strings are especially useful for regular expressions where backslashes have syntactic meaning and having to continually escape them makes regular expressions less readable.

All the built-in types (except for None and tuples) have a large number of methods that do useful things. Strings are no exception; they have methods for trimming whitespace, joining strings, checking if they contain substrings, and much more. For a list of the Python methods available on strings, visit http://docs.python.org/lib/string-methods.html.

If you’re a .NET developer, then you’ll already be familiar with the .NET methods available on strings.

The CLR Module and IronPython Types

One thing that makes working with IronPython so straightforward is that the basic datatypes are both Python objects and .NET objects. An IronPython string is also a .NET string—which is clever magic. By default the .NET methods aren’t available on the basic types such as strings.

>>> string = 'Hello World'
>>> string.ToUpper()
Traceback (most recent call last):
AttributeError: 'str' object has no attribute 'ToUpper'

The IronPython developers made this decision after much debate; in the end, they felt that leaving extra attributes on Python objects wouldn’t be a faithful implementation of Python. You can enable the .NET methods by importing the clr module.

>>> import clr
>>> string.ToUpper()
'HELLO WORLD'

Note

The clr module provides functions for interacting with the underlying .NET runtime. It’s the basic entry point for .NET/IronPython interoperation, and we use it a great deal throughout the book.

You can easily determine that IronPython strings are also .NET strings. If you compare the Python str type with the .NET System.String type, you’ll see that they are, in fact, the same object.

>>> import System
>>> System.String
<type 'str'>
>>> System.String is str
True

When using the basic types, you can choose whether to use .NET patterns or Python patterns of programming from within IronPython code. As we go through the book, you’ll find that we also have this choice when dealing with higher level concepts such as working with files or threading.

We’ve looked at strings; now let’s look at the next most common objects—numbers.

Numbers

Python has four types for representing numbers: integers, long integers, floating point numbers, and complex numbers.

Integers are for representing whole numbers, positive or negative, up to sys.maxint,[7] which is a value that depends on the underlying platform. Long integers are used to represent integers that are greater than this number. Internally, Python promotes large numbers into longs automatically; in practice, you’ll rarely care whether a number is an int or a long. With Python long integers, the maximum number you can represent is only limited by the amount of memory you have. That’s likely to be quite a big number.

You can mix operations involving integers and floats; the result will always be a float.

If you divide two integers, you’ll get an integer back. This can be surprising if you aren’t expecting it. If you need to get the real value of a division back, then make sure that one of the values is a float.

>>> 9/2
4
>>> 9/2.0
4.5

Python also has syntax for complex numbers, which are unusual, but handy for certain kinds of math. Having said that, I don’t think I’ve ever needed to use them.

Numbers and strings are fine for representing individual values, but often you need to store more complicated information, which can involve creating nested data structures. To do this, you need datatypes capable of containing values—the container datatypes. The first of these is the list.

Lists

The list is a very flexible datatype. You can store objects of any type in lists, and they grow themselves in memory—you don’t need to specify a size for them. You can use them with the minimum of boilerplate code.

Note

As the name implies, lists are used for storing multiple objects. The objects are stored in a sequence, one after another, and you access them by their positions in the list. In Python terminology, the objects contained in a list (or other containers) are usually referred to as members. This is slightly different than the way .NET terminology uses member, which corresponds more closely to the Python term attribute.

The list is the archetypal sequence type in Python. You access the members of a list by position, a process called indexing; the first member is at position zero. You can also access members starting from the end, by using negative numbers.

>>> a = [1, 2, 3, 4]
>>> a[0]
1
>>> a[-1]
4

Attempting to access a member outside the bounds of the list will raise an IndexError.

>>> a = [0, 1, 2, 3]
>>> a[4]
Traceback (most recent call last):
IndexError: index out of range: 4
>>> a[-5]
Traceback (most recent call last):
IndexError: index out of range: -5

Assigning to members of a list uses the same syntax.

>>> a = [1, 2, 3, 4]
>>> a[0] = 'a'
>>> a
['a', 2, 3, 4]
>>> a[-1] = 'b'
>>> a
['a', 2, 3, 'b']

Deleting members uses the del keyword.

>>> del a[0]

Because you can modify a list by changing members or adding and removing members, it is a mutable datatype. The list has a close cousin that’s immutable: the tuple.

Tuples

The tuple is a container object similar to a list. Tuples can be indexed in the same way as lists; but, because they’re immutable, you can’t assign to members. Once a tuple has been created, you can’t add or remove members. Because of this restriction, tuples are more efficient (both in memory use and speed) than lists. The fact that they’re immutable also means that they can be used as dictionary keys—which is handy.

By convention in Python, tuples are used when the elements are heterogeneous (different types of objects); lists are used for homogeneous collections. This is a distinction observed more in theory than in practice.

Tuples often appear implicitly in Python. For example, when a function returns multiple values, they’re returned as a tuple.

Both tuples and lists are container types that provide access to their members by position (sequences). To check to see if a sequence contains a particular member, the interpreter has to search it. If the sequence doesn’t contain the object you’re looking for, every member will have to be checked, a very inefficient way of searching. Other container types provide a more efficient way of checking membership. One of these is the dictionary, which stores values associated with a key.

Dictionaries

The dictionary is the archetypal mapping type in Python. (The actual type is dict.) Dictionaries store values mapped to keys, which can be any immutable value.[8] Dictionaries with strings as keys map names to objects; much of Python is implemented on top of dictionaries.

Fetching a value from a dictionary is done using the same syntax as indexing a list, but using the key rather than an index position.

>>> x = {'Key 1': 'Value number 1',
... 'Key 2': 'Value number 2'}
>>> x['Key 1']
'Value number 1'
>>> x['Key 2']
'Value number 2'

If you attempt to access a dictionary with a key that doesn’t exist, then a KeyError will be raised.

>>> x = {'Key 1': 'Value number 1',
... 'Key 2': 'Value number 2'}
>>> x['Key 3']
Traceback (most recent call last):
KeyError: 'Key 3'

Adding new members to a dictionary, changing existing ones, or deleting entries is trivially easy.

>>> a = {}
>>> a[(0, 1)] = 'Get the point?'
>>> a
{(0, 1): 'Get the point?'}
>>> a[(0, 1)] = 'something else'
>>> a
{(0, 1): 'something else'}
>>> del a[(0, 1)]
>>> a
{}

You probably won’t be astonished to learn that dictionaries have a host of useful methods. You can find a list of them at http://docs.python.org/lib/typesmapping.html.

If you need to store groups of data where you know each member will be unique, sets can be more appropriate than a dictionary. Sets don’t require you to create a key for the objects stored inside them.

Sets

Unlike the other types we’ve looked at so far, there’s no built-in syntax for creating sets. Instead you call the set constructor, passing in an iterable like a list or a tuple. Sets are used for containing members, where each member is unique. Checking to see if a member is contained in a set is much faster than checking for membership of a list.

>>> x = set([1, 2, 3, 4])
>>> 5 in x
False
>>> 2 in x
True

Sets are iterable, but they’re inherently unordered; even if the order of iteration appears to be consistent, you shouldn’t rely on it in your code. Sets have methods for adding members; calculating the union, intersection, and difference between sets; and much more. See http://docs.python.org/lib/set-objects.html.

You’ve now met all the basic datatypes that carry values. Two more types, which you’ll use to represent values or information in your programs, have fixed values. These types are None and the Booleans.[9]

None and the Booleans

None, True, and False are useful values that turn up all the time.

None represents null values and, like tuples, sometimes turns up implicitly in Python. If a function has no explicit return value, then None is returned.

True and False are the Boolean values, and are returned by comparison operations.

>>> 1 == 2
False
>>> 3 < 4
True

In Boolean terms, the following objects are considered to be False:

  • False and None

  • 0 (integer, long, or float)

  • An empty string (byte-string or Unicode)

  • An empty list, tuple, set, or dictionary

Everything else is considered True.

>>> x = {}
>>> if not x:
...   print 'Not this one'
...
Not this one
>>> y = [1]
>>> if y:
...    print 'This one'
...
This one

We’ve now covered the fundamental built-in types. You should be able to recognize these when they appear in Python source code and understand operations performed on them. You also know how to include these types in your own programs.

To be able to use them effectively, you need to understand how Python treats variables, the names you use to refer to your objects.

Names, objects, and references

Although Python supports several different programming paradigms, it’s built on solid object-oriented principles. A natural consequence of this is that everything in Python is an object.

Note

All the built-in types inherit from the Python class object. All IronPython objects inherit from the .NET System.Object.

If you’ve programmed in C#, you’ll be used to the difference between value types and reference types. When you pass a value type, you’re passing the value (the data) itself. When you pass a reference type, you’re passing a reference to the object rather than the object itself. From this reference, you can access the properties of the object, call its methods, and so on.

With Python, there are only reference types. This rule is straightforward, but it does have some consequences that can be surprising for programmers new to Python.

Python draws an important distinction between objects and the names you use to refer to them. When you assign a value to a variable, you’re creating a name bound to an object. If you later assign a new value to the variable, you’re removing the reference to the original object and rebinding the name to a new object.

>>> a = 3    Note
>>> b = a      Note
>>> a = 6    Note
>>> b      Note
3

This is illustrated in figure 2.3.

Names bound to objects by assignment statements

Figure 2.3. Names bound to objects by assignment statements

The effect of Names bound to objects by assignment statements, shown in Names bound to objects by assignment statements, can confuse new programmers. The name b is bound to the object pointed to by the name a Names bound to objects by assignment statements (which is an integer with the value of three Names bound to objects by assignment statements). New programmers might expect the line b = a to cause the variable b to have the same value as the variable a, so that when a is changed, b ought to change as well. Instead, when a = 6 is executed, the name a is rebound to point to a different object Names bound to objects by assignment statements, whereas the name b remains bound to the original one Names bound to objects by assignment statements.

A more succinct way of saying this is that Python doesn’t really have variables. Python has objects and names that reference objects. Understanding this is central to understanding Python. If you already understand the difference between reference and value types, then this is probably pretty intuitive to you. If you don’t feel you understand it yet, don’t worry about it for now; it should become clearer as you use Python more.

Although Python doesn’t have the reference type/value type distinction, it does draw a distinction between mutable and immutable datatypes.

Mutable and immutable objects

You’ve already seen that reassigning a name points it to a new object. Often you want to change or update the value a name points at; this might be incrementally building a string, adding members to a list, or updating a counter.

Some objects can be changed in place without having to create a new object. These are mutable objects. Others can’t be changed once they’ve been created. These are immutable objects.

As you might expect, the container types list and dict (dictionaries) are mutable. Adding new members doesn’t create a new object, but modifies the object in place. Tuples are immutable. Attempting to change or delete members, or add new ones, will raise an exception; but, tuples can contain mutable objects.

Strings and the number types are also immutable. Operations that change values don’t change the underlying object but create new ones.

>>> a = b = 'a string'
>>> a += ' which is getting bigger'
>>> a
'a string which is getting bigger'
>>> b
'a string'

Here the in-place operator += is merely syntactic sugar[10] for a = a + 'which is getting bigger'. The name a is re-bound to a new string which is formed by adding 'a string' to 'which is getting bigger'. The name b remains bound to the string 'a string'.

The last two sections might seem confusing at first; but, once you’ve grasped this, you’ve understood a lot about Python. Here’s a simple test:

>>> a = []
>>> b = [a]
>>> a.append(1)

Can you predict what the object pointed to by the name b contains?[11]

At last you’ve reached the end of the section devoted to the basic datatypes. It’s time to move from the mortar to the bricks. Functions and classes contain code, which uses the basic types. This code will be comprised of statements and expressions, so the first part of the section on basic constructs will cover these.

Python: basic constructs

We’ve now covered the basic datatypes and the way that Python handles names and objects. These things are important, but objects alone do not a program make. You also need to be able to do things with these objects. Constructs common to many programming languages, such as functions and classes and loops and conditionals, are needed to form the substance of programs. This section is about the different constructs available to you in Python.

The lowest level of constructs consists of statements and expressions. Every line of Python code will have at least one statement or expression. Statements and expressions are the raw ingredients out of which you create conditionals and loops. Stepping up a level, from these you’ll craft functions and classes and, up the final step to full applications, organize functions and classes into modules and packages.

Statements and expressions

Python draws a distinction between statements and expressions. This wouldn’t be true in a purely functional programming language where everything is an expression.

Note

A statement does something, performs an action, whereas an expression returns a value.

Statements are actions such as printing, assigning a value, or raising an exception. A statement doesn’t return a value, so you can’t get away with the following:

>>> x = print 3
Traceback (most recent call last):
SyntaxError: unexpected token print (<stdin>, line 1)

Statements can’t be compounded together. You can do multiple assignments as a single statement.

>>> x = y = 10

Statements other than assignment use a Python keyword. These keywords are reserved words, and you can’t use them as variable names. You can see the full list of reserved keywords (not all of which form statements) at http://docs.python.org/ref/keywords.html.

Python follows the normal BODMAS[12] precedence rules for operators in expressions. The best reference I could find on Python operators is from A Byte of Python by CH Swaroop at http://www.swaroopch.com/notes/Python_en:Operators_and_Expressions#Operator_Precedence.

Parentheses not only group parts of an expression together, but also allow you to break unwieldy expressions over multiple lines.

>>> (1 + 2 + 3 + 4 + 5 + 6 + 7 +
... 8 + 9 + 10)
55

Python uses keywords and syntax for its most basic elements that will be familiar to most programmers, and are easy to understand even if they aren’t familiar. The most fundamental of these are conditionals and loops.

Conditionals and loops

Very often through the course of a program you’ll need it to take different actions depending on some condition—maybe finding the terminating condition of an algorithm, or responding to user input. The conditional statement in Python uses the if keyword.

Every statement that starts a new block of code must be terminated with a colon. Indented code following the if statement will only be executed if the condition evaluates to True. As soon as your code is dedented, execution continues normally. if allows you to provide multiple branches for different conditions; a final branch will be executed if none of the other conditions have been met. elif is used for multiple branches, and else for the final branch.

>>> x = -9
>>> if x == 0:
...    print 'x equals zero'
... elif x > 0:
...    print 'x is positive'
... else:
...    print 'x is negative'
...
'x is negative'

Another conditional in Python is while, which is also a loop. The while loop repeats a block of code until a condition is met, or until you explicitly break out of the loop.

>>> a = 0
>>> while a < 2:
...    print a
...    a += 1
...
0
1

If you want to terminate the loop early, you can use the break statement. With an optional else clause, you can tell whether a loop completed normally.

>>> a = 0
>>> while a < 6:
...   print a
...   if a >= 2:
...      break
... else:
...    print 'Loop terminated normally'
0
1
2

The second kind of loop in Python is the for loop. It’s used to iterate over a sequence (or any iterable), and allows you to perform an operation on every member.

>>> sequence = [1, 2, 3]
>>> for entry in sequence:
...    print entry
...
1
2
3

Although it’s theoretically possible to write a program using only the constructs we’ve examined so far, it would be an extremely ugly program. To reuse blocks of code (and write well-structured, modular programs), you need functions and classes.

Functions

Functions allow you to break code down into smaller units and make programs more readable. Functions take values as arguments and return values, but they may also be used for their side effects, such as writing to the filesystem or a database.

Like other aspects of Python, the syntax for functions is straightforward. It’s shown in figure 2.4.

Function definition, body, and return

Figure 2.4. Function definition, body, and return

A function is created using the def keyword, followed by the function name and an argument list (which can be empty if the function receives no arguments). Because they introduce a new block of code, function definitions are terminated with a colon.

The function body is the indented block which follows the def. If the interpreter hits a return statement, then it leaves the function and returns the specified value (or None if no value is supplied). If the interpreter reaches the end of the function body without hitting a return statement at all, then None is also returned.

If the function needs to receive arguments, then a comma-separated list of names is placed between the parentheses that follow the function name. When the function is called, a value must be supplied for every name in the argument list.

Python also allows you to define function arguments that have a default value. If the argument isn’t supplied when the function is called, the default value is used instead. This is done by assigning a value to the argument inside the function definition.

>>> def PrintArgs(arg1, arg2=3):
...    print arg1, arg2
...
>>> PrintArgs(3)
3 3
>>> PrintArgs(3, 2)
3 2

As you can see, when you don’t pass in an explicit second value, arg2 takes on the default value specified in the function definition. If you do pass in a second value, it’s used instead of the default one. Arguments with default values are known as keyword arguments. Note that you can also pass in keyword arguments by position; this is shown in the last line of the following example.

>>> def ShowingOffKeywords(a=None, b=None):
...    if a is not None:
...       print 'a =', a
...    if b is not None:
...       print 'b =', b
...
>>> ShowingOffKeywords(a=3)
a = 3
>>> ShowingOffKeywords(b='a string')
b = a string
>>> ShowingOffKeywords(2.0, 3)
a = 2.0
b = 3

If used sparingly, keyword arguments can greatly improve an API by removing the need to pass in values that rarely differ from the defaults. If you overuse them, you can create a complex API that no one will remember how to use. Keyword arguments also provide a convenient mechanism for configuring .NET classes—the ultimate goal of this tutorial.

Note

Callable objects can be called with arguments and return values. Functions are callable objects, but they’re not the only things that are callable. This is why you might see the term callable used instead of function in places through this book.

Functions are great for simple and self-contained operations where the overhead of writing a class isn’t needed. Many simple operations end up being needed in program after program. Instead of having to re-implement your own functions over and over again, Python has a selection of built-in functions.

Built-in functions

There are many operations that you need to perform often or that can be awkward to do cleanly. For a lot of these, Python provides built-in functions that are always available. Unlike the reserved keywords, you can create names (variables or functions) that shadow these names. Needless to say, shadowing built-ins is bad practice.

Table 2.2 shows some of the most useful built-in functions, along with a brief explanation.

Table 2.2. Some commonly used built-in functions

Name

Example

Purpose

abs

abs(-3)

Returns the absolute value (positive) of a number passed in.

enumerate

enumerate(['a', 'b', 'c'])

Returns an iterable (an enumerate object). Iterating over this returns a sequence of tuples: (index, element). Index is the position of the element in the sequence or iterable you pass in.

len

len([1, 2, 3])

Returns the length (number of elements) of an object passed in (sequence, iterable, set, or dictionary and friends).

isinstance

isinstance(myObject, SomeType)

Tells you if an object is an instance of a specified type, or one of its subclasses.

max

max(6, 10, 12, 8)

Returns the largest of all the arguments you pass in, or the largest member of a sequence or iterable.

 

max([1, 2, 3, 4])

 

min

min(6, 10, 12, 8)

Like max, except it returns the smallest instead of the largest.

 

min([1, 2, 3, 4])

 

open

open(filename, mode)

Opens the specified file with the specified mode.[a]

range

range(10)

Returns a list with elements from 0 up to (but not including) the number you pass in.

 

range(5, 10)

Returns a list with elements from the first value you pass in, up to (but not including) the second value you pass in.

reversed

reversed(sequence)

Returns a sequence, which is a reversed version of the one you pass in. (It does not alter the sequence that you pass in.)

sorted

sorted(iterable)

Returns a new list, which is a sorted version of the sequence or iterable that you pass in.

sum

sum(iterable)

Returns the sum of all the members of the sequence or iterable you pass in.

zip

zip(iterable1, iterable2...)

Returns a list of tuples, with corresponding members from each of the sequences or iterables that you pass in. The first tuple will have the first member from each argument, the second tuple will have the second member, and so on. The list returned is only as long as the shortest one that you pass in.

[a] The filename can be a path relative to the current directory or an absolute path. The mode should be a string. See the docs for all the options. open returns an open file object.

Many of these functions have optional arguments or alternative usage patterns. For details, refer to the Python documentation, which provides a list of all the built-in functions at http://docs.python.org/lib/built-in-funcs.html.

It’s worth familiarizing yourself with these functions. We use a few of them through the book, and they can be extremely useful.

This list of built-in functions includes the type objects of the datatypes we’ve already looked at. These create new objects of their type. For example, int takes numbers or strings as input and returns a new integer.

>>> int(3.2)
3
>>> int('3')
3

The other constructors do a similar job, creating new objects from input you provide.

Functions are the bread and butter of procedural and functional programming. In recent years,[13] a paradigm called object-oriented programming has arisen, building on the principles of procedural programming. Object-oriented programming allows you to combine data with functions (called methods) for working on the data. The basic element in object-oriented programming is a programming construct called the class, which we turn to now.

Classes

At the start of this chapter, we looked at the built-in datatypes. These are relatively simple objects, such as strings or integers, which represent a value. As well as their value, most[14] of the built-in types also have methods associated with them.

You use these methods for performing common operations; they’re a bit like the built-in functions. For example, strings have methods to return a copy of themselves in lowercase or to remove whitespace from the start and end. The key thing is that the built-in types only have methods that are relevant to their type. Numbers don’t have a lower or a strip method.

The selection of available methods is determined by the type of the object and its class. You can define your own classes to create new kinds of objects.[15]

Note

Classes are the blueprints for creating new kinds of objects. Once you’ve defined a class, you can create as many individual objects from the blueprint as you want.

New classes are not only for representing data but also for structuring programs and providing a framework. Well-written classes will make your program easier to understand; but, more importantly, they’re a thing of beauty and elegance!

Methods are attributes of objects that you call like functions—they can take arguments and return values. All object attributes are accessed using the dot syntax.

The following example uses the string method startswith. This method takes an argument and returns True if the string starts with the argument you supplied.

>>> string = 'Hello world'
>>> string.startswith('Hello')
True

Not all attributes are methods. Attributes can also be values, properties, or any other object. The normal way to access attributes though, whatever they may be, is with the dot syntax.

So how do you create your own classes? The answer is the class statement. We talk about classes being declared. A typical line that commences a class declaration is shown in figure 2.5.

A class declaration

Figure 2.5. A class declaration

Between the parentheses following the class name is a list of classes that your class inherits from. If your class inherits from another class, then it automatically gains the attributes and methods of that class. If you need several classes that do similar things, then you may be able to put the common parts into a single class and then have your other classes inherit from it. Classes that you inherit from are called base classes. Classes can have more than one base class—Python supports multiple inheritance—but this can be a recipe for confusion; it’s usually best avoided if possible.

Indented inside the class statement are your methods and any class attributes. To write methods, you need to know about two things first: self and __init__.

Every method you write needs to take an extra argument. The individual objects created from classes are called instances. Individual strings are instances of the str or unicode class. For the code inside your methods to access the instance it belongs to, it needs a reference to the instance. This is passed in as the first argument to methods; the Python convention is to call this argument self. self is the equivalent of this in C#, except that you need to explicitly declare it in the method signature.

You also need to know that constructor methods are declared in Python with the name __init__. __init__ is entirely optional; you don’t need to write a constructor if your class doesn’t require one.

Note

The constructor of a class is a method called when new objects are created from the class. New instances are created (or instantiated) by calling the class, and passing in any arguments required by the constructor.

Method names that begin and end with a double underscore (spoken as dunder, which is much quicker to say) often have a special purpose in Python; they’re called on your objects by the interpreter. You’ll encounter several of these magic methods as we progress.

When you create a new instance by calling the class, the constructor is called and the new instance returned.

>>> class MyObject(object):
...    def __init__(self, value):
...       self.value = value
...
>>> myObject = MyObject(10)
>>> print myObject.value
10

When the instance is created, __init__ is called with a reference to the new instance (self) and the argument passed in. __init__ then sets the value attribute on the instance to 10.

Classes can have attributes too; this is useful because class attributes are shared between all instances.

>>> class SimpleObject(object):
...    classAttribute = object()
...
>>> instance1 = SimpleObject()
>>> instance2 = SimpleObject()
>>> instance1.classAttribute is instance2.classAttribute
True

Note that in this example a constructor isn’t defined, so you inherit the default one from object, which does nothing.

Python doesn’t have true private or protected attributes. By convention, any method or attribute name that starts with a single or a double underscore isn’t part of the public API. Exceptions are the magic methods, whose names start and end with double underscores. You don’t often call these directly anyway, except perhaps in subclasses calling up to their parent classes. Attribute or method names that have a leading double underscore are treated specially by the interpreter. The names are mangled[17] to make it harder to access them from outside the class. From inside the class, the names can be used normally; from outside, they can only be accessed via the mangled name.

We’ve now covered the basic constructs in Python, the mortar and the bricks of the language. Functions and classes are the most essential building components of any Python program. By now, you should be starting to get an overview of Python and how it works; for example, methods are really just special cases of functions. One consequence of this is that you can replace methods on classes with alternative functions at runtime, something useful for testing. The more of an overview you have, the easier it is to understand the details of what’s going on.

The goal of this chapter is to allow you to create an IronPython program that takes full advantage of the .NET platform. Many aspects of the .NET framework are similar to Python, such as the basic class structure with methods and attributes. To create Python programs, you need to understand the objects and patterns you’ll encounter from a Python point of view. You also need to know enough Python to be able to create applications. Before we can dive into .NET, you need a few more raw materials; think of these as the timbers, ties, and plastics of the house that is the Python language. In the next section, we look at a few further aspects of Python necessary to our construction work.

Additional Python features

If you’ve used languages like VB or C# before, then most of the syntax covered so far should be at least familiar. The core language features of Python aren’t large; Python is small enough to fit your brain. This section will look at a few more features of Python, the semantics of dividing programs into modules and importing from them, scoping rules, decorators, and so on. We look at exception handling first.

Exception handling

Exceptions indicate that some kind of error has happened. They can be raised by the operating system when you try something like accessing a file that doesn’t exist, or by the Python runtime when you do something daft like adding strings to numbers.

You can also raise exceptions yourself, to indicate that an error condition has been reached.

If you don’t handle exceptions, then your program will literally stop with a crash. Catching exceptions in Python is done with the try: ... except: block.

>>> 'a string' + 3
Traceback (most recent call last):
TypeError: Cannot convert int(3) to String
>>> try:
...   'a string' + 3
... except:
...   pass
...

When used like this, the except clause catches all exceptions. This is bad practice; you should only catch the specific types of exceptions that you’re expecting. Catching and silencing exceptions you aren’t expecting can allow bugs in your code to pass unnoticed, and cause problems that are very difficult to debug.

In Python, you can specify which types of exceptions you want to catch after the except. You specify either a single exception type or a tuple of exceptions. You can also chain multiple except clauses to provide different blocks of code to handle different types of exceptions. You can also provide a block of code to be executed only if no exceptions occur, by ending your except clauses with an optional else clause.[18]

>>> try:
...    do_something()
... except (OSError, IOError):
...    print 'Looks like the path was invalid'
... except TypeError:
...    print 'Why did this happen'
... else:
...    print 'It all worked out fine'
...

Sometimes you need to keep hold of the exception instance to check attributes or the message. The string representation of exception instances is the error message. You can bind the exception to a name within the except clause; this can be useful for logging.

>>> try:
...     'a string' + 3
... except TypeError, e:
...    print e
...
Cannot convert int(3) to String

In some circumstances you may wish to catch the error and then re-raise it. If you use the raise statement on its own, it re-raises the last exception that occurred.

For a slightly different use case, try: ... except: has a companion—the try: ... finally: block. You use finally rather than except when you need to guarantee that some code runs, whether or not the preceding block was successful. For example, you might want to use finally when closing a database connection or an open file. If an exception is raised, the finally block is executed, and then the exception is re-raised.

The next example illustrates the finally block, and also the other side of catching exceptions—raising exceptions.

>>> try:
...    raise Exception("Something went wrong")
... finally:
...    print 'This will be executed'
...    print 'whether an exception is raised'
...    print 'or not'
...
This will be executed
whether an exception is raised
or not
Traceback (most recent call last):
Exception: Something went wrong

The raise statement raises exceptions. There are several different variations of raise, but the only one you really need to know is the form used in the example.

raise ExceptionType(arguments)

The base exception class in Python is Exception.

That finishes it off for exceptions, at least for the moment. We’ve already talked at length about how Python can take advantage of programming styles like functional programming. Understanding the scoping rules is important for any programming, but it’s particularly vital to functional programming.

Closures and scoping rules

Scoping rules are the way that a language looks up names that you use within a program. Python uses lexical scoping.[20] Your code can access names defined in any enclosing scopes, as well as all global values.

But first, we probably need to explain what a scope is. Let’s look at global and local scopes.

>>> x = 3            Closures and scoping rules
>>> def function1():
...    print x       Closures and scoping rules
...
>>> def function2():
...    x = 6         Closures and scoping rules
...    print x
...
>>> function1()
3
>>> function2()
6

This code segment defines the name x. Because it isn’t inside a function or method body, x is defined in the global scope Closures and scoping rules. function1 uses the name x Closures and scoping rules, which isn’t defined inside it. When Python encounters the name and can’t find it locally, it looks it up in the global scope.

Inside function2 the name x is also defined locally Closures and scoping rules. When you call function2 and the Python interpreter encounters the name x, the first place it looks is the local scope. The local name x shadows the global variable.

Things get interesting when you introduce another scope. If a function has a function defined inside it, the outer function is said to enclose the inner function. When the inner function uses a name, the name is first looked for in the scope of the inner function, its local scope. If it isn’t found, then Python will check the enclosing scope. This applies to as many levels of nesting as you want; the outer scopes will be searched all the way up to the global scope. If the name isn’t found anywhere, then a NameError exception is raised.

Closures and scoping rules

When you call outerFunction, it defines the function innerFunction. This function exists in the local scope of outerFunction. (Try calling innerFunction from the global scope.) outerFunction calls innerFunction; this prints the values x and y. Neither of these is local to innerFunction. x is defined inside outerFunction, shadowing the global x. y is only defined in the global scope.

When you call a function, the names inside it are looked up from the enclosing scope where the function was defined, not from where it’s used. Your outer functions can return a reference to an inner function that can then be used far from where it was created. It will still use names from the scope(s) that enclosed it when it was defined. To illustrate this, let’s use a classic example and create an adder function.

>>> def makeAdder(value):
...    def newAdder(newValue):     Closures and scoping rules
...       return value + newValue    Closures and scoping rules
...    return newAdder
...
>>> adder = makeAdder(7)      Closures and scoping rules
>>> adder(3)
10
>>> adder(-4)
3

You pass in a value to the makeAdder function, and it returns a newAdder function Closures and scoping rules that adds numbers to the original value that you passed in Closures and scoping rules. When you call makeAdder with an argument Closures and scoping rules, the argument is bound to the name value, which is local to the scope of makeAdder. You can reuse makeAdder to create as many adder functions as you like. Every time you call it a new scope is created.

Several parts of Python show the influence of functional programming. One favorite of mine is the list comprehension, which is a particularly elegant construct. List comprehensions also get used a lot in Python, so it would be impossible to have a tutorial that doesn’t mention them.

List comprehensions

List comprehensions allow you to express several lines of code in a single readable line. This is a concept borrowed from the language Haskell, that allows you to combine a for loop and an optional filter in a single construct. If you’re brave, you can combine several loops into a single list comprehension, but this tends to be a bit taxing on the brain.

List comprehensions are so named because they create lists. Consider the following simple segment of code:

>>> result = []
>>> input = [1, 2, 3, 4, 5]
>>> for value in input:
...    result.append(value * 3)
...
>>> result
[3, 6, 9, 12, 15]

This constructs a list (called result) from all the members of input, multiplying each value by three.

This segment of code can be written in a much more concise way using a list comprehension.

>>> input = [1, 2, 3, 4, 5]
>>> result = [value * 3 for value in input]
>>> result
[3, 6, 9, 12, 15]

The expression value * 3 is calculated for each member of input, and the result list is built. The two forms of this code, using a loop and using a list comprehension, are functionally identical.

List comprehensions also allow you to include filters. Suppose you only wanted to use the values from the input that are greater than two; you can put this condition into the body of the list comprehension.

>>> input = [1, 2, 3, 4, 5]
>>> result = [value * 3 for value in input if value > 2]
>>> result
[9, 12, 15]

A list comprehension is itself an expression, and so you can use one anywhere an expression can be used.

We haven’t covered every possible variant of Python syntax, but you should have learned enough to give you a good grounding to be able to start exploring. The final sections in this chapter are about organizing programs into libraries called modules and packages. The mechanisms for working with modules and packages are useful for organizing your own programs, but they’re also the same mechanisms by which you access the .NET libraries.

Modules, packages, and importing

The last thing you want when programming is to have all your code contained in a single monolithic file. This makes it almost impossible to find anything. Ideally, you want to break your program down into small files containing only closely related classes or functionality. In Python, these are called modules.

Note

A module is a Python source file (a text file) whose name ends with .py. Objects (names) defined in a module can be imported and used elsewhere. They’re very different from .NET modules, which are partitions of assemblies.

The import statement has several different forms.

import module[21]
from module import name1, name2
from module import name as anotherName
from module import *

Importing a module executes the code it contains and creates a module object. The names you’ve specified are then available from where you imported them.

If you use the first form, you receive a reference to the module object. Needless to say, these are first-class objects that you can pass around and access attributes on (including setting and deleting attributes). If a module defines a class SomeClass, then you can access it using module.SomeClass.

If you need access to only a few objects from the module, you can use the second form. It imports only the names you’ve specified from the module.

If a name you wish to import would clash with a name in your current namespace, you can use the third form. This imports the object you specify, but binds it to an alternative name.

The fourth form is the closest to the C# using directive. It imports all the names (except ones that start with an underscore) from the module into your namespace. In Python, this is generally frowned on. You may import names that clash with other names you’re using without realizing it; when reading your code, it’s not possible to see where names are defined.

Python allows you to group related modules together as a package. The structure of a Python package, with subpackages, is shown in figure 2.6.

The structure of a Python package on the filesystem

Figure 2.6. The structure of a Python package on the filesystem

Note

A package is a directory containing Python files and a file called __init__.py. A package can contain subpackages (directories), which also have an __init__.py. Directories and subdirectories must have names that are valid Python identifiers.

A package is a directory on the Python search path. Importing anything from the package will execute __init__.py and insert the resulting module into sys.modules under the package name. You can use __init__.py to customize what importing the package does, but it’s also common to leave it as an empty file and expose the package functionality via the modules in the package.

You import a module from a package using dot syntax.

import package.module
from package import module

Packages themselves may contain packages; these are subpackages. To access subpackages, you just need to use a few more dots.

import package.subpackage.module
from package.subpackage import module

Python also contains several built-in modules. You still need to import these to have access to them, but no code is executed when you do the import. We mention these because one of them is very important to understanding imports. This is the sys module.[22]

When you import a module, the first thing that Python does is look inside sys.modules to see if the module has already been imported. sys.modules is a dictionary, keyed by module name, containing the module objects. If the module is already in sys.modules, then it will be fetched from there rather than re-executed. Importing a module (or name) from different places will always give you a reference to the same object.

If the module hasn’t been imported yet, Python searches its path to look for a file named module.py.[23] If it finds a Python file corresponding to the import, Python executes the file and creates the module object. If the module isn’t found, then an ImportError is raised.

The list of paths that Python searches is stored in sys.path. This is a list of strings that always includes the directory of the main script that’s running. You can add (or remove) paths from this list if you want.

Some Python files can be used both as libraries, to be imported from, and as scripts that provide functionality when they’re executed directly. For example, consider a library that provides routines for converting files from one format to another. Programs may wish to import these functions and classes for use within an application, but the library itself might be capable of acting as a command-line utility for converting files.

In this case, the code needs to know whether it’s running as the main script or has been imported from somewhere else. You can do this by checking the value of the variable __name__. This is normally set to the current module name unless the script is running as the main script, in which case its name will be __main__.[24]

def main():
   # code to execute functionality
   # when run as a script
if __name__ == '__main__':
   main()

This segment of code will only call the function main if run as the main script and not if imported.

When you re-import a module, you get a new reference to the already-created module object. This can be a pain when you’re actively developing a module and want to play with it from the interactive interpreter. If you want to force the interpreter to use the latest version, you can use the reload function and then re-import it.

Note

Calling reload doesn’t rebind names that point to objects from the old module. You have to do this yourself after reloading—hence, the need to import again.

Another useful feature of modules shared with several types of objects is the docstring.

Docstrings

If you cast your mind back to the end of the first chapter, you’ll recall the useful builtin function help. This is for working with the interactive interpreter; it prints some useful information about an object to the console. For Python modules, functions, classes, and methods, it will include any docstring that you’ve defined. So what is this esoteric creature, the docstring?

A docstring is a string written inline with an object and that isn’t assigned a name. This is then available to the help function, and lives attached to the object as the __doc__ attribute. The following segment illustrates this a bit more clearly, using a function as an example:

>>> def adder(val1, val2):
...   "This function takes two values and adds them together."
...   return val1 + val2
...
>>> help(adder)
Help on function adder in module __main__

 | adder(val1, val2)
 |    This function takes two values and adds them together.
>>> adder.__doc__
'This function takes two values and adds them together.'
>>>

If you want docstrings that contain multiple lines, you can use triple-quoted strings.

Docstrings aren’t just useful for interactive help; they’re guides for anyone reading your source code and are also used by automatic documentation tools[25] for creating API documentation from source code.

At last, you know enough to create large and well-structured IronPython programs, as long as you don’t need to access much .NET functionality. Before you rush off to try this, we’d like to save you some time. CPython comes with a large standard library of packages and modules for common programming tasks. Before you start coding, it’s well worth checking to see if at least part of your task is already written for you. The next section talks about the standard library, introduces you to a few of the more commonly used modules, and explains how to use the library with IronPython.

The Python standard library

Python comes with batteries included. This statement refers to the standard library that accompanies CPython. Many of the modules it provides are pure-Python modules, but some of them are extension modules written in C.

Note

An open source project called Ironclad,[26] created by Resolver Systems, re-implements the Python C-API in C# with the goal of allowing you to import and use Python C extensions from IronPython.

Unfortunately, extension modules written in C won’t work directly in IronPython. Other modules, even in the standard library, rely on implementation details or undocumented features of Python. Some of these incompatibilities between Python and IronPython have already been fixed, but some are very difficult cases.

That’s the bad news. The good news is that a large proportion of the standard library works fine. The IronPython team, along with the Python community, has put a lot of effort into ensuring that as much of the standard library as possible works with IronPython; in some cases, alternatives to C extension modules have been created. Existing code that uses the standard library stands a better chance of working, and these modules are available to use within your code.

Note

The standard library is subject to the liberal Python License.[27] You are free to distribute it (or parts of it) along with your application as long as you include the copyright notice.

The first step in using the standard library is obtaining it. The IronPython 2 msi installer comes with the Python 2.5 standard library, and makes it automatically available for import.[28]

An alternative way of getting hold of the standard library is downloading and installing CPython. Go to www.python.org/download/ and download the latest version of Python 2.5.[29]

On Windows, the default install location is C:Python25. The standard library itself will be located in C:Python25Lib.

You then need to expose the library to IronPython. There are three convenient ways of doing this. The first is best for use on a single machine, the second for distributing applications, and the third for quick one-off experimentation.

  • Set an environment variable IRONPYTHONPATH to the location of the standard library directory.

  • Copy the whole standard library into a directory accessible by your application. You’ll need to add this directory to sys.path at runtime.

  • Start IronPython while the current directory is C:Python25Lib.

You can manually add the standard directory library at the interactive interpreter with the following steps:

>>> import sys
>>> sys.path.append(r'C:Python25Lib')

Notice that you use a raw string to add the path; otherwise, the backslashes will be interpreted as escape characters.

There are many modules and packages in the Python standard library. We’ve listed some of the commonly used ones in table 2.3. Some of these modules, such as re and cStringIO, are implemented in C for CPython. Many of them[30] have been implemented by the IronPython team as built-in modules (written in C#) so that these modules are available to IronPython. You can find a list of all of them at http://docs.python.org/modindex.html.

Table 2.3. Useful standard library modules

Module

Purpose

sys

Provides system-specific settings like the path, executable name, and command-line arguments.

os

Provides functions for interacting with the operating system, including launching processes.

os.path

Provides functions for working with files, directories, and paths.

re

Regular expression library.

math

Floating point math routines.

random

Generates random numbers and for making random selections.

time

For working with times and formatting date strings.

unittest

An extensible unit test framework.

optparse

A library for parsing command-line arguments.

cPickle

Provides object persistence. Serialize Python or .NET objects as text or binary.

decimal

Provides support for decimal floating point arithmetic.

cStringIO

Implements a file-like class (StringIO) that reads and writes a string buffer.

collections

High-performance container datatype, the deque.

itertools

A number of iterator building blocks.

types

Provides access to type objects for components such as classes, functions, and methods.

Python isn’t a big language, nor is it difficult to learn, but we’ve still covered a lot of material in this chapter. The best way of getting it ingrained, of course, is to use it. In the next chapter, you’ll be putting what you’ve learned here to practical use; you’ll also learn about the .NET framework, which comes with its own standard library.

Summary

There’s a great deal to Python that we haven’t looked at; but, if we’ve been doing our job, then what you’ve learned will give you a good foundation. Python emphasizes a readable style of programming with the use of English keywords wherever possible. You now know the bare bones of the Python programming language—the frame of the house without the furnishings that make a house a home. We completed the chapter by looking at how to use the Python standard library with IronPython. The standard library provides a large set of Python modules for you to use with your code to supplement what’s available through .NET.

Any program you write will use classes that you create yourself, along with ones from the Python standard library and the .NET framework. Although we’ve covered the syntax needed to write classes, writing ones that make the best use of Python takes practice—you need to write some.

More importantly, we haven’t covered the use of the classes from the .NET framework; this, after all, is the reason to be using IronPython. In the next chapter, you’ll be able to use the knowledge gained in this chapter, and put it to work with the power of .NET. In the process, you’ll be getting some practice at writing your own Python classes.



[1] We’ve highlighted particular differences between Python and IronPython in callouts and sidebars.

[3] Python is a highly dynamic language and so the type of some objects can’t be inferred; the type is determined only at runtime, making it hard for IDEs to always know what attributes are available. Several IDEs do an excellent job, but Wing is the best of the ones I’ve tried.

[4] Whereas languages like Ruby inherit more from Smalltalk in their core object model.

[5] See this page for a full list of the escape characters: http://docs.python.org/ref/strings.html.

[6] With the odd exception that a raw string can’t end in an odd number of backslashes; otherwise, the last backslash escapes the closing quote.

[7] Typically on a 32-bit operating system, this will be the largest number that can be stored in a 32-bit signed integer: 2147483647.

[8] Well, technically any hashable value. This allows you to implement custom objects and control how or whether they behave as dictionary keys. This is a subject for another page, though.

[9] Which sounds like a great name for a band...

[10] Syntactic sugar means providing a nicer syntax for something that’s possible another way.

[11] The answer is [[1]]. This list pointed to by the name b contains the list pointed to by the name a, which now has a member.

[12] Brackets, Orders, Division and Multiplication, Addition and Subtraction. See http://www.mathsisfun.com/operation-order-bodmas.html.

[13] Well, Simula from the 1960s is widely regarded as the first object-oriented language although the term was first applied to Smalltalk. By the mid 1990s, OOP had become the dominant programming methodology, largely credited to the success of C++.

[14] Not surprisingly, the NoneType doesn’t have many useful methods on it...

[15] For a longer introduction to objects and classes, visit my (Michael’s)article “Object-Oriented Programming with Python” at http://www.voidspace.org.uk/python/articles/OOP.shtml.

[16] At least don’t inherit directly. Old-style classes are instances of the ClassType metaclass.

[17] They’re mangled by adding an underscore followed by the class name to the start of the name.

[18] OSError and IOError are built-in Python exception types.

[19] Unless you enable Python 2.5 compatibility.

[20] The Python exception to full lexical scoping is that you can’t rebind names in an enclosing scope.

[21] This can also be written using the third form import module as SomethingElse.

[22] You can find a reference to the sys module at http://docs.python.org/lib/module-sys.html. This module exposes some CPython implementation details, such as access to Python stack frames, which don’t exist in IronPython. Most of the sys module is available, though.

[23] As well as searching for a corresponding Python file, IronPython looks for a package directory, a built-in module, or .NET classes. You can even add import hooks to further customize the way imports work.

[24] You can access the namespace of the main script by doing import __main__.

[28] IronPython automatically adds the directory sys.exec_prefix + 'Lib' to sys.path.

[29] Version 2.5.2 at the time of writing.

[30] All the modules listed in table 2.3 work with IronPython, but not every C extension in the standard library has been ported.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.126