Reputation: 1055
I need a delegated class to delegate a @classmethod
. Here's what I've tried:
class Foo(object):
def __init__(self, a):
self.a = a
@classmethod
def from_a(cls, a):
return cls(a)
class Bar(object):
def __init__(self, foo):
elf._foo = foo
def __getattribute__(self, name):
return getattr(self._foo, name)
But, of course this doesn't define how to look up attributes of Foo
(not of an instance of Foo
), so Bar.from_a(5)
will raise an AttributeError
. While it is of course possible to do this explicitly by defining a from_a
method on Bar
or to do this at instantiation by calling Bar(Foo.from_a(5))
, I would rather do this implicitly. Ideas?
Upvotes: 0
Views: 321
Reputation: 208635
I started working on what I thought would be a simple approach for this using a metaclass, but it is actually fairly complex. What you should probably be doing here is having Bar
inherit from Foo
, but I'll show you what I came up with all the same:
import types
import functools
def make_delegating_type(delegatee):
class DelegatingType(type):
def __getattr__(self, name):
obj = getattr(delegatee, name)
if isinstance(obj, (types.FunctionType, types.MethodType)):
@functools.wraps(obj)
def wrapper(*args, **kwargs):
result = obj(*args, **kwargs)
if isinstance(result, delegatee):
return self(result)
return result
return wrapper
return obj
return DelegatingType
class Foo(object):
def __init__(self, a): self.a = a
@classmethod
def from_a(cls, a): return cls(a)
class Bar(object):
__metaclass__ = make_delegating_type(Foo)
def __init__(self, foo): self._foo = foo
def __getattr__(self, name): return getattr(self._foo, name)
Note that in 3.x you would use class Bar(object, metaclass=make_delegating_type(Foo)
instead of the __metaclass__ = make_delegating_type(Foo)
line at the top of the Bar
class body.
Here is how this works. Your current version currently delegates attribute lookups on instances of Bar
to an instance of Foo
, this uses a metaclass so that attributes lookups on the class Bar
are delegated to the class Foo
as well. Unfortunately it is not as simple as just using a __getattr__
definition that returns getattr(delegatee, name)
, because if the attribute your a looking up is a factory function as in your example you need a version of that factory function that will return an instance of your delegating type. So for example Bar.from_a(5)
should be the same as Bar(Foo.from_a(5))
, and with the naive approach you would just get Foo.from_a(5)
. That is why there is all the logic detecting if the attribute is a function or method, and creating a wrapper that checks the return type of that function/method.
To reiterate, I do not recommend that you use this code! It is much more complicated then just defining from_a
on Bar
or having Bar
inherit from Foo
. But hopefully it will be a learning experience for you, as it was for me.
Upvotes: 2