2

For the below code :

import os.path

class Test:

   @classmethod
   def foo(cls):
       cls.exist = os.path.exists
       print type(cls.exist)
       print type(os.path.exists)

def main():
   Test.foo()

I am getting the output as :

<type 'instancemethod'>
<type 'function'>

I am just assigning the function os.path.exist to class variable cls.exist. But when I print both the variable, I get one as instance method and other as function. why ?

0

2 Answers 2

1

You may be assigning it as a class variable, but it's bound to the class:

Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
TypeError: unbound method exists() must be called with Test instance as first argument (got str instance instead)

You need to make exist a staticmethod in order for it to be "independent" of the class:

cls.exist = staticmethod(os.path.exists)

Now, both are of the same type:

<type 'function'>
<type 'function'>

And you can actually call it:

>>> Test.exist('foo')
False
Sign up to request clarification or add additional context in comments.

1 Comment

This is a better (and much shorter!) explanation of the "intuitive but not really true" part of my answer. And, even though it's not really true, you can write code as if it were and it works.
1

First, the intuitive explanation:

When you assign a function to a class, it gets converted into an unbound method.

An unbound method is a magical thing that creates a bound method for each instance of the class.

A bound method is a magical thing that has the special self argument bound into it.

(This is slightly different in 3.x, because there are no unbound methods; functions themselves are magical things that create a bound method for each instance of the class. But you're clearly asking about 2.x.)

So:

>>> class C(object): pass
>>> def foo(self): print(self)
>>> C.foo = foo
>>> c = C()

Now you can call c.foo(), and the self argument will be automatically filled in with c. But you can't call C.foo() without an argument, any more than you can call foo() without an argument.

>>> c.foo()
<__main__.C object at 0x108df7e50>
>>> C.foo()
TypeError: unbound method foo() must be called with C instance as first argument (got nothing instead)
>>> foo()
TypeError: foo() takes exactly 1 argument (0 given)

Of course there's nothing stopping you from calling C.foo() or foo() with an explicit self argument. You normally wouldn't do this, but it works fine:

>>> C.foo(1)
1
>>> foo(1)
1

If you look at the function, unbound method, and bound method, they're pretty easy to tell apart:

>>> foo, C.foo, c.foo
(<function __main__.foo>, <unbound method C.foo>, <bound method C.foo of <__main__.C object at 0x108df7e50>>)
>>> type(foo), type(C.foo), type(c.foo)
(function, instancemethod, instancemethod)

If you just want a workaround, you can use staticmethod. This takes a function and creates a thing that, when assigned to a class, acts like a function instead of an unbound method:

>>> bar = staticmethod(foo)
>>> C.bar = staticmethod(foo)
>>> c = C()
>>> bar, C.bar, c.bar
(<staticmethod at 0x109e990f8>, <function __main__.foo>, <function __main__.foo>)

And now, you can call it on the class or an instance as a regular function, with no magic self method:

>>> C.bar(1)
1
>>> c.bar(1)
1
>>> bar(1)
TypeError: 'staticmethod' object is not callable

But that intuitive explanation isn't really true. It will always lead you to the right answer, but it's not how things really work.

To really understand this, you need to understand how descriptors work.

When you assign a function to a class as an attribute, nothing magical happens. What gets stored in the class dictionary is just the plain old function:

>>> C.__dict__['foo']
<function __main__.foo>

And when you create an instance, it doesn't put anything in the instance dictionary, because attribute lookup automatically falls back to the class dictionary:

>>> c.__dict__['foo']
KeyError: 'foo'

The actual magic happens at lookup time. Whenever you look up any attribute (method or otherwise), the interpreter doesn't just return the object from the dictionary, it calls that object's __get__ method, passing the instance (or None, if called on a class object) and the class.

Normal functions have a __get__ method that produces bound or unbound methods as required:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(None, C) == C.foo
True
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x108df7e50>>
>>> C.__dict__['foo'].__get__(c, C) == c.foo
True

You can do the same thing manually and get the same result:

>>> types.MethodType(foo, None, C) == C.foo
True
>>> types.MethodType(foo, c, C) == c.foo
True

And now you can probably guess what staticmethod looks like: It has a __get__ method that just returns the original function, no matter what you pass it.

There's a bit more complexity for dealing with settable data attributes, etc., but really, this is pretty much the whole deal, and if you understand this much, you pretty much understand all the magic in Python. You can build staticmethod, classmethod, and property, by yourself, construct classes from scratch, etc.

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.