Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Get unbound class method

How can you get a not bound class method?

class Foo:
    @classmethod
    def bar(cls): pass

>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>

Edit: This is python 3. Sorry for the confusion.

like image 578
darkfeline Avatar asked Jan 29 '13 02:01

darkfeline


People also ask

Is unbounded a class method in Python?

Unbound methods are methods that are not bound to any particular class instance yet. Bound methods are the ones which are bound to a specific instance of a class.

What is __ get __ in Python?

__get__(self, obj, type=None) : This attribute is called when you want to retrieve the information (value = obj. attr) , and whatever it returns is what will be given to the code that requested the attribute's value. gfg.

What is unbound method call in Python?

An unbound method is essentially a function with some trimmings. A 'bound method' is called that because the first argument (ie self ) is already set to a ; you can call b(10) and it works just the same way as if you had done a. fred(10) (this is actually necessary given how CPython operates).

Is bounded a class method?

If a function is an attribute of class and it is accessed via the instances, they are called bound methods. A bound method is one that has ' self ' as its first argument. Since these are dependent on the instance of classes, these are also known as instance methods.


1 Answers

Python 3 does not have unbound methods. Forget about classmethods for a moment, and look at this:

>>> class Foo:
...     def baz(self): pass
>>> Foo.baz
<function __main__.baz>

In 2.x, this would be <unbound method Foo.baz>, but 3.x does not have unbound methods.

If you want to get the function out of a bound method, that's easy:

>>> foo = Foo()
>>> foo.baz
<bound method Foo.baz of <__main__.Foo object at 0x104da6850>>
>>> foo.baz.__func__
<function __main__.baz>

In the same way:

>>> class Foo:
...     @classmethod
...     def bar(cls): pass
>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.bar.__func__
<function __main__.bar>

Things are much more interesting in 2.x, because there actually are unbound methods to get. You can't normally see an unbound classmethod, because the whole point is that they get bound to the class at class creation time, instead of being left unbound and then bound to each instance at instance creation time.

But really, an unbound method is just any instancemethod whose im_self is None. So, just as you can do this:

class Foo(object):
    def baz(self): pass

foo = Foo()
bound_baz = foo.baz
unbound_baz = new.instancemethod(bound_baz.im_func, None, bound_baz.im_class)

Note that bound_baz.im_func is the 2.x version of bound_baz.__func__ in 3.x—but that new.instancemethod does not have a 3.x equivalent.

The documentation says that new is deprecated in favor of types, for 3.x compatibility, and in fact, you can do this in 2.x:

unbound_baz = types.MethodType(bound_baz.im_func, None, bound_baz.im_class)

But that doesn't work in 3.x, because MethodType does not take a class parameter, and does not allow its instance parameter to be None. And personally, when I'm doing something that is explicitly 2.x-only and cannot be ported to 3.x, I think using new is clearer.

Anyway, given a class in 2.x, you can do this:

class Foo(object):
    @classmethod
    def bar(cls): pass

bound_bar = Foo.bar
unbound_bar = new.instancemethod(bound_bar.im_func, None, bound_bar.im_class)

If you print it out, you'll see:

<unbound method type.bar>

Or, using your example, with an old-style class:

class Foo:
    @classmethod
    def bar(cls): pass

<unbound method classobj.bar>

And yes, maybe it's a bit of a cheat that the im_class of a classmethod for an old-style class is classobj even though that's not Foo.__class__, but it seems like the most reasonable way to get old-style and new-style classes working similarly in all of the usual use cases.

like image 148
abarnert Avatar answered Oct 23 '22 18:10

abarnert