spiridon_the_sun_rotator
spiridon_the_sun_rotator

Reputation: 1044

Creation of a class wrapper in Python

I would like to do the following: given an instance of a Base class create an object of a Wrapper class that has all the methods and attributes of the Base class + some additional functionality.

class Base:

    def __init__(self, *args, **kwargs):
         self.base_param_1 = ...
         # some stuff

    def base_method_1(self, *args, **kwargs):
         # some stuff

class Wrapper(...):

    def  __init__(self, cls_instance, *args, **kwargs):
        self.wrapper_param_1 = ...
        # some stuff

    def wrapper_method_1(self, *args, **kwargs):
        # some stuff

The use case is like the following:

wrapper_obj = Wrapper(Base(*base_args, **base_kwargs), *wrapper_args, *wrapper_kwargs)

The expected behavior is that one can access base_param_1, wrapper_param_1 and base_param_1, base_param_2.

It is important that the fields of the Base class are the same in Wrapper (no copying).

I've seen that new functionality can be added in this way Adding a Method to an Existing Object Instance, but this approach has caveats and is not recommended.

Inheritance seems not to be an option here, since I am an already constructed object and this Wrapper can take different classes, despite with common Base.

EDIT

It is also required that Wrapper object is identified as Base instance.

Upvotes: 4

Views: 8385

Answers (2)

spiridon_the_sun_rotator
spiridon_the_sun_rotator

Reputation: 1044

With the help of @schwobaseggl I came up to this solution. Probably, somehow mad, but with the desired functionality:

  class OuterWrapper:

    def __new__(self, obj, *args, **kwargs):

      class InnerWrapper:

        def __init__(self, obj, *args, **kwargs):
          self.obj = obj

        def __getattr__(self, name):
          return getattr(self.obj, name)

      wrapper_obj = InnerWrapper(obj, *args, **kwargs)
      InnerWrapper = type('InnerWrapper', (obj.__class__, InnerWrapper, object), {})
      wrapper_obj.__class__ = InnerWrapper
      return wrapper_obj

Upvotes: 0

user2390182
user2390182

Reputation: 73460

You can override __getattr__. That way, Wrapper specific attributes are looked up first, then the wrapped object's attributes are tried.

class Wrapper:
    def  __init__(self, base_obj, *args, **kwargs):
        self.base_obj = base_obj
        # some stuff

    def wrapper_method(self):
        return "new stuff"

    def __getattr__(self, name):
        return getattr(self.base_obj, name)
        
w = Wrapper("abc")
w.wrapper_method()
# 'new stuff'
w.upper()
# 'ABC'

Upvotes: 7

Related Questions