Reader small image

You're reading from  IPython Notebook Essentials

Product typeBook
Published inNov 2014
Publisher
ISBN-139781783988341
Edition1st Edition
Tools
Right arrow
Author (1)
Luiz Felipe Martins
Luiz Felipe Martins
author image
Luiz Felipe Martins

Luiz Felipe Martins holds a PhD in applied mathematics from Brown University and has worked as a researcher and educator for more than 20 years. His research is mainly in the field of applied probability. He has been involved in developing code for the open source homework system, WeBWorK, where he wrote a library for the visualization of systems of differential equations. He was supported by an NSF grant for this project. Currently, he is an Associate Professor in the Department of Mathematics at Cleveland State University, Cleveland, Ohio, where he has developed several courses in applied mathematics and scientific computing. His current duties include coordinating all first-year calculus sessions.
Read more about Luiz Felipe Martins

Right arrow

Appendix B. A Brief Review of Python

Introduction


This appendix will give you a brief tour of the Python syntax. This is not intended to be a course on Python programming, but can be used by readers who are unfamiliar with the language as a quick introduction. The following topics will be covered in this appendix:

  • Basic types, expressions, and variables and their assignment

  • Sequence types

  • Dictionaries

  • Control structures

  • Functions, objects, and methods

Basic types, expressions, and variables and their assignment


Any data that can be referred to in a Python code is considered an object. Objects are used to represent everything from atomic data, such as numbers, to very complex data structures, such as multidimensional arrays, database connections, and documents in several formats.

At the root of the object hierarchy are the numeric data types. These include the following:

  • Integers: There are three types of integers in Python.

    • Plain integers: They are represented in the native architecture, which, in most systems, will be either 32- or 64-bit signed values.

    • Long integers: They are integers with unlimited range, subject to available memory. Most of the time, the programmer does not need to be concerned with the distinction between plain and long integers. Python deals with conversions between the types in a transparent way.

    • Booleans: They represent the values False and True. In most situations, they are equivalent to 0 and 1, respectively.

  • Floats: They represent the native double-precision floating-point numbers.

  • Complex: They represent complex numbers, represented as a pair of double-precision floating-point numbers.

The following table has examples of literals (that is, constants) for each data type:

Data type

Literals

Integers

0, 2, 4, …, 43882838388

5L, 5l (long integer)

0xFE4 (hexadecimal)

03241 (octal)

Real numbers (float)

5.34, 1.2, 3., 0

1.4e-32 (scientific notation)

Complex

1.0+3.4j, 1+2j, 1j, 0j, complex(4.3, 2.5)

The imaginary unit is represented by j, but only if it follows a number literal (otherwise, it represents the variable named j). So, to represent the imaginary unit we must use 1j and the complex zero is 0j. The real and imaginary part of a complex number are always stored as double-precision floating-point values.

Note

Note that the set of numeric types is greatly extended by NumPy to allow efficient numeric computations.

The assignment statement is used to store values in variables, as follows:

a = 3
b = 2.28
c = 12
d = 1+2j

Python supports multiple simultaneous assignments of values, so the previous four lines of code could be equivalently written in a single line as follows:

a, b, c, d = 3, 2.28, 12, 1+2j

In a multiple assignment, all expressions in the right-hand side are evaluated before the assignments are made. For example, a common idiom to exchange the values of two variables is as follows:

v, w = w, v

As an exercise, the reader can try to predict the result of the following statement, given the preceding variable assignments:

a, b, c = a + b, c + d, a * b * c * d
print a, b, c, d

The following example shows how to compute the two solutions of a quadratic equation:

a, b, c = 2., -1., -4.
x1, x2 = .5 * (-b - (b ** 2 - 4 * a * c) ** 0.5), .5 * (-b + (b ** 2 - 4 * a * c) ** 0.5)
print x1, x2

Note that we force the variables a, b, and c to be floating-point values by using a decimal point. This is good practice when performing numerical computations. The following table contains a partial list of Python operators:

Operators

Python operators

Arithmetic

+ (Addition)

- (Subtraction, unary minus)

* (Multiplication)

/ (Division, see the note below the table)

// (Integer division)

% (Remainder)

Comparison

== (Equal to)

> (Greater than)

< (Less than)

>= (Greater than or equal to)

<= (Less than or equal to)

!= (Not equal to)

Boolean

and

or

not

Bitwise Boolean

& (AND)

| (OR)

^ (XOR)

~ (NOT)

