Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268

Warning: (6.034) preg_replace_callback(): Unknown modifier 'c' in /afs/athena.mit.edu/course/6/6.034/web_scripts/wiki/extensions/SyntaxHighlight_GeSHi/geshi/geshi.php on line 3268
Python for Schemers - 6.034 Wiki

Python for Schemers

From 6.034 Wiki

Jump to: navigation, search

Contents

Hello World

Scheme:

"Hello world!"

Python:

print "Hello world!"


Recursion vs. Iteration

Many of Python's constructs are built around iteration, while Scheme's are built around recursion. You can iterate in Scheme and recurse in Python, but it's messier.

Python has built-in syntax for iterating over a list, for ... in .... In fact, other kinds of iteration, such as over a sequence of numbers, are expressed in terms of this. Here's an example of iteration:

>>> menu = ['spam', 'spam', 'spam', 'eggs',
           'baked beans', 'spam']
 
>>> for item in menu:
        print item.upper()
# Result:
SPAM
SPAM
SPAM
EGGS
BAKED BEANS
SPAM

Consider this factorial code in Scheme:

 

You could translate it literally into Python like this:

def factorial(n):
    if n == 0: return 1
    else: return n * factorial(n-1)

But the more efficient and natural way to write it is by iterating over a range() of numbers.

def factorial(x):
    product = 1
    for i in range(1, x+1):
        product *= i
    return product

range(1, x+1) gives you the numbers from 1 (inclusive) to x+1 (exclusive). For example, range(1, 4) is (1, 2, 3). The endpoint is omitted so that the difference between the end and start is the number of items you get in the range.

More things you can do with lists

You index lists with the [] operator. Indices start at 0. So menu[0] is "spam", and menu[3] is "eggs".

You can also get a slice of a list, using a range of indices that acts much like the range function above. Again, it includes the starting point, and excludes the endpoint. menu[1:4] is ["spam", "spam", "eggs"].

>>> menu.append("spaaaaaaaaaam")
>>> menu
["spam", "spam", "spam", "eggs", "baked beans", "spam", "spaaaaaaaaaam"]
 
>>> len(menu)
7
>>> menu.count("spam")
4
>>> if 'spam' in menu: print "I don't like spam"
... 
I don't like spam
>>> menu[2] = 'ham'
>>> menu
['spam', 'spam', 'ham', 'eggs', 'baked beans', 'spam', 'spaaaaaaaaaam']
>>> sorted(menu)
['baked beans', 'eggs', 'ham', 'spaaaaaaaaaam', 'spam', 'spam', 'spam']

Tuples contain separate items just like lists do, but they are immutable; their contents never change.

>>> the_tuple = (3, 4, 5)
>>> the_tuple[1] = 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Dictionaries

Dictionaries are another kind of data structure. They contain values like lists do, but they are unordered, and you can use any immutable object as the index.

phrase = "and now for something completely different"
def count_letters(text):
    counts = {}
    for letter in text:
        if letter not in counts:
            counts[letter] = 0
        counts[letter] += 1
    return counts
 
count_letters(phrase)
 
# Result:
{'a': 1, ' ': 5, 'c': 1, 'e': 5, 'd': 2, 'g': 1, 'f': 3, 'i': 2,
'h': 1, 'm': 2, 'l': 2, 'o': 4, 'n': 4, 'p': 1, 's': 1, 'r': 2,
't': 3, 'w': 1, 'y': 1}

List comprehensions

In Scheme, you would often use map and filter to operate on lists.

; Result: (0 1 4 9 16 25 36 49 64 81)
; Result: (1 9 25 49 81)

Python has special syntax that incorporates both map and filter, called "list comprehensions".

[x*x for x in range(10)]
# Result:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
# This version adds a filter, testing if x is odd,
# using the modulo operator x % 2.
 
[x*x for x in range(10) if x % 2 == 1]
# Result:
[1, 9, 25, 49, 81]

Modules and inline help

Python comes with a standard library with many useful functions and classes. They don't clutter up the global namespace; instead, they are in modules that you can load:

>>> import math
>>> math.pi
3.1415926535897931

