Как распечатать Docstring функции python изнутри самой функции?

Я хочу распечатать строку документации функции python изнутри самой функции. для например.

def my_function(self):
  """Doc string for my function."""
  # print the Docstring here.

На данный момент я делаю это сразу после определения my_function.

print my_function.__doc__

Но предпочел бы, чтобы функция делала это сама.

Я пробовал вызывать print self.__doc__ print self.my_function.__doc__ и print this.__doc__ внутри my_function, но это не сработало.


person shane87    schedule 11.01.2012    source источник


Ответы (8)


def my_func():
    """Docstring goes here."""
    print my_func.__doc__

Это будет работать до тех пор, пока вы не измените объект, привязанный к имени my_func.

new_func_name = my_func
my_func = None

new_func_name()
# doesn't print anything because my_func is None and None has no docstring

Ситуации, в которых вы бы это сделали, довольно редки, но они случаются.

Однако, если вы напишете декоратор следующим образом:

def passmein(func):
    def wrapper(*args, **kwargs):
        return func(func, *args, **kwargs)
    return wrapper

Теперь вы можете сделать это:

@passmein
def my_func(me):
    print me.__doc__

И это гарантирует, что ваша функция получит ссылку на себя (аналогично self) в качестве первого аргумента, поэтому она всегда сможет получить строку документации нужной функции. При использовании в методе обычным self становится вторым аргументом.

person kindall    schedule 11.01.2012
comment
Мне очень нравится ваш метод декоратора. Гораздо более pythonic и менее рискованно, чем проверка кадра, И позволяет вам избежать использования имени функции. Слик! Проголосовал! - person FlipMcF; 21.02.2012
comment
Разве *args и **kwargs не должны быть в списке аргументов my_func в последнем фрагменте кода? Или дополнительные аргументы были просто опущены, хотя программист мог поместить любые дополнительные аргументы в определение функции. Я не уверен, что это ясно, хотя в тексте это указано в качестве первого аргумента. - person RufusVS; 27.08.2017
comment
Идея состоит в том, что оболочка должна иметь возможность принимать любые аргументы, потому что у нее нет возможности узнать (во время оформления), какие аргументы принимает обернутая функция. Затем он передает все полученные аргументы обернутой функции. Если есть неправильный номер, вы получите сообщение об ошибке в этот момент. - person kindall; 27.08.2017
comment
@FlipMcF, я согласен, что этот подход великолепен, но какой код здесь, в этом вопросе или ответе, фактически выполнял проверку кадра? ИЗМЕНИТЬ О! Другой возможный ответ ниже: stackoverflow.com/a/25549647/1175496 Я должен просто Ctrl+F :) - person The Red Pea; 25.12.2017

Это должно работать (в моих тестах это работает, включая вывод). Возможно, вы могли бы использовать __doc__ вместо getdoc, но мне это нравится, поэтому я использовал именно его. Кроме того, это не требует от вас знания имен класса/метода/функции.

Примеры для класса, метода и функции. Скажите, если это не то, что вы искали :)

from inspect import *

class MySelfExplaningClass:
    """This is my class document string"""

    def __init__(self):
        print getdoc(self)

    def my_selfexplaining_method(self):
        """This is my method document string"""
        print getdoc(getattr(self, getframeinfo(currentframe()).function))


explain = MySelfExplaningClass()

# Output: This is my class document string

explain.my_selfexplaining_method()

# Output: This is my method document string

def my_selfexplaining_function():
    """This is my function document string"""
    print getdoc(globals()[getframeinfo(currentframe()).function])

my_selfexplaining_function()

# Output: This is my function document string
person Tehnix    schedule 12.01.2012

Это работает:

def my_function():
  """Docstring for my function"""
  #print the Docstring here.
  print my_function.__doc__

my_function()

в Питоне 2.7.1

Это также работает:

class MyClass(object):
    def my_function(self):
        """Docstring for my function"""
        #print the Docstring here, either way works.
        print MyClass.my_function.__doc__
        print self.my_function.__doc__


foo = MyClass()

foo.my_function()

Однако это не будет работать само по себе:

class MyClass(object):
    def my_function(self):
        """Docstring for my function"""
        #print the Docstring here.
        print my_function.__doc__


foo = MyClass()

foo.my_function()

NameError: глобальное имя «my_function» не определено

person jgritty    schedule 11.01.2012
comment
Ваш метод класса работает только потому, что вы ранее определили my_function как функцию в глобальном пространстве имен. Попробуйте это со свежим экземпляром Python;) - person Alex Leach; 11.01.2012
comment
@jgritty Вы не проверили свой второй фрагмент. это не работает - person eyquem; 11.01.2012
comment
@Alex Leach Вы тестировали фрагмент с классом? Не работает, на самом деле.... - person eyquem; 11.01.2012
comment
@jgritty и Алекс Лич. Методы, т. е. функции, определенные в классе, не могут знать вне их пространства. См. этот вопрос (stackoverflow.com/questions/1765677/python- вложенные классы-область/) и мой ответ на него - person eyquem; 11.01.2012
comment
нет, я этого не делал, но я предположил, что это мог сделать jgritty. Только что протестировал его, он работает нормально и, как и ожидалось (измените строку документации метода, по-прежнему печатает строку документации функции). Функции, определенные в модуле, доступны внутри методов класса. например Нет необходимости повторно импортировать что-то в методе класса, если оно было импортировано в начале скрипта. - person Alex Leach; 11.01.2012
comment
Все протестировано в одном экземпляре интерпретатора Python. Фрагмент класса не будет работать сам по себе, я согласен;) - person Alex Leach; 11.01.2012
comment
Конечно же, я сделал ошибку. Я отредактировал пример, так что теперь он действительно работает. - person jgritty; 12.01.2012

Для этого есть довольно простой способ, о котором еще никто не упоминал:

import inspect

def func():
    """Doc string"""
    print inspect.getdoc(func)

И это делает то, что вы хотите.

Здесь нет ничего необычного. Все, что происходит, это то, что выполнение func.__doc__ в функции откладывает разрешение атрибута на достаточно долгое время, чтобы поиск __doc__ работал так, как вы ожидаете.

Я использую это с docopt для точек входа консольного скрипта.

person Keith Gaughan    schedule 11.01.2017
comment
Но это не лучше, чем просто print func.__doc__ - person Reed_Xia; 22.05.2019

Вы задали свой вопрос как метод класса, а не как функцию. Здесь важны пространства имен. Для функции подойдет print my_function.__doc__, так как my_function находится в глобальном пространстве имен.

Для метода класса print self.my_method.__doc__ будет правильным решением.

Если вы не хотите указывать имя метода, а хотите передать ему переменную, вы можете использовать встроенные функции hasattr(object,attribute) и getattr(obj,attr), которые делают, как говорится, позволяя вам передавать переменные со строками, являющимися именем метода. например

class MyClass:
    def fn(self):
        """A docstring"""
        print self.fn.__doc__ 

def print_docstrings(object):
   for method in dir( object ):
       if method[:2] == '__':  # A protected function
           continue
       meth = getattr( object, method )
       if hasattr( meth , '__doc__' ):
           print getattr( meth , '__doc__' )

x = MyClass()
print_docstrings( x )
person Alex Leach    schedule 11.01.2012
comment
есть ли хороший способ не повторять само имя метода? что-то вроде этого.__doc__ или что-то в этом роде? извините, я новичок в python .. - person shane87; 11.01.2012
comment
да, не совсем .. Я просмотрел dir( self.my_function ), и self.my_function.__func__ выглядел, возможно, интересным, но к нему все еще нужно получить доступ через эту объектную модель. Вы можете добавить такую ​​функцию: for method in dir(MyClass): if hasattr(getattr(MyClass,method),'__doc__'): print getattr( getattr( MyClass,method), '__doc__') - person Alex Leach; 11.01.2012
comment
очевидно, что отступ не слишком полезен. Я вставлю это в свой первоначальный ответ... - person Alex Leach; 11.01.2012
comment
@ shane87, AlexLeach и MattLuongo: посмотрите мой ответ, чтобы узнать, как не повторять имя класса/метода/функции. - person Tehnix; 12.01.2012