Bitwise shift

<< (Left shift)

>> (Right shift)

Note

Care should be taken with the division operator (/). If the operands are integers, the result of this operation is the integer quotient. For example, 34/12 results 2. To get the floating point result, we must either enter floating point operands, as in 34./12., or add the following statement:

from __future__ import division

The // operator always represents integer division.

Arithmetic operators follow the rules for the order of operations that may be altered with the use of parenthesis. Comparison operators have lower precedence than arithmetic operators, and the or, and, and not operators have even lower precedence. So, an expression like the following one produces the expected result:

2 + 3 < 5 ** 2 and 4 * 3 != 13

In other words, the preceding command line is parsed as follows:

(((2 + 3) < (5 ** 2)) and ((4 * 3) != 13))

The logical operators and and or short circuit, so, for example, the second comparison is never evaluated in the command:

2 < 3 or 4 > 5

The precedence rules for the bitwise and shift operators may not be as intuitive, so it is recommended to always use parenthesis to specify the order of operations, which also adds clarity to the code.

Python also supports augmented assignments. For example, the following command lines first assign the value 5 to a, and then increment the value of a by one:

a = 5
a += 1

Note

Python does not have increment/decrement operators, such as a++ and ++a, as in the C language.

All Python operators have a corresponding augmented assignment statement. The general semantic for any operator $ is the following statement:

v $= <expression>

The preceding statement is equivalent to the following:

v = v $ (<expression>)

Note

Note that $ is not a valid Python operator, it is just being used as a placeholder for a generic operator.

Sequence types


Python sequence types are used to represent ordered collections of objects. They are classified into mutable and immutable sequence types. Here, we will only discuss lists (mutable) and tuples and strings (both immutable). Other sequence types are mentioned at the end of this section.

Lists

The following example shows how to construct a list in Python and assign it to a variable:

numbers = [0, 1.2, 234259399992, 4+3j]

Individual entries in the list are accessed with index notation as follows:

numbers[2]

Notice that indexing always starts with 0. Negative indices are allowed and they represent positions starting at the end of the list. For example, numbers[-1] is the last entry, numbers[-2] is the next-to-last entry, and so forth.

Since lists are a mutable sequence type, we are allowed to modify the entries in-place:

numbers[0] = -3
numbers[2] += numbers[0]
print numbers

Another important way to refer to elements in a Python sequence type is slices, which allow the extraction of sublists from a list. Since this topic is very important for NumPy arrays, we defer the discussion to Appendix C, NumPy Arrays.

Python lists have a nice set of features, a few of which are illustrated in the following code examples:

  • To find the length of a list, use the following command:

    len(numbers)
    
  • To reverse a list in place, use the following command:

    numbers.reverse()
    print numbers
    
  • To append a new element, use the following command:

    numbers.append(35)
    print numbers
    
  • To sort the list in-place, use the following command:

    values = [1.2, 0.5, -3.4, 12.6, 3.5]
    values.sort()
    print values
    values.sort(reverse=True)
    print values
    
  • To insert a value at a position, use the following command:

    values.insert(3, 6.8)
    print values
    
  • To extend a list, use the following command:

    values.extend([7,8,9])
    print values
    

Python has a few handy ways to construct frequently used lists. The range() function returns a list of equally spaced integers. The simplest form returns a list of successive integers starting at 0:

range(10)

The preceding command returns the following list:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Note that the last element is one less than the argument given in the function call. The rule of thumb is that range(n) returns a list with n elements starting at zero so that the last element is n-1. To start at a nonzero value, use the two-argument version as follows:

range(3, 17)

A third argument specifies an increment. The following command line produces a list of all positive multiples of 6 that are less than 100:

range(6,100,6)

Negative increments can also be used:

range(20, 2, -3)

Lists support concatenation, which is represented by the + operator:

l1 = range(1, 10)
l2 = range(10, 0, -1)
l3 = l1 + l2
print l3

Note

Note that for the NumPy arrays, the + operator is redefined to represent vector/matrix addition.

The multiplication operator (*) can be used to construct a list by repeating the elements of a given list, as follows:

l4 = 3*[4,-1,5]
print l4

