Here you can use descriptors :
class deco(object): def __init__(self, func): self.func = func self.parent_obj = None def __get__(self, obj, type=None): self.parent_obj = obj return self def __call__(self, *args, **kwargs): return self.func(self.parent_obj, *args, **kwargs) def string(self, *args, **kwargs): return str(self(*args, **kwargs)) class Test(object): def __init__(self, value): self._value = value @deco def plus(self, n): return self._value + n
so that:
>>> test = Test(3) >>> test.plus(1) 4 >>> test.plus.string(1) '4'
This requires an explanation. deco
is a decorator, but it is also a descriptor . A descriptor is an object that defines the alternative behavior that should be invoked when an object is considered as an attribute of its parent. Interestingly, border methods themselves are implemented using the descriptor protocol
This is a sip. Let's see what happens when we run the sample code. First, when we define the plus
method, we use the deco
decorator. Now we usually see decorator functions, and the return value of the function is the result of decorating. Here we use the class as a decorator. As a result, Test.plus
not a function, but rather an instance of the deco
type. This instance contains a link to the plus
function that we want to wrap.
The deco
class has a __call__
method, which allows instances to act as functions. This implementation simply passes the arguments to the plus
function referenced. Note that the first argument will be a reference to the Test
instance.
The tricky part is implementing test.plus.string(1)
. To do this, we need a reference to the Test
instance, the plus
instance is an attribute. To do this, we use the descriptor protocol. That is, we define the __get__
method, which will be called whenever the deco
instance gets access as an attribute of some instance of the parent class. When this happens, it stores the parent object within itself. Then we can simply implement plus.string
as a method of the deco
class and use the reference to the parent object stored in the deco
instance to get the Test
instance to which plus
belongs.
This is a lot of magic, so here's a disclaimer: although it looks cool, it's probably not a good idea to implement something like this.