Decorating one method of a class C with another method of class C?

Discussion in 'Python' started by Dan Stromberg, Jun 7, 2014.

  1. Is there a way of decorating method1 of class C using method2 of class C?

    It seems like there's a chicken-and-the-egg problem; the class doesn't
    seem to know what "self" is until later in execution so there's
    apparently no way to specify @self.method2 when def'ing method1.
     
    Dan Stromberg, Jun 7, 2014
    #1
    1. Advertisements

  2. Yes. See below.
    Directly inside the class statement, you don't need self. The class
    defines a namespace, like inside a function or globals, so you can just
    refer to names directly, like this:


    class Spam:
    def decorate(func): # Look Ma, no self!
    @functools.wraps(func)
    def inner(self, *args):
    print(self, args)
    return func(*args)
    return inner
    @decorate
    def method(self, a, b):
    return a+b


    That works fine for what you want to do. The disadvantage, however, is
    that if you call the decorate method from *outside* of the class scope
    (that is, from the outside like Spam.decorate, or inside a method like
    self.decorate) you'll run into some weird problems. Try it and see if you
    can diagnose the cause of the problem.


    This problem has a trivial, simple, obvious and wrong solution: ensure
    that the decorate method is a static method. That requires Spam to be a
    new-style class (inherit from object, or a built-in), but is otherwise
    trivial:

    class Spam(object):
    @staticmethod
    def decorate(func): # Look Ma, no self!
    ...


    Unfortunately, this does not work! staticmethod objects are not callable.
    It is only after you go through the descriptor protocol that you end up
    with something which is callable. Sad but true. That means that this will
    fail:

    class Spam(object):
    @staticmethod
    def decorate(func): ...

    @decorate # calling directly fails
    def method(self, args): ...


    but this will be okay:

    @Spam.decorate # calling via Spam or instance works
    def function(args): ...


    We're *so close* it hurts :-(

    Good news! staticmethod is a class, and like most classes we can subclass
    it and get the behaviour we want!

    # Untested
    class callablestaticmethod(staticmethod):
    def __call__(self, *args):
    return self.__func__(*args)

    ought to do it. Then just use callablestaticmethod instead of
    staticmethod, and all will be ginger-peachy.


    But what happens if you want the decorator to refer to the class itself?
    You can always hard-code references to Spam inside the decorate method,
    but a better solution may be to use a callableclassmethod instead. (Left
    as an exercise.)

    Inside the decorated method, or the wrapper method "inner", we can refer
    to "self" as normal, and such references won't be evaluated until call-
    time when self exists. It's only inside the decorator itself, and the
    body of the class, that self doesn't yet exist.
     
    Steven D'Aprano, Jun 7, 2014
    #2
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.