Fork me on GitHub

Attaching Attributes to Functions

Disclaimer: I can’t tell if I like this behavior or not, but I’m leaning towards it being an awesome way to avoid using classes while still gaining the benefit of singleton-like behavior.

def init_something():
    if not hasattr(init_something, 'val'):
        val = 'WHATEVER'
        init_something.val = val
    return init_something.val

Let’s dig into what this code is doing.

How It Works

First, notice that we are using the function’s name, init_something inside it’s definition. I can then use this name to check if any attributes are attached to it. Being able to reference a function by it’s name is similar to using self in a class, except we won’t be using a class.

Next, we’ll call our cached value val. You probably want a more descriptive name in practice, but this works for now. If init_something hasn’t initialized val, we will give it a value and then attach it to the function. Every check after the first one will already have val ready and waiting to go.

So… does it work? Let’s add a print statement and see.

>>> def init_something():
...     if not hasattr(init_something, 'val'):
...         val = 'WHATEVER'
...         print 'Initializing value'
...         init_something.val = val
...     return init_something.val
... 
>>> init_something()
Initializing value
'WHATEVER'
>>> init_something()
'WHATEVER'
>>> init_something()
'WHATEVER'

Sweet! We only see it initialize the value on the first call

Doing It With Classes

I find it neat because the alternative is to use a class, which is heavier. This same behavior is achieved by using a class and implementing `__new__()`. This is different from `__init__()` because `__new__()` is the function responsible for creating `self`, which gets passed into `__init__()`.

More info: http://docs.python.org/reference/datamodel.html#object.__new__

That means we can implement `__new__()` such that it will only create a new instance in the case that one doesn’t already exist. That looks like below.

>>> class SomeClass(object):
...     def __new__(cls, *a, **kw):
...         if not hasattr(cls, 'val'):
...             print 'Initializing value'
...             cls.val = 'WHATEVER'
...         return cls.val
... 
>>> sc = SomeClass()
Initializing value
>>> sc = SomeClass()
>>> sc = SomeClass()

Notice that using it even seems to behave the same. The only difference is that one is a function and one is class and when I have to choose, I will choose the function.

See more
Tagged with python, code, functions,
Posted at 12:03 PM 10 April 2012
Bookmark and Share