The most flexible way to construct a list in Python is to use a list comprehension. A full discussion is beyond the scope of this appendix, but the following examples illustrate some of the possibilities:

  • To display the list of the squares of the integers from 0 to 10 (inclusive), use the following command line:

    [n ** 2 for n in range(11)]
    
  • To display the list of divisors of an integer, use the following command lines:

    k = 60
    [d for d in range(1, k+1) if k % d == 0]
    
  • To display the list of prime numbers up to 100, use the following command line (very inefficient):

    [k for k in range(2,101) if len([d for d in range(1, k+1) if k % d == 0])==2]
    
  • To display the list of tuples of points with integers coordinates and their distances to the origin, use the following command line:

    [(i,j,(i*i+j*j)**0.5) for i in range(5) for j in range(6)]
    

Tuples

Tuples are similar to lists, but are immutable—once created, their elements cannot

be changed. The following command lines will result in an error message:

t1 = (2,3,5,7)
t1[2] = -4

Tuples have a few specialized uses in Python. They can be used as indexes in dictionaries (because they are immutable). They also consist of the mechanism that Python uses to return more than one value from a function. For example, the built-in function divmod() returns both the integer quotient and remainder in a tuple:

divmod(213, 43)

Tuples support the same sequence interface as lists, except for methods that would modify the tuple. For example, there is no method named sort() that sorts a tuple in place.

Strings

A Python string represents an immutable sequence of characters. There are two string types: str, representing ASCII strings, and unicode, representing Unicode strings.

A string literal is a sequence of characters enclosed by either single quotes or double quotes, as follows:

s1 = 'I am a string'
s2 = "I am a string"
print s1
print s2

There is no semantic difference between single quotes and double quotes, except that a single-quoted string can contain double quotes and a double quoted string can contain single quotes. For example, the following command lines are correct:

s3 = "I'm a string"
print s3

Strings are used for two main purposes: as dictionary indexes and to print messages. When printing messages, strings have the format() method that allows easy display of information. We use this feature frequently to add annotations to graphics. Here is an example:

n = 3
message = 'The square root of {:d} is approximately {:8.5f}.'.format(n, n ** 0.5)
print message

In the preceding example, there are two format specifiers:

  • {:d}: This specifies a decimal format for an integer value

  • {:8.5f}: This specifies a field of width 8 and 5 decimals for a floating-point value

The format specifications are matched (in order) with the arguments, in this case n and n ** 0.5.

Strings have a rich interface. If you need to code something with strings, it is very likely that there is a built-in function that does the job with very little modification. A list of all available string methods, as well as formatting features, is available at https://docs.python.org/2/library/stdtypes.html#string-methods.

Dictionaries


Python dictionaries are a data structure that contains key-item pairs. The keys must be immutable types, usually strings or tuples. Here is an example that shows how to construct a dictionary:

grades = {'Pete':87, 'Annie':92, 'Jodi':78}

To access an item, we provide the key as an index as follows:

print grades['Annie']

Dictionaries are mutable, so we can change the item values using them. If Jodi does extra work to improve her grade, we can change it as follows:

grades['Jodi'] += 10
print grades['Jodi']

To add an entry to a dictionary, just assign a value to a new key:

grades['Ivan']=94

However, attempting to access a nonexistent key yields an error.

An important point to realize is that dictionaries are not ordered. The following code is a standard idiom to iterate over a dictionary:

for key, item in grades.iteritems():
    print "{:s}'s grade in the test is {:d}".format(key, item)

The main point here is that the output is not at all related to the order in which the entries were added to the dictionary.

For more details on the dictionary interface, you can refer to https://docs.python.org/2/library/stdtypes.html#mapping-types-dict.

Control structures


Control structures allow changes to the flow of the execution of code. There are two types of structures that are of interest to us: branching and looping.

Branching allows the execution of different code depending on the result of a test. The following example shows an improved version of code to solve quadratic equations. An if-then-else structure is used to handle the cases of real and imaginary solutions, as follows:

a, b, c = 2., -4., 5.
discr = b ** 2 - 4 * a * c
if discr >= 0:
    sqroot = discr ** 0.5
    x1 = 0.5 * (-b + sqroot)
    x2 = 0.5 * (-b - sqroot)
else:
    sqroot = (-discr) ** 0.5
    x1 = 0.5 * (-b + sqroot * 1j)
    x2 = 0.5 * (-b - sqroot * 1j)
print x1, x2

The preceding code starts by computing the discriminant of the quadratic. Then, an if-then-else statement is used to decide if the roots are real or imaginary, according to the sign of the discriminant. Note the indentation of the code. Indentation is used in Python to define the boundaries of blocks of statements. The general form of the if-then-else structure is as follows:

if <condition>:
     <statement block T>
