Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python method stealer

Tags:

python

oop

How can I "steal" or copy a method from one class onto another class?

Example code:

class A(object):
  def foo(self):
    print "blah"


class B(object):
  foo = A.foo

B().foo()

Expected output:

"blah"

Instead:

TypeError: unbound method foo() must be called with A instance as first argument (got nothing instead)

like image 426
ʞɔıu Avatar asked Nov 24 '12 02:11

ʞɔıu


3 Answers

Use __func__:

>>> A.foo
<unbound method A.foo>
>>> A.foo.__func__
<function foo at 0x00BC5F70>
>>> class B(object):
...   foo = A.foo.__func__
...
>>> B().foo()
"blah"

Quoting the docs:

An instance method object combines a class, a class instance and any callable object (normally a user-defined function).

Special read-only attributes: __self__ is the class instance object, __func__ is the function object; __doc__ is the method’s documentation (same as __func__.__doc__); __name__ is the method name (same as __func__.__name__); __module__ is the name of the module the method was defined in, or None if unavailable.

like image 158
mgibsonbr Avatar answered Sep 22 '22 01:09

mgibsonbr


You can use class inheritance here. Inheritance lets you create an object based on another object, inheriting all of its functions and attributes.

In this case, it looks like:

class A(object):
  def foo(self):
    print "blah"


class B(A):
  # You can add new methods or attributes here,
  #  or even overwrite those inherited from A if you
  #  really want to, though you have to be careful with that.
  pass

After that declaration,

>>> B().foo()
"blah"

This works because:

  • You created class A, and created for it a method foo.
  • You created class B inheriting from A, meaning that when A "gave birth to it," B was born with everything that A has.
    • In our case, B is an exact copy of A, since we didn't do anything else to it. However, we could make changes or add more methods.

An example:

class A(object):
    def foo(self):
        print "blah"

class B(A):
   def newfoo(self):
       print "class A can't do this!"

Which in use, we'd see:

>>> A().foo()
blah
>>> B().foo()
blah
>>> A().newfoo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'newfoo'
>>> B().newfoo()
class A can't do this!

In particular, the reason that your code above didn't work is that when you tried to set B.foo, you wrote

class B(object):
    foo = A.foo

instead of

class B(object):
    foo = A().foo

When you wrote A.foo without the (), you were asking for the method straight from the A type, which wouldn't work in Python. If you were to do foo = A().foo, what you'd be doing is instantiating an A object, and then getting a copy of its method foo, and then assigning it.

like image 29
jdotjdot Avatar answered Sep 24 '22 01:09

jdotjdot


The problem here is that it is a bound method you are trying to steal, however, your example does not involve a function that uses instance state (self). Hence you have two immediate options:

  1. Make the definition of A.foo a static method (@staticmethod decorator)
  2. Decorate or wrap the function to pass an unused argument. E.g. using functools.

Eg.

import functools
stolen = functools.partial(A.foo, None)

This works because your method does not use instance state, and does not require creating a subclass.

To embellish a bit further, a bound instance method (like A.foo) expects a bound instance argument (self, where self is an instance of A). In normal usage, this first argument is passed automatically:

a = A()

Now:

a.foo()
A.foo(a)

...Are both equivalent. In the first case, the syntax instance.bound_method() infers InstanceClass.bound_method(instance) from a lexical viewpoint (instance resolves to self). This is why calling A.foo() will cause an error, since it expects an instance of A.

What the solution above does is to warp the function into one that passes a None as the instance, since the instance is never used anyway (there is no state-based logic). In the case of using staticmethod, it removes the 1st implied expected bound instance argument self.

like image 32
Preet Kukreti Avatar answered Sep 23 '22 01:09

Preet Kukreti