2010-10-30 15 views
38

Tengo una función de envoltura que devuelve una función. ¿Hay alguna manera de establecer programáticamente la docstring de la función devuelta? Si pudiera escribir en __doc__ yo haría lo siguiente:¿Cómo configuro programáticamente la docstring?

def wrapper(a): 
    def add_something(b): 
     return a + b 
    add_something.__doc__ = 'Adds ' + str(a) + ' to `b`' 
    return add_something 

Entonces podría hacer

>>> add_three = wrapper(3) 
>>> add_three.__doc__ 
'Adds 3 to `b` 

Sin embargo, desde __doc__ es de sólo lectura, no puedo hacer eso. ¿Cuál es la forma correcta?


Editar: Ok, quería mantener esto simple, pero por supuesto esto no es lo que en realidad estoy tratando de hacer. Aunque en general __doc__ es grabable en mi caso, no es así.

Estoy tratando de crear cajas de prueba para unittest automáticamente. Tengo una función de contenedor que crea un objeto de la clase que es una subclase de unittest.TestCase:

import unittest 
def makeTestCase(filename, my_func): 
    class ATest(unittest.TestCase): 
     def testSomething(self): 
      # Running test in here with data in filename and function my_func 
      data = loadmat(filename) 
      result = my_func(data) 
      self.assertTrue(result > 0) 

    return ATest 

Si creo esta clase y tratar de establecer la cadena de documentación de testSomething Me aparece un error:

>>> def my_func(): pass 
>>> MyTest = makeTestCase('some_filename', my_func) 
>>> MyTest.testSomething.__doc__ = 'This should be my docstring' 
AttributeError: attribute '__doc__' of 'instancemethod' objects is not writable 
+1

¿Por qué no solo escribe un docstring? –

+5

@RaeKettler: Porque si lo actualizas, siempre debes recordar actualizar manualmente todas las otras copias en todas las otras funciones del contenedor – endolith

Respuesta

13

Pasaría el docstring a la función de fábrica y usaría type para construir manualmente la clase.

def make_testcase(filename, myfunc, docstring): 
    def test_something(self): 
     data = loadmat(filename) 
     result = myfunc(data) 
     self.assertTrue(result > 0) 

    clsdict = {'test_something': test_something, 
       '__doc__': docstring} 
    return type('ATest', (unittest.TestCase,), clsdict) 

MyTest = makeTestCase('some_filename', my_func, 'This is a docstring') 
4

__doc__ no se puede escribir solo cuando tu objeto es de tipo 'tipo'.

En su caso, add_three es una función y solo puede establecer __doc__ en cualquier cadena.

41

Un instancemethod obtiene su cadena de documentación de su __func__. Cambie el docstring de __func__ en su lugar. (El atributo __doc__ de funciones son son grabable.)

>>> class Foo(object): 
...  def bar(self): 
...   pass 
... 
>>> Foo.bar.__func__.__doc__ = "A super docstring" 
>>> help(Foo.bar) 
Help on method bar in module __main__: 

bar(self) unbound __main__.Foo method 
    A super docstring 

>>> foo = Foo() 
>>> help(foo.bar) 
Help on method bar in module __main__: 

bar(self) method of __main__.Foo instance 
    A super docstring 

Desde el 2.7 docs:

User-defined methods

A user-defined method object combines a class, a class instance (or None) and any callable object (normally a user-defined function).

Special read-only attributes: im_self is the class instance object, im_func is the function object; im_class is the class of im_self for bound methods or the class that asked for the method for unbound methods; __doc__ is the method’s documentation (same as im_func.__doc__);__name__ is the method name (same as im_func.__name__); __module__ is the name of the module the method was defined in, or None if unavailable.

Changed in version 2.2: im_self used to refer to the class that defined the method.

Changed in version 2.6: For 3.0 forward-compatibility, im_func is also available as __func__ , and im_self as __self__ .

4

sólo tiene que utilizar decoradores. Aquí está su caso:

def add_doc(value): 
    def _doc(func): 
     func.__doc__ = value 
     return func 
    return _doc 

import unittest 
def makeTestCase(filename, my_func): 
    class ATest(unittest.TestCase): 
     @add_doc('This should be my docstring') 
     def testSomething(self): 
      # Running test in here with data in filename and function my_func 
      data = loadmat(filename) 
      result = my_func(data) 
      self.assertTrue(result > 0) 

    return ATest 

def my_func(): pass 

MyTest = makeTestCase('some_filename', my_func) 
print MyTest.testSomething.__doc__ 
> 'This should be my docstring' 

aquí es un caso de uso similar: Python dynamic help and autocomplete generation

0

En el caso de que usted está tratando de generar automáticamente las subclases unittest.TestCase, es posible que tenga más kilometraje anulando su método shortDescription.

Este es el método que quita la docstring subyacente a la primera línea, como se ve en la salida de prueba de la unidad normal; anularlo fue suficiente para darnos control sobre lo que aparecía en herramientas de informes como TeamCity, que era lo que necesitábamos.

5

Esto es una adición al hecho de que el atributo __doc__ de las clases del tipo type no se puede cambiar. El punto interesante es que esto solo es cierto siempre que la clase se cree utilizando el tipo. Tan pronto como utilice una metaclase, puede simplemente cambiar __doc__.

El ejemplo utiliza el módulo abc (AbstractBaseClass). Funciona utilizando un ABCMeta metaclase especial

import abc 

class MyNewClass(object): 
    __metaclass__ = abc.ABCMeta 

MyClass.__doc__ = "Changing the docstring works !" 

help(MyNewClass) 

dará lugar a

""" 
Help on class MyNewClass in module __main__: 

class MyNewClass(__builtin__.object) 
| Changing the docstring works ! 
""" 

Asumo que esto debería ser un comentario, pero aún así la recopilación de mis primeros 50 puntos ...

Cuestiones relacionadas