Fork me on GitHub

Some Black Magic Python for n00bs

I had lunch with an old friend yesterday and we were discussing Python. He had a background in Perl and PHP so I knew some of the higher-order aspects of Python wouldn’t be clear to him yet. He also had rudimentary knowledge of Python decorators, a tool I use all the time.

In an effort to help, I wrote up some code that demonstrates some of these concepts. I think it will be useful to readers of this blog too.

Higher Order Uses of Python

Python gives us some features found in functional languages. I make use of the higher-order features all the time.

The basic idea is that you can write code that operates on code similarly to how it operates on data. For example, I can define a function and then pass it to some other function to use later.

def foo():
    print 'foo'

def bar():
    print 'bar'

list_of_funs = [foo, bar, foo, foo, bar]
for fun in list_of_funs:
    fun()

def call_fun(fun):
    fun()

call_fun(foo)
call_fun(bar)

On Duck Typing

If it looks like a dict and smells like a dict, well, let’s just treat it like a dict! (more here)

class Foo(object):
    def __init__(self, *args, **kwargs):
        self.bar = 'bar'
        self._dict = dict()

    ## Make our Foo object behave like a dictionary
    def __setitem__(self, key, value):
        self._dict[key] = value
        
    def __getitem__(self, key):
        return self._dict[key]

## Create a foo instance
f = Foo()

## Treat it like a dictionary
f['some_key'] = 'some value'
print f['some_key']

## Set some more values
f['whatevz'] = 'meow meow meow'
f['dude'] = 'wheres your car?'
f['wheres your car?'] = 'dude'

A Caveat

We have only implemented the code to handle get and set like a dictionary.

for k,v in f.items():
    print 'array[%s] => %s' % (k, v)

Hey wait… that loop didn’t work, did it? It’s because f isn’t a complete dictionary implementation. We only implemented the part that lets you set key-value pairs or retrive a value by key.

Read more here: http://docs.python.org/release/2.6.6/howto/descriptor.html

On List Comprehensions and Generators

First, some code using list comprehensions

some_list = [1,2,3,4,5]
squares = [x**2 for x in some_list]
print squares

another_list = [9,8,7,6,5]
added_squares = [x**2 + y**2 for x in some_list for y in another_list]
print added_squares

And now for the same idea with a generators.

some_list = [1,2,3,4,5]
squares = (x**2 for x in some_list) # similar syntax
print squares

We didn’t get a list back that time. But check out what it does when we treat it like a list.

for square in squares:
    print square

It printed the list! But why isn’t squares itself a list?

A generator is a way to delay computation of some code until a later point. Consider the idea of searching through files on a file system. You can write a generator that will yield the name of every file in the file system when you iterate over it. Rather than compute the list of files ahead of time, you generate the next file when you’re about to use it.

First, we’ll introduce the concept of yield with an example.

def generator_function():
    x = [1,2,3,4,5]
    for i in x:
        yield i

def print_remaining(generator):
    ## now print the remaining
    for i in generator:
        print i
        
## Instanitate the generator
generator = generator_function()

## Print the first two yields
print generator.next()
print generator.next()

## Finish off the generator
print_remaining(generator)

So what you see is that a generator is essentially a function that behaves like an iterator. You yield a value and the execution stops until you ask it for the next value.

We can build the concept of an infinite list by using a generator. Here is an example.

def infinite_list():
    i = 1
    while True:
        yield i
        i = i+1

long_list = infinite_list()
long_list.next()
1
long_list.next()
2
long_list.next()
3

On Decorators

A decorator is basically a function that wraps another one. Consider it from the perspective of authentication. If a user isn’t authenticated, you don’t want them to be able to call a funciton.

def auth_wrapper(method):
    """This function creates a new function that decides whether or not
    method can be called. It is intended to wrap a class funciton, thus
    the use of `self`. If self._auth isn't present, and exception will
    be thrown instead of calling the wrapped function.
    """
    def wrapped(self): 
        if self._auth:
            return method(self)
        else:
            raise Exception('Auth failed!')
    return wrapped # return the function we just defined above

We can see by the use of self that this decorator expects to be used on a class method. Let’s write a class that uses it!

class SomeThing(object):
    def __init__(self):
        self._auth = False
        
    def make_authed(self):
        self._auth = True
        
    @auth_wrapper
    def some_fun(self):
        print 'Some fun!'

If we instanitate SomeThing, we can try it out.

st = SomeThing()
try:
    st.some_fun() # throws an exception
except Exception,e:
    print 'ERROR: ', e
st.make_authed()
st.some_fun()
See more
This post has 23 notes
Tagged with code, python,
Posted at 2:08 PM 05 April 2011
  1. steurermgwu38277 reblogged this from j2labs
  2. davydany reblogged this from j2labs
  3. whitneymcn reblogged this from j2labs and added:
    half-assed programmer...I am, James’ code posts are...sweet...
  4. j2labs posted this
Bookmark and Share