Lesson 04 - Cake

So far, I've skipped over a lot of language features and little "cool things" that you can do with Python. I chose to do so for brevity in previous lessons. Some of them will look very foreign if you haven't seen them before. Almost all of them are just plain cool. Every single one will make your life as a programmer easier, if used properly.

Contents

Store anything in a variable

In addition to storing things like strings, integers, and instances of an object, you can store just about anything.

Let's define a small function:

def hello_world():
    print "Hello World"

Now, let's look at it in an interactive Python interpretor:

>>> hello_world()
Hello World
>>> hello_world
<function hello_world at 0x9c4bf44>
>>> abc = hello_world
>>> abc()
Hello World
>>> abc
<function hello_world at 0x9c4bf44>

Notice that the memory address is the same for both functions. I can even delete the original function:

>>> del(hello_world)
>>> hello_world
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  NameError: name 'hello_world' is not defined

But del only removes the reference. The function itself is still in memory, and I can still get to it through the new variable:

>>> abc
<function hello_world at 0x9c4bf44>

Decorators

A decorator is a way to change the behavior of a function without having to rewrite it. There are two different syntaxes for decorators.

The older syntax looks like this:

def marshmellow(func):
    def marsh():
        return func()+.25
    return marsh

def hot_chocolate():
    return 1.00
hot_chocolate = marshmellow(hot_chocolate)

The newer syntax looks like this:

def marshmellow(func):
    def marsh():
        return func()+.25
    return marsh

@marshmellow
def hot_chocolate():
    return 1.00

Decorators can even be stacked:

@marshmellow
@marshmellow
def hot_chocolate():
    return 1.00

Lambda Functions

A lambda function is a feature that was added early on in the life of Python. It was added in Python 1.0 because someone missed them from lisp, and submitted a working patch.

A lambda function is a function that is basically a one liner. It has compact syntax, and is most definitely useful for making your code smaller. It has the misfortune of the syntax not being blindingly obvious if you haven't seen it before, but those familiar with lambda functions find that it makes their code cleaner and more concise.

Let's create a lambda function and a regular function and compare them:

def regular_hello(planet):
    return "Hello " + planet

lambda_hello = lambda planet: "Hello " + planet

Now, let's compare them in an interactive interpretor:

>>> regular_hello
<function regular_hello at 0xa0531ec>
>>> lambda_hello
<function <lambda> at 0xa04ad84>
>>> dir(regular_hello)
['__call__', '__class__', '__closure__', '__code__', '__defaults__',
'__delattr__', '__dict__', '__doc__', '__format__', '__get__',
'__getattribute__', '__globals__', '__hash__', '__init__', '__module__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure',
'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals',
'func_name']
>>> dir(lambda_hello)
['__call__', '__class__', '__closure__', '__code__', '__defaults__',
'__delattr__', '__dict__', '__doc__', '__format__', '__get__',
'__getattribute__', '__globals__', '__hash__', '__init__', '__module__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure',
'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals',
'func_name']

It looks like all the same parts are present. Let's try using both to see if they behave differently:

>>> regular_hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: regular_hello() takes exactly 1 argument (0 given)
>>> lambda_hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (0 given)

It looks like the function name is different, but both return a TypeError if there are no arguments.

>>> regular_hello("World")
'Hello World'
>>> lambda_hello("World")
'Hello World'

Both seem to behave the same when we give them a string object (or anything else that can be concatenated with a string via the + operator)

The only difference is that the syntax is a bit different, and a lambda function has no actual name. Sometimes, instead of assigning a lambda function to a variable, you can define it in line where you would normally pass a function name. A common example of this is the filter function, reduce function, etc:

>>> filter(lambda x: not x % 3, range(10))
[0, 3, 6, 9]

This essentially says "leave all numbers between 0 and 9 that have no remainder when divided by three."

List Comprehensions

List comprehensions are a very powerful tool. In my calculus class, I'd check my series and sequence solutions using a list comprehension.

A common way to illustrate list comprehensions is using a mathematical construct, such as listing the squares of integers:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Here's something a little more complex. Let's use a list comprehension to build a list of database options as defined by a dictionary:

>>> opts = {'engine': 'mysql', 'hostname': 'localhost', 'dbname': 'super_db',
... 'user': 'root', 'pass': 'secret', 'port': 3306}
>>> list_opts=[str(key)+"="+str(opts[key]) for key in opts.keys()]
>>> list_opts
['engine=mysql', 'hostname=localhost', 'dbname=super_db', 'user=root',
'pass=secret', 'port=3306']

So I've effectively reduced a dictionary to a list with all the key/value pairs. This can be useful in some cases. A database connection string may need to be parsed out of a dictionary and passed on to the database engine.

