Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top