You can actually solve this by creating a new class for every type. If you want this to work transparently, __new__ is the place for it.
import weakref
class BigWrap(object):
def __new__(cls, wrapped):
wrapped_type = type(wrapped)
print('Wrapping %s (%s)' % (wrapped, wrapped_type))
# creates a new class, aka a new type
wrapper_class = type( # new_class = type(class name, base classes, class dict)
'%s_%s_%d' % (cls.__name__, wrapped_type.__name__, id(wrapped)), # dynamic class name
(
cls, # inherit from wrap to have all new methods
wrapped_type, # inherit from wrap_type to have all its old methods
),
{
'__getitem__': wrapped.__getitem__, # overwrite __getitem__ based on wrapped *instance*
'__new__': wrapped_type.__new__, # need to use wrapped_type.__new__ as cls.__new__ is this function
})
cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use
return cls._wrappers[wrapped_type](wrapped)
# self is already an instance of wrap_<type(wrapped)>
def __init__(self, wrapped):
self.__wrapped__ = wrapped
Initial "solution":
import weakref
class wrap(object):
_wrappers = weakref.WeakValueDictionary() # cache wrapper classes so we don't recreate them
def __new__(cls, wrapped):
wrapped_type = type(wrapped)
print('Wrapping %s (%s)' % (wrapped, wrapped_type))
try:
return object.__new__(cls._wrappers[wrapped_type]) # need to use object.__new__ as cls.__new__ is this function
except KeyError:
print('Creating Wrapper %s (%s)' % (wrapped, wrapped_type))
# creates a new class, aka a new type
wrapper_class = type( # class name, base classes, class dict
'%s_%s' % (cls.__name__, wrapped_type.__name__), # dynamic class name
(cls,), # inherit from wrap to have all its method
{'__getitem__': wrapped_type.__getitem__}) # overwrite __getitem__ based on wrapped class
cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use
return cls._wrappers[wrapped_type](wrapped)
# self is already an instance of wrap_<type(wrapped)>
def __init__(self, wrapped):
self._data = wrapped
Be careful however! This will do what you want - use the wrapped class' __getitem__. However, this doesn't always make sense! For example, list.__getitem__ is actually built into CPython's CAPI and not applicable to other types.
foo = wrap([1,2,3])
print(type(foo)) # __main__.wrap_list
foo[2]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-82791be7104b> in <module>()
----> 1 foo[2]
TypeError: descriptor '__getitem__' for 'list' objects doesn't apply to 'wrap_list' object