You can take this one step further, and using reduce and a lambda function, make this list into a single string that looks like raw POST data:

>>> post_data=reduce(lambda x,y: x+'&'+y, list_opts)
>>> post_data
'engine=mysql&hostname=localhost&dbname=super_db&user=root&pass=secret&port=3306'

Property

In many object oriented languages, direct access to an attribute is supported. Say I have an instance of an object called zebra. I have decided to name it, so I do something like:

>>> zebra.name = "Spot"

This most certainly works in Python, but this can get messy. What is stopping me from assigning anything but a string there? Why not assign an integer? A function? A reference to the object itself? Nothing! Furthermore, once a pet is named, it is not typical that its name changes. What is stopping me from legally changing this poor zebra's name fifteen times per minute?

The solution in many cases is to make zebra.name a private variable, so outside code can't touch it. The programmer of the zebra class will have left some function to return the current value, and set it to a new value. Unfortunately, there is no way to easily predict what this might be. I know the data is stored in a variable called name, but what functions are there? I could try set_name("Spot"), or I could try SetName("Spot"). Ultimately, the programmer could have chosen anything.

So both of these options, even though they work in Python, aren't ideal. Let's combine them! We can easily have the best of both worlds.

Let's look at our animal class:

class animal(object):
    _name=""
    def get_name(self):
        "The name of the animal"
        return self._name
    def set_name(self, value):
        if not self._name: self._name = str(value)
        else raise Exception("Your animal already has a name: %s" % self._name)
    def del_name(self):
        del self._name
    name = property(get_name, set_name, del_name)

This class's name property looks and acts much like storing the value there directly. Another programmer may have no idea that you are even using the property feature. The only thing that could happen is if you call set_name twice, you'll get an exception.

Now that I have an animal class that implements the policy on the name, let's use it:

>>> zebra = animal()
>>> zebra.name
''
>>> zebra.name="Spot"
>>> zebra.name
'Spot'
>>> zebra.name="Milo"
Traceback (most recent call last):
  ...
  Exception: Your animal already has a name: Spot
>>> del(zebra.name)
>>> zebra.name="Milo"
>>> zebra.name
'Milo'

Pickle

Pickle is a module that provides object serialization. You can take (almost) any Python object, and run it through the pickle serializer. The output is a string. You can store it in a database, send it in an e-mail, save it to disk, or even memorize it. When you input that string to be de-pickled, the result will be another Python object that you can put anywhere you want (store in a variable, list, dictionary, etc).

I won't walk you through the use of the pickle module, but here's the link to its documentation: http://docs.python.org/library/pickle.html

Less commonly used

Python classes are very mature and very feature complete. It supports some very advanced constructs.

Nested Classes/Functions

There really aren't any major differences if you choose to define one class inside another, but it allows for greater control over the organization of your code.

One example might be implementing a Linked List data structure, which requires a node to work. You could define the node class inside the parent class to keep your code and namespace litter free:

class LinkedList(object):
    """A Linked List class"""
    def __init__():
        pass

    class LinkedListNode(object):
        """A Linked List node class"""
        def __init__():
            pass

We have already seen an example of defining a function inside a function. A decorator function is simply a function that returns another function, so we defined that new function in-line.

Meta Classes

Defining a metaclass allows you to do some very very complex, way cool stuff. Django has some very powerful features because of metaclasses.

If you want to know more about them, see comp.lang.python or Google for them. Basically, they are a very advanced Python feature, and if you think you need them, you probably don't.

Python 2.6/3.0 features

The reason Python 2.6 was released was to provide a stepping stone for existing Python 2.x code to be ported to Python 3.0. There are quite a few more changes than those listed here, but these looked interesting enough to present on.

with

with open('/etc/passwd', 'r') as pass:
    for line in pass:
        print line

The expression is evaluated, and stored into the supplied variable. This only works if the object returned by the expression supports the context management protocol (__enter__ and __exit__ functions)

See Also: PEP 343, What's new in Python 2.6: the 'with' Statement

Advanced String Formatting

Instead of using the % operator for string formatting, you can use string.format(). Here are the examples taken from the "What's new" document for Python 2.6.

>>> # Substitute positional argument 0 into the string.
>>> "User ID: {0}".format("root")
'User ID: root'
>>> # Use the named keyword arguments
>>> "User ID: {uid}   Last seen: {last_login}".format(
...    uid="root",
...    last_login = "5 Mar 2008 07:20")
'User ID: root   Last seen: 5 Mar 2008 07:20'

This new method of string formatting is much more powerful, and is a pleasure to work with.

See Also: PEP 3101