2010-10-30 14 views
38

J'ai une fonction wrapper qui retourne une fonction. Existe-t-il un moyen de définir par programme la docstring de la fonction renvoyée? Si je pouvais écrire __doc__ je ferais ce qui suit:Comment est-ce que je programme la docstring par programme?

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

alors que je pouvais faire

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

Cependant, comme __doc__ est en lecture seule, je ne peux pas le faire. Quelle est la bonne façon? Edit: Ok, je voulais garder cela simple, mais bien sûr ce n'est pas ce que je suis en train d'essayer de faire. Même si en général __doc__ est inscriptible dans mon cas, il ne l'est pas.

J'essaie de créer des bancs d'essai pour unittest automatiquement. J'ai une fonction d'emballage qui crée un objet de classe qui est une sous-classe 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 je crée cette classe et essayer de régler le docstring de testSomething je reçois une erreur:

>>> 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

Pourquoi ne pas il suffit d'écrire une docstring? –

+5

@RaeKettler: Parce que si vous le mettez à jour, vous devez toujours vous rappeler de mettre à jour manuellement toutes les autres copies dans toutes les autres fonctions d'encapsulation – endolith

Répondre

13

Je passerais la docstring dans la fonction usine et utiliserais type pour construire manuellement la classe.

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__ n'est pas accessible en écriture seulement quand votre objet est de type 'type'.

Dans votre cas, add_three est une fonction et vous pouvez simplement définir __doc__ sur n'importe quelle chaîne.

41

Un instancemethod tire son docstring de son __func__. Modifiez la docstring de __func__ à la place. (L'attribut __doc__ des fonctions sont sont inscriptibles.)

>>> 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 

De l'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

utiliser juste décorateurs. Voici votre cas:

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' 

est ici un cas d'utilisation similaire: Python dynamic help and autocomplete generation

0

Dans le cas où vous essayez de générer automatiquement des sous-classes unittest.TestCase, vous pouvez avoir plus de kilométrage remplaçant leur méthode shortDescription.

C'est la méthode qui dépouille la docstring sous-jacente jusqu'à la première ligne, comme dans la sortie unittest normale; Il était suffisant de nous donner le contrôle sur ce qui se présentait dans les outils de reporting comme TeamCity, ce dont nous avions besoin.

5

Ceci est un ajout au fait que l'attribut __doc__ des classes de type type ne peut pas être modifié. Le point intéressant est que ceci n'est vrai que tant que la classe est créée en utilisant le type. Dès que vous utilisez une métaclasse, vous pouvez simplement changer __doc__.

L'exemple utilise le module abc (AbstractBaseClass). Il fonctionne à l'aide d'un ABCMeta métaclasse spécial

import abc 

class MyNewClass(object): 
    __metaclass__ = abc.ABCMeta 

MyClass.__doc__ = "Changing the docstring works !" 

help(MyNewClass) 

se traduira par

""" 
Help on class MyNewClass in module __main__: 

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

Je suppose que cela devrait être un commentaire, mais la collecte encore mes premiers 50 points ...

Questions connexes