2011-01-30 11 views
5

Estoy tratando de implementar un sistema de "subcomando" como una clase heredable en Python. Mi caso de uso esperado es algo así como:Definir un decorador en una clase, que también se puede usar dentro de la definición de clase

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" 

he implementado Command.subcommand como un método estático y todo funcionaba bien hasta que he intentado añadir un subcomando a la clase padre, que me consiguió TypeError: 'staticmethod' object is not callable. En retrospectiva, es obvio que no va a funcionar this:

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" 

La única alternativa que he encontrado hasta ahora es declarar el decorador subcommand fuera de la clase padre, y luego inyectarla después de la definición de clase se ha completado.

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 

Sin embargo, como alguien que nunca utiliza Python antes de añadir decoradores, esto se siente muy torpe para mí. ¿Hay una forma más elegante de lograr esto?

+0

Bueno, la primera pregunta es por qué implementa un decorador como método en una clase, a menos que esa clase se use para un grupo de decoradores. Y si para eso sirve, ¿por qué decoras el decorador "común"? Parece torpe, probablemente porque el diseño de tu clase es un poco extraño ... :) –

+0

¿Qué esperas exactamente que logre este decorador por ti? Python ya sabe cuáles son los subcomandos, porque sabe qué clases son subclases de otros. –

+0

@Lennart - puramente para la estética de importación. No era fanático de 'Importar comando ... clase MyCommand (comando.Comando)' (se siente redundante) o 'desde comando importar * ... @ subcomando' (parece que está contaminando el espacio de nombres). –

Respuesta

5

Hay dos soluciones a este problema que se me ocurren. El más simple es hacer que sea un método estático después haya terminado de usarlo en la clase padre:

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. 

Esto es algo frágil en el que no se puede utilizar en la clase padre que después de realizar un método estático. La forma más robusta para hacer esto es añadir una clase intermedia:

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" 

Ahora puede heredar todas sus clases de CommandBase en lugar de Command.

+0

Esto es algo sorprendente. Pensé que al ejecutar decoradores de métodos de clase, la clase en sí misma aún no existía (la metaclase se llama al final), pero aún se encuentra el subcomando. De todos modos, ambos ejemplos tienen errores tipográficos: comillas faltantes en primer lugar y probablemente querías decir '@ Command.subcommand' en el segundo. ¿Estoy en lo correcto? – 6502

+0

@ 6502 He corregido los errores tipográficos. Si observas la definición de 'subcomando' en el primer ejemplo, verás que está definida como una función normal. Todas las definiciones de métodos tienen lugar en el ámbito en el que se encuentra 'subcomando', por lo que es de esperar que el subcomando sea visible en ese ámbito. – aaronasterling

+0

Sí, tiene sentido ... el subcomando es solo una variable local hasta el final del cuerpo de la clase, en ese punto el diccionario se construye y se envía a la metaclase. Todavía me siento extraño escribiendo, p.un ciclo while en el cuerpo de una declaración de clase :-D ... – 6502

Cuestiones relacionadas