else:
     <statement block F>

First, the condition <condition> is evaluated. If it is True, the statement <statement block T> is executed. Otherwise, <statement block F> is executed. The else: clause can be omitted.

The most common looping structure in Python is the for statement. Here is an example:

numbers = [2, 4, 3, 6, 2, 1, 5, 10]
for n in numbers:
    r = n % 2
    if r == 0:
        print 'The integer {:d} is even'.format(n)
    else:
        print 'The integer {:d} is odd'.format(n)

We start by defining a list of integers. The for statement makes the variable n assume each value in the list numbers in succession and execute the indented block for each value. Note that there is an if-then-else structure inside the for loop. Also, the print statements are doubly-indented.

A for loop is frequently used to perform simple searches. A common scenario is the need to step out of the loop when a certain condition is met. The following code finds the first perfect square in a range of integers:

for n in range(30, 90):
    if int(n ** 0.5) ** 2 == n:
        print n
        break

For each value of n in the given range, we take the square root of n, take the integer part, and then calculate its square. If the result is equal to n, then we go into the if block, print n, and then break out of the loop.

What if there are no perfect squares in the range? Change the preceding function, range(30, 60), to range(125, 140). When the command line is run, nothing is printed, since there are no perfect squares between 125 and 140. Now, change the command line to the following:

for n in range(125, 140):
    if int(n ** 0.5) ** 2 == n:
        print n
        break
else:
    print 'There are no perfect squares in the range'

The else clause is only executed if the execution does not break out of the loop, in which case the message is printed.

Another frequent situation is when some values in the iteration must be skipped. In the following example, we print the square roots of a sequence of random numbers between -1 and 1, but only if the numbers are positive:

import random
numbers = [-1 + 2 * rand() for _ in range(20)]
for n in numbers:
    if n < 0:
        continue
    print 'The square root of {:8.6} is {:8.6}'.format(n, n ** 0.5)

When Python meets the continue statement in a loop, it skips the rest of the execution block and continues with the next value of the control variable.

Another control structure that is frequently used is the while loop. This structure executes a block of commands as long as a condition is true. For example, suppose we want to compute the running sum of a list of randomly generated values, but only until the sum is above a certain value. This can be done with the following code:

import random
bound = 10.
acc = 0.
n = 0
while acc < bound:
    v = random.random()
    acc += v
    print 'v={:5.4}, acc={:6.4}'.format(v, acc)

Another common situation that occurs more often than one might expect requires a pattern known as the forever loop. This happens when the condition to be checked is not available at the beginning of the loop. The following code, for example, implements the famous 3n+1 game:

n = 7
while True:
    if n % 2 == 0:
        n /= 2
    else:
        n = 3 * n + 1
    print n
    if n == 1:
        break

The game starts with an arbitrary integer, 7 in this case. Then, in each iteration, we test whether n is even. If it is, we divide it by 2; otherwise, multiply it by 3 and add 1. Then, we check whether we reached 1. If yes, we break from the loop. Since we don't know if we have to break until the end of the loop, we use a forever loop as follows:

while True:
   <statements>
   if <condition>:
        break
   <possibly more statements>

Some programmers avoid this construct, since it may easily lead to infinite loops if one is careless. However, it turns out to be very handy in certain situations. By the way, it is an open problem if the loop in the 3n+1 problem stops for all initial values! Readers may have some fun trying the initial value n=27.

Functions, objects, and methods

We now come to the constructs that really make Python so flexible and powerful, its object-oriented features. We have already seen some examples of object-oriented code in the previous sections (the object-oriented paradigm is so integral to Python that is hardly possible to write any code without using it), but now we will have a more specific treatment of these features.

Functions

We have already seen many examples of functions being used. For example, the len() function is used to compute the length of a list:

lst = range(1000)
print len(lst)

The most basic syntax for calling a function is as follows:

function_name(arg1, arg2, …, argn)

In this case, arg1, arg2, …, argn are called positional arguments, since they are matched according to the position in which they appear. As an example, let's consider the built-in function, pow(). This function takes up to three arguments:

pow(b, n, m)

In this form, the preceding function uses an optimized algorithm to compute b raised to the power n modulo m. (If you are wondering, this is an important operation in public key cryptography, for example.) The arguments b, n, and m are associated by their position. For example, to compute 12 raised to the tenth power modulo 15, we use the following command:

pow(12, 10, 15)

