Skip to content
Advertisement

Systematically rewiring functions

I have a number of classes where most methods merely ‘rewire’ method calls to self.value, and return a new instance:

class someClass():
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        return self.__class__( self.value.__add__(other) )

    def someMethod(self, *args, **kwargs):
        return self.__class__( self.value.someMethod(*args, **kwargs) )

    def someOtherMethod(self, *args, **kwargs):
        return self.__class__( self.value.someOtherMethod(*args, **kwargs) )

Of course, not all methods inside are like this, but most are.

Instead of having to implement explicitly someMethod, someOtherMethod and __add__, is there a way to do this systematically? Perhaps with __getattr__ or __getattribute__?


Here is why subclassing the value’s type is not viable :

>>> class someClass(int):
    pass
>>> a = someClass(5)
>>> isinstance(a+5, someClass)
False #I need this to be True

In order to make that last line return True, I would have to ‘rewire’ all operators like before : subclassing doesn’t help at all

Advertisement

Answer

A partial answer, because you still have to list the methods that you want to wrap, but the following makes it a lot easier to wrap a large number of methods, because you just add them to a list of methods to be wrapped:

class MyClass():

    def __init__(self, value):
        self.value = value
    
    def __str__(self):
        return f'<MyClass({repr(self.value)})>'

    
def make_wrappers(cls, methods):
    for method in methods:
        def wrapper(self, *args, _method=method, **kwargs):
            return self.__class__(getattr(self.value, _method)(*args, **kwargs))
        setattr(cls, method, wrapper)    


make_wrappers(MyClass, ['__add__', '__sub__', 'upper'])

x = MyClass(3)
print(x+2, x-2)  # <MyClass(5)> <MyClass(1)>

y = MyClass('hello')
print(y.upper())  # <MyClass('HELLO')>

It does not seem that you can use __getattr__ to do entirely what you want. This is because if you are trying to evaluate a+b, where a is an instance of your class, then if you have not defined an __add__ method but have defined __getattr__, then this will raise a TypeError and ignore (not call) __getattr__. Therefore you cannot use __getattr__ to wrap “magic methods” such as __add__ (at least in Python 3).

Note here that the _method is used to ensure that value of the variable method is bound into the wrapper at the time when it is defined (it is added to the defaults dictionary). If instead you were to use method directly inside wrapper where _method is used, then you would find that every wrapper would use the value of method inherited from an outer scope when it is called, which would not be what you want.

Note also that the solution relies on “monkey-patching” the class. This will work. What will not work in Python 3 is to monkey-patch instances of the class — for example if you tried to use setattr to add a method directly to x that does not exist in MyClass, then calling it would not work.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement