Search code examples
pythonpython-3.xoopmonkeypatching

Safely bind method from one class to another class in Python


I know I can attach a function to a class and make it a method:

 >>> def is_not_bound(inst, name):
...     print("Hello %s" % name)
... 
>>> 
>>> class NoMethods:
...     pass
... 
>>> 
>>> setattr(NoMethods, 'bound', is_not_bound)
>>> 
>>> NoMethods().bound("oz") # prints: Hello oz
Hello oz

To my surprise this works also with a bound method from one class to another:

>>> class Foo:
...     def foo(self, name):
...         print("Hello %s" % name)
... 
>>> class B:
...     pass
... 
>>> setattr(B, 'bound_to_b', getattr(Foo, 'foo'))
>>> B().bound_to_b("confused?")
Hello confused?
>>> 

Can I safely use this? Is there something I am overseeing?

update

One caveat that I found already:

>>> B.bound_to_b
<function Foo.foo at 0x7fc997e8b730>

Even though I called the method from B, It seems bound to Foo.

And even more surprising!:

>>> def new_method(self, addto):
...     return self.num + addto
... 
>>> setattr(B, 'add', new_method)
>>> b=B()
>>> b.num = 2
>>> b.add(2)
4

Solution

  • Apparently, this is an intended behavior (which is cool!). But also apparently, this behavior is not very familiar.

    If you knew Python 2 for a long time, you would might not be aware of the fact that Python 3 has no methods (as commented above).

    So in Python 3:

    >>> class Foo:
    ...     def foo(self, name):
    ...         print("Hello %s" % name)
    ... 
    >>> Foo.foo
    <function Foo.foo at 0x7f729a406730>
    >>> def foo():
    ...     pass
    ... 
    >>> foo
    <function foo at 0x7f729b83ff28>
    >>> 
    >>> Foo.foo
    <function Foo.foo at 0x7f729a406730>
    

    There is no distinction! In Python 2 however:

    Python 2.7.14 (default, Feb  2 2018, 02:17:12) 
    [GCC 7.3.0] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> class Foo:
    ...     def foo(self, name):
    ...         print("Hello %s" % name)
    ... 
    >>> Foo.foo
    <unbound method Foo.foo>
    >>>