Python also supports sequences of arguments of arbitrary size. For example, the max() function computes the maximum of an arbitrary sequence of values:

max(2,6,8,-3,3,4)

The preceding command returns the value 8.

A third way to pass arguments to a function is to use keyword arguments. This turns out to be very useful, since it is in general difficult to remember the exact order of positional arguments. (I would prefer not to write a function with more than three or four positional arguments, for example.)

For example, the built-in int() function can be used to convert a string to an integer. The optional keyword argument, base, lets us specify the base for conversion. For example, the following command line assigns to n, an integer given in base 2:

n = int('10111010100001', base=2)
print n

Keyword arguments always have a default value. In our example, if the base is not specified, it is assumed to be 10.

We often need to write our own functions. This is done with the keyword, def. As an example, let's consider writing code to implement the well-known bisection method to solve equations numerically. A possible solution is as follows:

def bisection(f, a, b, tol=1e-5, itermax=1000):
    fa = f(a)
    fb = f(b)
    if fa * fb > 0:
        raise ValueError('f(a) and f(b) must have opposite signs')
    niter = 0
    while abs(a-b) > tol and niter < itermax:
        m = 0.5 * (a + b)
        fm = f(m)
        if fm * fa < 0:
            b, fb = m, fm
        else:
            a, fa = m, fm
    return min(a, b), max(a, b)

The preceding function takes three important and necessary arguments:

  • The f function accepts a float value as input and returns a float value as output

  • The floating-point values, a and b, which specify an interval that contains a zero of the function

The other two arguments are optional. The argument tol specifies the desired tolerance in the result and itermax specifies the maximum number of iterations. To use the bisection() function, we must first define the function f. We will take the opportunity to display another way to define a function in Python, as follows:

from math import cos, pi
f = lambda x: cos(x) - x

We are now ready to call the function with the following command:

bisection(f, 0, pi/2)

The preceding function returns the following output:

(0.7390851262506977, 0.7390911183631504)

Note that we designed the function to return an interval containing the zero. The length of the interval is less than tol, unless the maximum number of iterations is reached. If we want a smaller tolerance, we could use the following function:

bisection(f, 0, pi/2, tol=1E-10)

Now, suppose that we are concerned with the time the computation is taking. We can limit the maximum number as follows:

bisection(f, 0, pi/2, itermax=10, tol=1E-20)

Note that the order in which the keyword arguments are given is irrelevant and the desired tolerance is not reached in the preceding example.

Objects and methods

Objects are the most general data abstraction in Python. Actually, in Python, everything is an object from the point of view of the programmer.

An object is nothing more than a collection of structured data, together with an interface to operate on this data. Objects are defined using the class construct, but our goal here is not to show how to define classes. Although designing a new class is an advanced topic, using existing classes is pretty straightforward.

As an example, let's explore the built-in type str. Let's start by defining a str object we can play with as follows:

message = 'Mathematics is the queen of science'

To start, let's convert the message to uppercase as follows:

message.upper()

We say that the preceding statement calls the upper() method of the message object. A method is simply a function that is associated to an object. The following are a few other methods of the str objects:

  • To find the first occurrence of a substring (returns -1 if the string is not found), use the following command line:

    message.find('queen')
    
  • To split the string in words, use the following command line:

    words = message.split()
    print words
    
  • To count the number of occurrences of s substring, use the following command line

    message.count('e')
    
  • To replace a substring by something else, use the following command line:

    message.replace('Mathematics', 'Mme. Curie')
    

Note

Note that the preceding methods do not change the original string object, but return new modified strings. Strings are immutable. For mutable objects, methods are free to change the data in the object.

Summary


In this appendix, we gave an overview of the Python syntax and features, covering basic types, expressions, variables, and assignment, basic data structures, functions, objects, and methods.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
IPython Notebook Essentials
Published in: Nov 2014Publisher: ISBN-13: 9781783988341
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Luiz Felipe Martins

Luiz Felipe Martins holds a PhD in applied mathematics from Brown University and has worked as a researcher and educator for more than 20 years. His research is mainly in the field of applied probability. He has been involved in developing code for the open source homework system, WeBWorK, where he wrote a library for the visualization of systems of differential equations. He was supported by an NSF grant for this project. Currently, he is an Associate Professor in the Department of Mathematics at Cleveland State University, Cleveland, Ohio, where he has developed several courses in applied mathematics and scientific computing. His current duties include coordinating all first-year calculus sessions.
Read more about Luiz Felipe Martins