Utilizando el docstring de un método para sobrescribir automáticamente los que de otro método

StackOverflow https://stackoverflow.com/questions/71817

  •  09-06-2019
  •  | 
  •  

Pregunta

El problema:Tengo una clase que contiene un método de plantilla execute el que llama a otro método _execute.Las subclases se supone que para sobrescribir _execute implementar alguna funcionalidad específica.Esta funcionalidad debe ser documentada en el docstring de _execute.Los usuarios avanzados pueden crear sus propias subclases para ampliar la biblioteca.Sin embargo, otro usuario que trabaje con una subclase sólo debe utilizar execute, para que no vea la correcta docstring si él usa help(execute).

Por lo tanto, sería bueno modificar la base de clase de tal manera que en una subclase de la docstring de execute se sustituye automáticamente con la de _execute.Alguna idea de cómo se podría hacer esto?

Yo estaba pensando en metaclasses para hacer esto, para hacer esta completamente transparente para el usuario.

¿Fue útil?

Solución

Bueno, si no te importa copiar el original método en la subclase, puede utilizar la siguiente técnica.

import new

def copyfunc(func):
    return new.function(func.func_code, func.func_globals, func.func_name,
                        func.func_defaults, func.func_closure)

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        for key in attrs.keys():
            if key[0] == '_':
                skey = key[1:]
                for base in bases:
                    original = getattr(base, skey, None)
                    if original is not None:
                        copy = copyfunc(original)
                        copy.__doc__ = attrs[key].__doc__
                        attrs[skey] = copy
                        break
        return type.__new__(meta, name, bases, attrs)

class Class(object):
    __metaclass__ = Metaclass
    def execute(self):
        '''original doc-string'''
        return self._execute()

class Subclass(Class):
    def _execute(self):
        '''sub-class doc-string'''
        pass

Otros consejos

Hay una razón que usted no puede reemplazar la base de la clase execute función directamente?

class Base(object):
    def execute(self):
        ...

class Derived(Base):
    def execute(self):
        """Docstring for derived class"""
        Base.execute(self)
        ...stuff specific to Derived...

Si usted no quiere hacer lo anterior:

Método de los objetos no se admite la escritura a la __doc__ atributo, por lo que tiene que cambiar __doc__ en el real objeto de la función.Puesto que usted no desea sobrescribir el uno en la clase base, se tendría que dar a cada subclase su propia copia de execute:

class Derived(Base):
    def execute(self):
        return Base.execute(self)

    class _execute(self):
        """Docstring for subclass"""
        ...

    execute.__doc__= _execute.__doc__

pero esto es similar a una rotonda camino de redefinición de execute...

Mira la functools.envolturas() decorador;hace todo esto, pero no sé de improviso si usted puede conseguir que se ejecute en el contexto adecuado

Bien el doc-cadena se almacena en __doc__ por lo que no sería demasiado duro para volver a asignar se basa en la doc-cadena de _execute después del hecho.

Básicamente:

class MyClass(object):
    def execute(self):
        '''original doc-string'''
        self._execute()

class SubClass(MyClass):
    def _execute(self):
        '''sub-class doc-string'''
        pass

    # re-assign doc-string of execute
    def execute(self,*args,**kw):
        return MyClass.execute(*args,**kw)
    execute.__doc__=_execute.__doc__

Ejecutar tiene que ser re-declarado que el doc cadena se une a la versión de ejecutar para el SubClass y no para MyClass (que de otra forma interferir con otras sub-clases).

Eso no es una muy buena manera de hacerlo, pero desde el punto de VISTA del usuario de una biblioteca debe dar el resultado deseado.Entonces, usted puede terminar para arriba en un meta-clase para hacer más fácil para las personas que están sub-classing.

Estoy de acuerdo en que la más simple, la mayoría de Python manera de abordar esto es simplemente redefinir ejecutar en sus clases y tiene que llamar al método execute de la clase base:

class Sub(Base):
    def execute(self):
        """New docstring goes here"""
        return Base.execute(self)

Esto es muy poco código para lograr lo que se desea;el único inconveniente es que se debe repetir este código en cada subclase que se extiende de la Base.Sin embargo, este es un pequeño precio a pagar por el comportamiento que usted quiere.

Si quieres un descuidado y detallado manera de asegurarse de que el docstring para ejecutar se genera de forma dinámica, puede utilizar el descriptor de protocolo, que sería significativamente menos código que con las demás propuestas aquí.Esto es molesto porque no se puede simplemente establecer un descriptor de una función existente, lo que significa que la ejecución debe ser escrito como una clase separada con un __call__ método.

Aquí está el código para hacer esto, pero hay que tener en cuenta que el ejemplo anterior es mucho más simple y más Python:

class Executor(object):
    __doc__ = property(lambda self: self.inst._execute.__doc__)

    def __call__(self):
        return self.inst._execute()

class Base(object):
    execute = Executor()

class Sub(Base):
    def __init__(self):
        self.execute.inst = self

    def _execute(self):
        """Actually does something!"""
        return "Hello World!"

spam = Sub()
print spam.execute.__doc__  # prints "Actually does something!"
help(spam)                  # the execute method says "Actually does something!"
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top