3

Why does Python turn a free function into an unbound method upon assignment to a class variable?

def make_func(s):
    def func(x):
        return '%s-%d' % (s, x)
    return func

class Foo(object):
    foofunc = make_func('foo')

So this works as expected: (returns "dog-1")

make_func('dog')(1)

But this fails with:

Foo.foofunc(1)

TypeError: unbound method func() must be called with Foo instance as first argument (got int instance instead)

Upon closer inspection, Python turned the "inner" function func inside make_func into a method, but since there's no self, this method will never work. Why does Python do this?

>>> import inspect
>>> inspect.getmembers(Foo, inspect.ismethod)
[('foofunc', <unbound method Foo.func>)]

2 Answers 2

3

Python can't tell "how" you assigned a method to a class attribute. There is no difference between this:

class Foo(object):
    def meth():
        pass

and this

def func():
    pass

class Foo(object):
    meth = func

In both cases, the result is that a function object is assigned to a class attribute named 'meth'. Python can't tell whether you assigned it by defining the function inside the class, or by "manually" assigning it using meth = func. It can only see the "end result", which is an attribute whose value is a function. Either way, once the function is in the class, it is converted to a method via the normal process that notices functions in class definitions and makes them into methods.

Sign up to request clarification or add additional context in comments.

2 Comments

proof is in the pudding: foofunc = staticmethod(make_func('foo')) gives you the behavior you expected
Hmm, it probably create the class by setting an dictionary key of 'meth' to the definition of 'func', which it then transformed into a method. Quite unexpected, I must say.
-2
class Foo(object):
    foofunc = make_func('foo')

foofunc is a class variable, not a method (for which you need the 'def'). And you initialize it with the result of make_func(...), so it won't change again.

If you want to call Foo.foofunc, you need to assign foofunc = make_func without a parameter.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.