Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 16658

Math 152: Intro to Mathematical Software

2017-01-18

Kiran Kedlaya

**Lecture 4: Introduction to Python (part 2) **

Administrivia:

  • Peer evaluations for Homework 1 due Thursday, January 19, at 8pm

  • Homework 2 due Tuesday, January 24 at 8pm. Remember, put all your work in the "assignments/2017-01-24" folder; it will be collected automatically after the deadline passes.

  • Guest lecturer Friday, January 20: William Stein, the creator of SageMath and SageMathCloud.

Today's topic is composite data types (lists, tuples, dicts).

Lists

A list is a Python type consisting of an ordered list of other objects. This is similar to the concept of an array in other programming languages; however, in Python the objects of a list can be of different types (e.g., numbers and strings can be combined in the same list).

If l is a list, then the objects of the list can be retrieved as l[0], l[1], ...; note that the first index is 0, not 1. The last items in a list can be retrieved as l[-1], l[-2], ....

l = [2, 3, "ab"] print(l[0]) print(l[-1])
2 ab
%md Here are some examples of operations on lists.

Here are some examples of operations on lists.

l = [2,5,3] print(len(l)) # Length of a list
3
print(4 in l) # Test for membership in a list
False
l += [6,7] # The + operator concatenates lists print(l)
[2, 5, 3, 6, 7]
del l[3] # Remove an element from a list print(l)
[2, 5, 3, 7]
print(l[:]) # This creates a new list consisting of a certain range of elements of the original list.
[2, 5, 3, 7]
%md Many of the available operations on lists, and on other Python objects, are accessed using a slightly different syntax than the one used so far. Look at these examples.

Many of the available operations on lists, and on other Python objects, are accessed using a slightly different syntax than the one used so far. Look at these examples.

l.sort() # Sort the list print(l)
[2, 3, 5, 7]
l.append(8) # Add an element to a list print(l)
[2, 3, 5, 7, 8, 8, 8]

These kinds of functions are specific to a certain type of object, in this case lists; they are called methods of the object class. This is a symptom of the fact that Python is an object-oriented programming language; we will see later how Sage takes advantage of this architecture to implement mathematical objects.

In the meantime, I point out two ways to find out more about the methods associated to a particular class of objects.

  1. Use tab-completion: type l. and hit the Tab key to see a list of methods associated to l. If you have typed some letters after the period before hitting Tab, you'll only get objects whose names match the initial string you typed.

  2. Look at the page from the Python documentation corresponding to the type. For example, for lists this page is https://docs.python.org/2/tutorial/datastructures.html.

# Try tab-completion here!
[2, 3, 5, 7, 8, 8, 8] 5
%md ## List Comprehensions

List Comprehensions

Mathematicians are fond of constructing sets out of other sets. Python offers a similar syntax:

[expression(x) for x in [list]] [expression(x) for x in [list] if something(x)] [expression(x, y) for x in [list] for y in [another list] if something(x, y)]

et cetera.