When you're at an interactive Python prompt, you can get help on any module, class, or function just by running help() on it:

>>> import math
>>> help(math)
Help on module math:

NAME
    math

FILE
    /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload/
math.so

MODULE DOCS
    http://www.python.org/doc/current/lib/module-math.html

DESCRIPTION
    This module is always available.  It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)
        
        Return the arc cosine (measured in radians) of x.

[...and many pages more...]

If you are running ipython as your interactive Python interpreter, you can also get a brief summary of what an object is by typing object?, and see its source code by typing object??. ipython can be a great way of experimenting with Python code. Type ? for a summary of what ipython can do.

Classes

In addition to the basic data structures such as lists, tuples, and dictionaries, you can also define new object-oriented data structures. (In fact, even lists, tuples, and dictionaries are object-oriented.)

class Point(object):
    # When you start a class or a function with a plain string,
    # it becomes a "docstring" that documents that class or function.
    # Here comes one now.
 
    "A 2-D point in rectangular coordinates."
 
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
    def radius(self):
        return math.sqrt(self.x**2 + self.y**2)
 
    def angle(self):
        return math.atan2(self.y, self.x)
 
    def distance_to(self, other):
        return math.sqrt((self.x - other.x)**2 +
                         (self.y - other.y)**2)
    def __repr__(self):
        # The __repr__ of an object determines how it prints.
        # In particular, we print one of these objects using
        # the "fill-in-the-blanks" string operator, %.
        # This replaces occurrences of "%s" with items from a tuple.
 
        return "Point(%s, %s)" % (self.x, self.y)
 
# Note that all these methods needed to be defined with "self" as
# the first argument, but you leave out "self" when you call these
# methods.
 
>>> p = Point(2, 3)
>>> p
Point(2, 3)
>>> p.radius()
3.6055512754639891
>>> p.angle()
0.98279372324732905
>>> p.distance_to(Point(1, 4))
1.4142135623730951

Lexical scope

You may remember the concept of "lexical scope" from Scheme. Every function and let statement introduces a new scope where variables can be defined. When looking up the value of a variable, Scheme starts from the current scope (or "frame"), then looks outward to the scope containing that one, and so on until it reaches the global environment.

That lets you do neat tricks like this, creating a function that saves state from one call to the next:

 

Python only has two scopes: global and local. Each function has its own local scope. Whenever you assign to a variable using = inside a function, you're assigning to a local variable.

(You can use the global keyword to get a global variable that you can assign to, but if you have to assign to a global variable from within a function, it's a good sign that you're doing things wrong.)

This means that, even though Python also has nested functions that can be returned as values, the direct translation of the Scheme code above won't quite work:

def make_counter():
    count = 0
    def the_counter():
        count += 1
        return count
    return the_counter
 
>>> c = make_counter()
>>> c()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "examples.py", line 117, in the_counter
    count += 1
UnboundLocalError: local variable 'count' referenced before assignment

To do this, we would need to pass the variable "count" into the inner function. One way to do that is like this, where we define an inner variable named "count" whose default value is the other "count" to bridge the gap:

def make_counter():
    count = 0
    def the_counter(count=count):
        count += 1
        return count
    return the_counter

But this is clunky. In Python, when you want a function to keep state with it, you often do that by making it a method on a class:

class Counter(object):
    def __init__(self): self.counter = 0
    def count(self):
        self.counter += 1
        return self.counter

But you can also use a generator, which is essentially a function that remembers its state and can return multiple times. You turn a function into a generator by using the yield keyword inside it.

def gen_counter():
    n = 0
    while True:
        n += 1
        yield n
 
>>> counter = gen_counter()
 
# If you just look at the result, it's an opaque "generator" object.
>>> counter
<generator object at 0x82f58>
>>> counter.next()
1
>>> counter.next()
2
>>> for n in counter:
...     squared = n*n
...     if squared > 300: break
...     print squared, 
9 16 25 36 49 64 81 100 121 144 169 196 225 256 289

And now, your Moment of Zen

I leave you with an Easter egg in Python that explains a bit of its design philosophy:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Personal tools