开发者

Defining a decorator in a class, which is also usuable within the class definition

I'm trying to implement a "subcommand" system as an inheritable class in Python. My expected use case is something like:

from command import Command
import sys

class MyCommand(Command):
    @Command.subcommand
    def foo(self):
        print "this can be run as a subcommand"

    def bar(self):
        print "but this is a plain method and isn't exposed to the CLI"

MyCommand()(*sys.argv)

# at the command line, the user runs "mycommand.py foo"

I implemented Command.subcommand as a static method and everything worked fine until I tried to add a subcommand to the parent class, which got me TypeError: 'staticmethod' object is not callable. In hindsight, it's obvious that this won't work:

class Command(object):
    @staticmethod
    def subcommand(method):
        method.is_subcommand = True

        return method

    @subcommand
    def common(self):
        print "this subcommand is available to all child classes"

The only alternative I've found so开发者_JAVA技巧 far is to declare the subcommand decorator outside the parent class, then inject it after the class definition is complete.

def subcommand(method):
    method.is_subcommand = True

    return method

class Command(object):
    @subcommand
    def common(self):
        print "this subcommand is available to all child classes"

Command.subcommand = staticmethod(subcommand)
del subcommand

However, as someone who never used Python before decorators were added, this feels very clunky to me. Is there a more elegant way to accomplish this?


There are two solutions to this problem that I can think of. The simplest is to make it a static method after you're done using it in the parent class:

class Command(object):
    def subcommand(method): # Regular function in class definition scope.
        method.is_subcommand = True

        return method

    @subcommand
    def common(self):
        print "this subcommand is available to all child classes"

    subcommand = staticmethod(subcommand)
    # Now a static method. Can no longer be called during class definition phase.

This is somewhat fragile in that you can't use it in the parent class after you make it a static method. The more robust way to do this is to add an intermediate class:

class Command(object):
    @staticmethod
    def subcommand(method):
        method.is_subcommand = True

        return method

class CommandBase(Command):

    @Command.subcommand
    def common(self):
        print "this subcommand is available to all child classes"

You can now inherit all of your classes from CommandBase instead of Command.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