Python method gotcha

Little quirk of Python that drove me crazy.

I love Python decorators. I wanted to make a decorator for lazy evaluation.

class Lazy:

        def __init__(self, f):
                self.f   = f
                self.val = None

        def __call__(self, *a, **b):
                if self.val == None:
                        self.val = self.f(*a, **b)

                return self.val

class A:

        def __init__(self):
                self.a = 5

        def foo(self):
                return self.a

        def bar(self):
                return self.a

a = A()

Doesn’t work!

Traceback (most recent call last):
  File "", line 31, in
  File "", line 11, in __call__
    self.val = self.f(*a, **b)
TypeError: foo() takes exactly 1 argument (0 given)

What’s going on? It looks like is not receiving ‘self’. That means it’s not a method — it’s a… callable object.

>>> print(
<demo3.Lazy instance at 0x7fc8aed5ddd0>
>>> print(
<bound method of <demo3.A instance at 0x7fc8aed5de18>>

This begs the question — how does Python decide whether something should be

  • A method bound to an object.
  • Just a function defined within a clas.

Well, it turns out, Python looks at the type of objects defined within a class, and if they happen to be a function (not just a callable object), it will make it into a method. A little wrapper fixes the problem.

# -*- coding: utf-8 -*-
def wrap(method):
        def foo(*a, **b):
                return method(*a, **b)

        return foo

def lazy(f):
        return wrap(Lazy(f))

Excellent. Now it’s lazy.

This entry was posted in python. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s