Как неоднократно отмечалось, использование имени функции — это динамический поиск в каталоге globals(). Работает только в модуле определения и только для глобальной функции. Если вы хотите узнать строку документа функции-члена, вам также нужно будет найти путь по имени класса, что довольно громоздко, поскольку эти имена могут быть довольно длинными:

def foo():
    """ this is foo """
    doc = foo.__doc__
class Foo:
    def bar(self):
       """ this is bar """
       doc = Foo.bar.__doc__

эквивалентно

def foo():
    """ this is foo """
    doc = globals()["foo"].__doc__
class Foo:
    def bar(self):
       """ this is bar """
       doc = globals()["Foo"].bar.__doc__

Если вы хотите найти строку документа вызывающего объекта, это в любом случае не сработает, поскольку ваш вспомогательный модуль печати может находиться в совершенно другом модуле с совершенно другим словарем globals(). Единственный правильный выбор - заглянуть во фрейм стека, но Python не дает вам исполняемый объект функции, он имеет только ссылку на объект кода "f_code". Но продолжайте, так как есть еще ссылка на "f_globals" этой функции. Таким образом, вы можете написать функцию для получения документа вызывающего абонента, подобного этому, и в качестве варианта вы получите свою собственную строку документа.

import inspect

def get_caller_doc():
    frame = inspect.currentframe().f_back.f_back
    for objref in frame.f_globals.values():
        if inspect.isfunction(objref):
            if objref.func_code == frame.f_code:
                return objref.__doc__
        elif inspect.isclass(objref):
            for name, member in inspect.getmembers(objref):
                if inspect.ismethod(member):
                    if member.im_func.func_code == frame.f_code:
                        return member.__doc__

и пошли тестить:

def print_doc():
   print get_caller_doc()

def foo():
   """ this is foo """
   print_doc()

class Foo:
    def bar(self):
       """ this is bar """
       print_doc()

def nothing():
    print_doc()

class Nothing:
    def nothing(self):
        print_doc()

foo()
Foo().bar()

nothing()
Nothing().nothing()

# and my doc

def get_my_doc():
    return get_caller_doc()

def print_my_doc():
    """ showing my doc """
    print get_my_doc()

print_my_doc()

результаты в этом выводе

 this is foo 
 this is bar 
None
None
 showing my doc 

На самом деле, большинству людей нужна их собственная строка документа только для того, чтобы передавать ее в качестве аргумента, но вызываемая вспомогательная функция может искать ее самостоятельно. Я использую это в своем коде unittest, где это иногда удобно для заполнения некоторых журналов или для использования строки документа в качестве тестовых данных. Вот почему представленный get_caller_doc() ищет только глобальные тестовые функции и функции-члены тестового класса, но я думаю, что этого достаточно для большинства людей, которые хотят узнать о строке документа.

class FooTest(TestCase):
    def get_caller_doc(self):
        # as seen above
    def test_extra_stuff(self):
        """ testing extra stuff """
        self.createProject("A")
    def createProject(self, name):
        description = self.get_caller_doc()
        self.server.createProject(name, description)

Определение правильного get_frame_doc(frame) с помощью sys._getframe(1) остается за читателем().

person Guido U. Draheim    schedule 28.08.2014

Пытаться:

class MyClass():
    # ...
    def my_function(self):
        """Docstring for my function"""
        print MyClass.my_function.__doc__
        # ...

(*) После my_function() отсутствовало двоеточие (:)

person juliomalegria    schedule 11.01.2012
comment
извините, ребята, моя глупость self.my_function.__doc__ действительно работает - person shane87; 11.01.2012

вставка print __doc__ сразу после объявления класса перед def __init__ будет печатать строку документа на консоли каждый раз, когда вы инициируете объект с классом

person emorphus    schedule 08.03.2017
comment
Но вопрос заключался в том, чтобы сообщать о входе в функцию, а не о создании экземпляра класса. - person RufusVS; 27.08.2017
comment
Извини за это. Тогда это будет то, что есть в принятом ответе. напечатать func_name.__doc__ - person emorphus; 27.08.2017