5

I am trying to create a wrapper class that behaves almost like the wrapped object. So far, I have come up with the following code:

import functools
import types

method_wrapper = type((None).__str__)

class Box:

    def __new__(cls, obj):
        attrs = {}
        attr_names = dir(obj)

        for attr_name in attr_names:
            attr = obj.__getattribute__(attr_name)

            if isinstance(attr, (types.MethodType, method_wrapper)):
                "Attr is a bound method, ignore self"
                @functools.wraps(attr)
                def wrapped_attr(self, *args, **kwargs):
                    return attr(*args, **kwargs)
                attrs[attr_name] = wrapped_attr

            elif isinstance(attr, types.FunctionType):
                "attr is a static method"
                attrs[attr_name] = staticmethod(attr)

            else:
                "attr is a property"
                attrs[attr_name] = attr

        cls = type(type(obj).__name__,
                   (cls, type(obj)),
                   attrs)

        return object.__new__(cls)

I tried testing it with:

if __name__ == '__main__':
    x=Box(object())

However it comes up with the following error message:

TypeError: __init__() should return None, not 'NotImplementedType'

__init__ is being properly dispatched by isinstance(attr, (types.MethodType, method_wrapper)), and wrapped_attr seems to be executed. Do you have any idea why this is happening?

3
  • 1
    Out of curiosity, what are you trying to achieve with this code? Commented May 17, 2014 at 23:13
  • I found this solution for my larger goal of writing a proxy class, however, I still would like to know why init is returning NotImplemented. Commented May 17, 2014 at 23:18
  • 2
    @EdgarAroutiounian I am trying to add debug info to a LISP interpreter that I wrote. Using a proxy object, which contains the debug info, would allow me to preserve the structure of my evaluator and parser, while changing the tokenizer and the error handler. Commented May 17, 2014 at 23:23

1 Answer 1

1

The problem is here:

for ...:
    attr = ...
    ...
    def wrapped_attr(...):
        ..attr..

This doesn't work as expected, because attr is rebound to various values by the for loop. All subfunctions will see the last value bound, not the value it had in that iteration of the loop. In this case, the last value bound, in alphabetical order, is __subclasshook__, which tends to return NotImplemented when called with random arguments.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Is the best workaround to generate a closure and pass in attr, As in def closure(attr):def wrapped_attr(...):...? That method just seems a bit messy.
@Joshua: Yes. Blame Python's scoping rules, which are usually nice but have slightly unexpected effects like this one.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.