Reputation: 4759
I recently came across this recipe for making a "weakmethod" and thought it was the bees' knees; but there seems to be a mystery argument being passed to the resulting MethodType
function that i can't seem to find:
from weakref import proxy
from types import MethodType
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
return MethodType(super(Foo, self).__getattribute__(name), proxy(self), self.__class__)
else:
return super(Foo, self).__getattribute__(name)
class Bar(Foo):
def my_func(self, a, b):
print a, b
def foo_my_func(self, a, b):
print 'FF Victory Theme'
>>> bar = Bar()
>>> bar.my_func(1, 2)
1 2
>>> weakmethod = bar.foo_my_func
>>> weakmethod(2, 3) # Or `bar.foo_my_func(2, 3)`
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
weakmethod(2, 3)
TypeError: foo_my_func() takes exactly 3 arguments (4 given)
What is this 4th argument that's being passed?
Upvotes: 0
Views: 114
Reputation: 1124170
You used super(Foo, self).__getattribute__(name)
to access the foo_my_func
method. This already returns a MethodType
object. You then wrap this object again.
So your returned object passes in proxy(self)
to the wrapped method, which passes in another self
argument. You started with a, b
, and end up with self, proxy(self), a, b
.
The recipe you linked to uses a decorator instead; this decorator is executed at class definition time, and wraps the function object. It is itself a descriptor, so it handles all the wrapping directly.
You'll want to either unwrap the result of super(Foo, self).__getattribute__(name)
or don't use __getattribute__
at all.
Unwrapping can be done with accessing the __func__
attribute on a method:
class Foo(object):
def __getattribute__(self, name):
attr = super(Foo, self).__getattribute__(name)
if name.startswith('foo_'):
return MethodType(attr.__func__, proxy(self), self.__class__)
return attr
Not using __getattribute__
is done by just accessing the __dict__
mapping on the class directly:
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
for cls in type(self).__mro__:
if name in cls.__dict__:
return MethodType(cls.__dict__[name], proxy(self), self.__class__)
return super(Foo, self).__getattribute__(name)
where type(self).__mro__
lets you iterate over the class and it's base classes in method resolution order to manually search for the method.
Upvotes: 1