web-dev-qa-db-fra.com

Comment définir par programme la docstring?

J'ai une fonction wrapper qui retourne une fonction. Existe-t-il un moyen de définir par programme la docstring de la fonction retournée? Si je pouvais écrire à __doc__, je procéderais comme suit:

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

Alors je pourrais faire

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

Cependant, puisque __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 j’essaie de faire. Même si en général, __doc__ est inscriptible dans mon cas, ce n’est pas le cas.

J'essaie de créer des cas de test pour unittest automatiquement. J'ai une fonction wrapper 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 que je tente de définir la docstring de testSomething, un message d'erreur s'affiche:

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

Je transmettrais la docstring à la fonction factory 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')
17
aaronasterling

Un instancemethod obtient sa docstring de son __func__. Changez la docstring de __func__ à la place. (L'attribut __doc__ des fonctions est accessible en écriture.)

>>> 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 la 2.7 docs :

Méthodes définies par l'utilisateur

Un objet méthode défini par l'utilisateur combine une classe, une instance de classe (ou None) et tout objet appelable (Normalement une fonction définie par l'utilisateur).

Attributs spéciaux en lecture seule: im_self est l’objet instance de la classe, im_func est l’objet function ; im_class est la classe de im_self pour les méthodes liées ou la classe qui a demandé la méthode pour les méthodes non liées;__doc__ est la documentation de la méthode (identique à im_func.__doc__); __name__ est le nom de la méthode (identique à im_func.__name__); __module__ est le nom du module dans lequel la méthode a été définie, ou None si non disponible.

Modifié dans la version 2.2: im_self faisait référence à la classe qui a défini la méthode.

Modifié dans la version 2.6: Pour la compatibilité aval 3.0, im_func est également disponible sous la forme __func__, et im_self sous la forme __self__.

45
hwiechers

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

L'exemple utilise le module abc (AbstractBaseClass). Il fonctionne avec une métaclasse ABCMeta spéciale

import abc

class MyNewClass(object):
    __metaclass__ = abc.ABCMeta

MyClass.__doc__ = "Changing the docstring works !"

help(MyNewClass)

aura pour résultat 

"""
Help on class MyNewClass in module __main__:

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

Je suppose que cela devrait être un commentaire, mais en rassemblant mes 50 premiers points ... 

6
jhp

Il suffit d'utiliser des 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'

Voici un cas d’utilisation similaire: Aide dynamique Python et génération de saisie semi-automatique

6
estani

__doc__ n'est pas accessible en écriture uniquement lorsque votre objet est de type 'type'.

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

4
satoru

Dans le cas où vous essayez de générer automatiquement des sous-classes unittest.TestCase, il se peut que plus de kilométrage remplace la méthode shortDescription .

C'est la méthode qui supprime la docstring sous-jacente jusqu'à la première ligne, comme dans la sortie unittest normale; ignorer cela était suffisant pour nous permettre de contrôler ce qui était indiqué dans des outils de reporting tels que TeamCity, ce dont nous avions besoin.

0
Paul Fenney