[n for n in range(-3,5)] # you can give a start value for range
[-3, -2, -1, 0, 1, 2, 3, 4]
[n*2 for n in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[n for n in range(20) if n%2 == 0]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[[x,y] for x in range(4) for y in ['X','Y','Z'] if x%2 == 0]
[[0, 'X'], [0, 'Y'], [0, 'Z'], [2, 'X'], [2, 'Y'], [2, 'Z']]

You can typically rewrite a list comprehension as a for loop (or nested for loops).

v = [] for n in range(10): v.append(n) print v
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

KEY INSIGHT: The order of the for loops and the if are exactly the same as in the list comprehension.

v = [] for x in range(4): for y in ['X','Y','Z']: if x % 2 == 0: v.append([x,y]) print v
[[0, 'X'], [0, 'Y'], [0, 'Z'], [2, 'X'], [2, 'Y'], [2, 'Z']]

Try this now:

Construct the following list using a list comprehension and the if/else exercise function my_sign you wrote last time:

[[-3,'negative'], [-2,'negative'], [-1,'negative'], [0,'zero'], [1,'positive'], [2,'positive'], [3,'positive']]

If you have time, do the same but using a for loop.

Warning: lists are mutable

Python Lists

There is a serious subtlety with how Python lists works. This is a frequent source of errors for new Python users.

In Python, objects are copied by reference, not by value. That is, when you perform the assignment b = a, Python does not assign a copy of the value of a to the new variable b; rather, a and b are both references to the same object in memory. (This is different from MATLAB, for example.)

This is already true for basic objects like integers, but isn't really an issue.

a = 3 b = a # Now a and b point to the same object in memory, a copy of the integer 3. b += 4 # This creates a new integer and makes b point to it instead, so a remains intact. print(a,b)
(3, 7)
print(id(a), id(b)) # This proves that a and b are now pointing to different places in memory
(140404786719216, 140404786720368)

By contrast, basic operations on lists change the original list, rather than creating a new one. This can lead to unexpected side effects.

a = [3,5] b = a b.append(7) print(a)
[3, 5, 7]
print (id(a), id(b))
(140404846383544, 140404846383544)

For comparison, here is an example of arrays in Matlab. (Technically, this is not Matlab but Octave, an open-source clone of MATLAB which is available from within Sage.)

%octave a = [1,2,3]; b = a; b(1) = 5; a b
a = 1 2 3 b = 5 2 3

And R (1-based and copy by value):

%r a <- c(1,2,3); b <- a; b[1] = 5; a b
[1] 1 2 3 [1] 5 2 3

Here's what happens in Javascript (0-based and copy by reference):

%javascript a = [1,2,3]; b = a; b[0] = 5; print("a =",a) print("b =",b)

And in Julia (1-based and copy by reference!):

%julia a = [1,2,3]; b = a; b[1] = 5; println("a =", a, "\nb =", b);
a =[5,2,3] b =[5,2,3]

The point is that different programming languages make very subtle and different design choices. And that's all they are -- choices. One choices really is better for some purposes and worse for other purposes.

The copy by reference choice of Python makes lists potentially subtle. For example:

v = [1,2,3] w = [v, v, [1,2,3]] w
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
w[0][0] = 'sage'

Now what the heck is w equal to? (Wait until lots of people in class think this through.)

print w
[['sage', 2, 3], ['sage', 2, 3], [1, 2, 3]]
[id(x) for x in w]
[139885300265904, 139885300265904, 139885298619168]

Moral: Just because something prints out at [[1, 2, 3], [1, 2, 3], [1, 2, 3]] doesn't mean you know what that something is! In Python, things can be very subtle.

Don't judge an object by its cover (how it prints).

If you don't get this point, you might hate Python. If you do, you might start to see huge power and flexibility in what Python offers... and understand why Python might be the most popular programming language in data science, which is itself a very popular emerging field.

Shallow and deep copies.

Let's try the copy module from Python's standard library: https://docs.python.org/2/library/

import copy # this is how you import a module in Python v = [1,2,3] w = [v, v, [1,2,3]] w1 = copy.copy(w) # how to use a function defined in a module w1[0][0] = 'hi' print "w1 = ", w1 print "w = ", w # not much of a copy!!! It just copies the top level references
w1 = [['hi', 2, 3], ['hi', 2, 3], [1, 2, 3]] w = [['hi', 2, 3], ['hi', 2, 3], [1, 2, 3]]
copy.
# Question: will this change w? w1[0] = 'hi' print w # what do you expect
[['hi', 2, 3], ['hi', 2, 3], [1, 2, 3]]
# To really copy, use deepcopy: import copy # this is how you import a module in Python v = [1,2,3] w = [v, v, [1,2,3]] w1 = copy.deepcopy(w) # uses more memory; causes more work -- and that's why we want options w1[0][0] = 'hi' print "w1 = ", w1 print "w = ", w # yep, not touched by changing w
w1 = [['hi', 2, 3], ['hi', 2, 3], [1, 2, 3]] w = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]

MORAL: Python is a real programming language designed by software engineers. It's not a special-purpose math-only language, and it respects deeper, and more powerful, subtle (and possibly confusing) ideas from programming.

Some more about data structures: tuples, dicts, sets

tuples

  • Like Python lists, except you use parens instead of square brackets

  • You can't change the number of entries or what they reference. However, you might be able to change the referenced thing itself.

v = [['x','y'], 2, 5] # a list t = (['x', 'y'], 2, 5) # a tuple print "v =", v print "t =", t
v = [['x', 'y'], 2, 5] t = (['x', 'y'], 2, 5)
t[1] = 3
Error in lines 1-1 Traceback (most recent call last): File "/projects/sage/sage-7.3/local/lib/python2.7/site-packages/smc_sagews/sage_server.py", line 976, in execute exec compile(block+'\n', '', 'single') in namespace, locals File "", line 1, in <module> TypeError: 'tuple' object does not support item assignment
v[1] = 3
t = (1,2,7)
t
(1, 2, 7)
v.append("something") # lists allow for lots of exciting ways of changing them v
[['x', 'y'], 3, 5, 'something']
del v[3] v
[['x', 'y'], 3, 5]
t[1] = 3 # not allowed!
Error in lines 1-1 Traceback (most recent call last): File "/projects/sage/sage-7.3/local/lib/python2.7/site-packages/smc_sagews/sage_server.py", line 976, in execute exec compile(block+'\n', '', 'single') in namespace, locals File "", line 1, in <module> TypeError: 'tuple' object does not support item assignment
t.append # not so for tuples
# However, and this is critical -- you can change v[0] or t[0] itself! v[0].append('z') v
t[0].append('z') # this doesn't change t -- # think of t as references to 3 specific objects -- you are changing one of the referenced objects, not t itself!
a = (1,[2,3],4) a[1].append(5) print(a)
(1, [2, 3, 5], 4)
t

Try this now: Write a function that takes as input a tuple and returns the sorted version of the tuple.

# Hints to make this really easy: v = (1,7, 4) sorted(v) # returns sorted list obtained from a tuple (or whatever)
[1, 4, 7]
tuple([4,7,8]) # tuple of a list makes a tuple
def sorted_tuple(x): return # what goes here? something
sorted_tuple((7,3,5)) # test your function!
s = set([1,3,2])
print(s)
set([1, 2, 3])
s.union(set([-1,2]))
set([1, 2, 3, -1])
2 in s
True