python - perfectly mimic inheritance with composition -


i attempting wrap class third-party package in such way new class looks subclass of third-party class. third-party class not support inheritance, , has nontrivial features, such functions have __getitem__ method. can wrap every attribute , method using solution based on wrapping class methods return instances of class , how can intercept calls python's "magic" methods in new style classes?. however, still need override __init__ method of third-party class. how can that? note: using new-style classes.

code far:

import copy  class wrappermetaclass(type):     """     works `wrapper` class create proxies wrapped object's magic methods.     """     def __init__(cls, name, bases, dct):          def make_proxy(name):             def proxy(self, *args):                 return getattr(self._obj, name)             return proxy          type.__init__(cls, name, bases, dct)         if cls.__wraps__:             ignore = set("__%s__" % n n in cls.__ignore__.split())             name in dir(cls.__wraps__):                 if name.startswith("__"):                     if name not in ignore , name not in dct:                         setattr(cls, name, property(make_proxy(name)))  class wrapper(object):     """     used provide (nearly) seamless inheritance-like interface classes not support direct inheritance.     """      __metaclass__ = wrappermetaclass     __wraps__  = none     # note __init__ method ignored wrappermetaclass     __ignore__ = "class mro new init setattr getattr getattribute dict"      def __init__(self, obj):         if self.__wraps__ none:             raise typeerror("base class wrapper may not instantiated")         elif isinstance(obj, self.__wraps__):             self._obj = obj         else:             raise valueerror("wrapped object must of %s" % self.__wraps__)      def __getattr__(self, name):         if name '_obj':             zot = 1         orig_attr = self._obj.__getattribute__(name)         if callable(orig_attr) , not hasattr(orig_attr, '__getitem__'):             def hooked(*args, **kwargs):                 result = orig_attr(*args, **kwargs)                 if result self._obj:                     return self                 elif isinstance(result, self.__wraps__):                     return self.__class__(result)                 else:                     return result             return hooked         else:             return orig_attr      def __setattr__(self, attr, val):         object.__setattr__(self, attr, val)         if getattr(self._obj, attr, self._obj) not self._obj: # update _obj's member if exists             setattr(self._obj, attr, getattr(self, attr))  class classtowrap(object):     def __init__(self, data):         self.data = data      def theirfun(self):         new_obj = copy.deepcopy(self)         new_obj.data += 1         return new_obj      def __str__(self):         return str(self.data)  class wrapped(wrapper):     __wraps__ = classtowrap      def myfun(self):         new_obj = copy.deepcopy(self)         new_obj.data += 1         return new_obj  # can't instantiate wrapped directly! problem! obj = classtowrap(0) wr0 = wrapped(obj) print wr0 >> 0 print wr0.theirfun() >> 1 

this works, seamless inheritance-like behavior, need instantiate wrapped directly, e.g.

wr0 = wrapped(0) 

which throws

valueerror: wrapped object must of <class '__main__.classtowrap'> 

i attempted override defining new proxy __init__ in wrappermetaclass, rapidly ran infinite recursions.

my codebase complex users @ different skill levels, can't afford use monkey-patching or solutions modify definition of example classes classtowrap or wrapped. hoping extension code above overrides wrapped.__init__.

please note question not duplicate of e.g. can mimic inheritance behavior delegation composition in python?. post not have answer detailed i'm providing here.

it sounds want wrapper.__init__ method work differently does. rather taking existing instance of __wraps__ class, should take arguments other class expects in constructor , built instance you. try this:

def __init__(self, *args, **kwargs):     if self.__wraps__ none:         raise typeerror("base class wrapper may not instantiated")     else:         self._obj = self.__wraps__(*args, **kwargs) 

if want wrapper remain same reason, put logic in new wrapped.__init__ method instead:

def __init__(self, data): # i'm explicitly naming argument here, use *args     super(self, wrapped).__init__(self.__wraps__(data)) # , **kwargs make extensible 

Comments

Popular posts from this blog

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

python Tkinter Capturing keyboard events save as one single string -

sql server - Why does Linq-to-SQL add unnecessary COUNT()? -