2010-04-22 10 views
7

Me gustaría escribir una función python que tenga una docstring creada dinámicamente. En esencia para una función func() Quiero func.__doc__ para ser un descriptor que llama a una función personalizada __get__ crear la documentación bajo pedido. Entonces help(func) debería devolver la docstring generada dinámicamente.Función dinámica docstring

El contexto aquí es escribir un paquete de python envolviendo una gran cantidad de herramientas de línea de comandos en un paquete de análisis existente. Cada herramienta se convierte en una función de módulo con un nombre similar (creada a través de fábrica de funciones e insertada en el espacio de nombres del módulo), con la documentación de la función y los argumentos de la interfaz generados dinámicamente a través del paquete de análisis.

+0

Dudo que esto sea posible ya que ni siquiera se puede subclasificar el tipo de función, sin mencionar de alguna manera hacer que 'def' produzca objetos de su propio tipo. – doublep

+0

Quiero hacer esto sobre la marcha porque dentro del paquete de análisis se pueden establecer valores de parámetros para diferentes herramientas y estos parámetros se recuerdan. Quiero que la función docstring presente las configuraciones de parámetros actuales para esa herramienta. Por lo tanto, cuando solicite 'help (wrapped_tool)', debe consultar el paquete de análisis sobre la marcha y crear la documentación en ese momento. –

Respuesta

9

No puede hacer lo que está buscando hacer, en la forma en que desea hacerlo.

Desde su descripción parece que se podría hacer algo como esto:

for tool in find_tools(): 
    def __tool(*arg): 
     validate_args(tool, args) 
     return execute_tool(tool, args) 
    __tool.__name__ = tool.name 
    __tool.__doc__ = compile_docstring(tool) 
    setattr(module, tool.name, __tool) 

es decir, crear la cadena de documentación de forma dinámica por adelantado cuando se crea la función. ¿Es la razón por la cual la docstring tiene que ser dinámica desde una llamada a __doc__ a la siguiente?

Suponiendo que existe, tendrás que ajustar tu función en una clase, usando __call__ para desencadenar la acción.

Pero incluso entonces tienes un problema. Cuando la ayuda() es llamado para encontrar la cadena de documentación, se llama a la clase, no es el caso, por lo que este tipo de cosas:

class ToolWrapper(object): 
    def __init__(self, tool): 
     self.tool = tool 
     self.__name__ = tool.name 
    def _get_doc(self): 
     return compile_docstring(self.tool) 
    __doc__ = property(_get_doc) 
    def __call__(self, *args): 
     validate_args(args) 
     return execute_tool(tool, args) 

no funcionará, ya que las propiedades son ejemplo, los atributos no clase. Puede hacer que el trabajo de la propiedad doc teniéndolo en una metaclase, en lugar de la propia clase

for tool in find_tools(): 
    # Build a custom meta-class to provide __doc__. 
    class _ToolMetaclass(type): 
     def _get_doc(self): 
      return create_docstring(tool) 
     __doc__ = property(_get_doc) 

    # Build a callable class to wrap the tool. 
    class _ToolWrapper(object): 
     __metaclass__ = _ToolMetaclass 
     def _get_doc(self): 
      return create_docstring(tool) 
     __doc__ = property(_get_doc) 
     def __call__(self, *args): 
      validate_args(tool, args) 
      execute_tool(tool, args) 

    # Add the tool to the module. 
    setattr(module, tool.name, _ToolWrapper()) 

Ahora usted puede hacer

help(my_tool_name) 

y obtener la cadena de documentación personalizada o

my_tool_name.__doc__ 

por lo mismo. La propiedad __doc__ se encuentra en la clase _ToolWrapper que se necesita para atrapar el último caso.

+3

No puedo comentar sobre la publicación de otra persona (novato, y todo), pero quiero decir que la sobrecarga de ayuda() no es una gran solución, porque no se puede garantizar que se llame a cualquier lugar help() use su versión en lugar de la versión predeterminada. Depender de ese tipo de parche de monos, en mi experiencia, lleva a problemas difíciles de depurar en una fecha posterior cuando llega un caso de uso inesperado (como una nueva herramienta de documentación, o un usuario con un ipython 'mejorado' o una GUI ayuda en un IDE, etc., etc.). – Ian

+0

Excelente, esto hizo el truco. Los primeros dos ejemplos de código son exactamente lo que escribí antes de publicar la pregunta. Estoy de acuerdo en que la sobrecarga de ayuda no es una gran idea. –

0

En lugar de jugar con la función, ¿por qué no escribir su propia función help?

my_global=42 

def help(func): 
    print('%s: my_global=%s'%(func.func_name,my_global))   

def foo(): 
    pass 

help(foo) 
+0

Estoy de acuerdo con @Ian, la sobrecarga del 'built' de la ayuda no es una gran idea, porque nunca está seguro de cómo se usará en otro lugar. Sin embargo, podría haber una implementación mejorada que envolvería un cambio entre una implementación personalizada y la predeterminada. –

Cuestiones relacionadas