2010-10-30 5 views
38

Ich habe eine Wrapper-Funktion, die eine Funktion zurückgibt. Gibt es eine Möglichkeit, den Docstring der zurückgegebenen Funktion programmgesteuert festzulegen? Wenn ich __doc__ schreiben könnte würde ich folgendes tun:Wie programmiere ich den Docstring programmatisch?

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

Dann konnte ich jedoch

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

tun, da __doc__ schreibgeschützt ist, kann ich das nicht tun. Was ist der richtige Weg?

Edit: Ok, ich wollte das einfach halten, aber das ist natürlich nicht das, was ich eigentlich versuche. Auch wenn im Allgemeinen __doc__ in meinem Fall beschreibbar ist, ist es nicht.

Ich versuche, Testfälle für unittest automatisch zu erstellen. Ich habe eine Wrapper-Funktion, die ein Objekt der Klasse erstellt, die eine Unterklasse von unittest.TestCase ist:

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 

Wenn ich diese Klasse erstellen und versuchen, das docstring von testSomething ich einen Fehler zu setzen:

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

Warum gehst du nicht schreibe einfach einen Docstring? –

+5

@RaeKettler: Weil Sie dann, wenn Sie es aktualisieren, immer daran denken müssen, alle anderen Kopien in allen anderen Wrapper-Funktionen manuell zu aktualisieren – endolith

Antwort

13

Ich würde den Docstring in die Factory-Funktion übergeben und type verwenden, um die Klasse manuell zu erstellen.

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__ ist nur schreibbar, wenn Ihr Objekt vom Typ 'type' ist.

In Ihrem Fall ist add_three eine Funktion und Sie können einfach __doc__ auf eine beliebige Zeichenfolge festlegen.

41

Ein instancemethod bekommt sein docstring von seinem __func__. Ändern Sie stattdessen den Docstring von __func__. (Das __doc__ Attribut Funktionen sind beschreibbar.)

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

Vom 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

Nur Dekorateure benutzen. Hier ist Ihr Fall ist:

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' 

Hier ist ein ähnlicher Anwendungsfall: Python dynamic help and autocomplete generation

0

In dem Fall, dass Sie versuchen, automatisch zu unittest.TestCase Subklassen zu erzeugen, können Sie mehr Kilometer zwingende ihre shortDescription Methode haben.

Dies ist die Methode, die den zugrunde liegenden Docstring auf die erste Zeile reduziert, wie in der normalen Unittest-Ausgabe zu sehen ist; das Überschreiben war genug, um uns die Kontrolle über das zu geben, was in Reporting-Tools wie TeamCity auftauchte, was wir brauchten.

5

Dies ist eine Ergänzung zu der Tatsache, dass das __doc__ Attribut der Klassen type nicht geändert werden kann. Der interessante Punkt ist, dass dies nur gilt, solange die Klasse mit type erstellt wird. Sobald Sie eine Metaklasse verwenden, können Sie einfach nur __doc__ ändern.

Das Beispiel verwendet das Modul abc (AbstractBaseClass). Es funktioniert eine spezielle ABCMeta metaclass mit

import abc 

class MyNewClass(object): 
    __metaclass__ = abc.ABCMeta 

MyClass.__doc__ = "Changing the docstring works !" 

help(MyNewClass) 

in

""" 
Help on class MyNewClass in module __main__: 

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

führen werde ich nehme an, das ein Kommentar sein sollte, aber immer noch meine ersten 50 Punkte sammeln ...