0

I've run into a very annoying, peculiar problem. I'm hoping someone can talk me through it. First I have a class decorator which delays a functions return value, in this case I'll just have it do nothing to simplify things.

class MyDecorator(object):
    def __init__(self, func, *args):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

    @classmethod
    def decorate(cls, func, delay=0):
        return cls(func, delay)

Then I have a mixin which has a method with a required argument upon which I'm attaching this decorator.

class MyMixin(object):
    @MyDecorator.decorate
    def my_method(self, arg1, *args, **kwargs):
        pass

Then I use my Mixin

class MyImplementation(MyMixin, object):
    pass

m = MyImplementation()
m.my_method('my first argument')

and then I get the error

TypeError: my_method() missing 1 required positional argument: 'arg1'

For those that're wondering, the argument 'my first argument' actually replaces self, as in self is not passed or even accessible, so another argument arg1 is required.

Now I know why this happens, wrapping the method with a class decorator creates an object, not a function and objects can't be bound to a class. I have a few ideas how to get past it (the most straightforward being a metaclass implementation, but because of the nature of what I'm trying to do being a mixin that won't work very well). I'm just looking for some advice as to how to go about binding the method to the instance before it is given to the decorator (it seems strange this doesn't auto happen but I suppose staticmethod and classmethod requires this doesn't happen) in such a way that I don't have to define a constructor (cause hello, Mixin).

Mohsin Kale
  • 197
  • 2
  • 11
  • You replaced a function object (a *descriptor*) with a class instance which is not a descriptor. This is common mistake. – Martijn Pieters Nov 08 '18 at 12:09
  • @MartijnPieters I know that and I've stated it. I'm looking for ways around it. – Mohsin Kale Nov 08 '18 at 12:10
  • Either make the class a descriptor too, or return a wrapper function object; function objects replacing function objects make it easier to still supply a descriptor. – Martijn Pieters Nov 08 '18 at 12:11
  • @MartijnPieters Meaning I want to keep it as a class decorator, but I also want to bind the method to the instance. – Mohsin Kale Nov 08 '18 at 12:11
  • Also see [How can I decorate an instance method with a decorator class?](//stackoverflow.com/q/30104047) – Martijn Pieters Nov 08 '18 at 12:11
  • So how would you determine `self` if you *don't* bind it to an instance? If you don't need `self` on the method, then remove that argument. – Martijn Pieters Nov 08 '18 at 12:12
  • @MartijnPieters Yeah, I've never used descriptors before (though I've known about them). Didn't know there was this trick to applying them as class decorators. Thank you :) – Mohsin Kale Nov 08 '18 at 12:14
  • Note that decoration happens **when the class is created**, not when you call the method. But *binding*, to an instance, happens each time you access a descriptor attribute (`instance.attributename`). – Martijn Pieters Nov 08 '18 at 12:14

0 Answers0