20

考虑以下python代码:

def function():
    "Docstring"

    name = ???
    doc = ???

    return name, doc

>>> function()
"function", "Docstring"

我需要用什么替换问号,以便从同一个函数中获取函数的名称和文档字符串?

编辑:到目前为止,大多数答案都明确地将函数的名称硬编码在其定义中。是否有可能做类似下面的事情,新函数 get_name_doc 将从调用它的外部框架访问该函数,并返回其名称和文档?

def get_name_doc():
    ???

def function():
    "Docstring"

    name, doc = get_name_doc()

    return name, doc

>>> function()
"function", "Docstring"
4

13 回答 13

17

这是不可能以一致的方式干净地完成的,因为名称可以更改和重新分配。

但是,只要函数没有被重命名或修饰,您就可以使用它。

>>> def test():
...     """test"""
...     doc = test.__doc__
...     name = test.__name__
...     return doc, name
... 
>>> test()
('test', 'test')
>>> 

这根本不可靠。这是一个出错的例子。

>>> def dec(f):
...     def wrap():
...         """wrap"""
...         return f()
...     return wrap
... 
>>> @dec
... def test():
...     """test"""
...     return test.__name__, test.__doc__
... 
>>> test()
('wrap', 'wrap')
>>> 

这是因为名称test在函数实际创建时没有定义,并且是函数中的全局引用。因此,每次执行时都会在全局范围内查找它。因此,在全局范围内更改名称(例如装饰器)会破坏您的代码。

于 2012-04-06T00:36:21.713 回答
8

下面的代码解决了函数名称的问题。但是,它无法检测到 aaronasterling 给出的示例的正确文档字符串。我想知道是否有办法回到与字节码对象关联的抽象语法树。那么阅读文档字符串就很容易了。

import inspect

def get_name_doc():
    outerframe = inspect.currentframe().f_back
    name = outerframe.f_code.co_name
    doc = outerframe.f_back.f_globals[name].__doc__    
    return name, doc

if __name__ == "__main__":

    def function():
        "Docstring"

        name, doc = get_name_doc()

        return name, doc

    def dec(f):
        def wrap():
           """wrap"""
           return f()
        return wrap

    @dec
    def test():
        """test"""
        return get_name_doc()

    assert function() == ('function', "Docstring")
    #The assertion below fails:. It gives: ('test', 'wrap')
    #assert test() == ('test', 'test')
于 2012-04-06T01:38:49.837 回答
3

这个怎么样:

import functools

def giveme(func):
    @functools.wraps(func)
    def decor(*args, **kwargs):
        return func(decor, *args, **kwargs)
    return decor

@giveme
def myfunc(me):
    "docstr"
    return (me.__name__, me.__doc__)

# prints ('myfunc', 'docstr')
print myfunc()

很快,giveme装饰器将(装饰的)函数对象添加为第一个参数。这样,函数在被调用时可以访问自己的名称和文档字符串。

由于装饰,原来的myfunc功能被替换为decor. 为了使第一个参数与 完全相同myfunc,传递给函数的是decor和不是func

functools.wraps装饰器用于给出原始函数的decor属性(名称、文档字符串等) 。myfunc

于 2012-04-06T03:59:19.360 回答
2

这将找到调用 get_doc 的函数的名称和文档。在我的意义上, get_doc 应该将函数作为参数(这会使它变得更容易,但实现起来的乐趣更少;))

import inspect

def get_doc():
    """ other doc
    """
    frame = inspect.currentframe()

    caller_frame = inspect.getouterframes(frame)[1][0]
    caller_name = inspect.getframeinfo(caller_frame).function
    caller_func = eval(caller_name)

    return caller_name, caller_func.__doc__


def func():
    """ doc string """
    print get_doc()
    pass


def foo():
    """ doc string v2 """
    func()

def bar():
    """ new caller """
    print get_doc()

func()
foo()
bar()
于 2012-04-06T01:03:32.243 回答
2
>>> import inspect
>>> def f():
...     """doc"""
...     name = inspect.getframeinfo(inspect.currentframe()).function
...     doc = eval(name + '.__doc__')
...     return name, doc
... 
>>> f()
('f', 'doc')
>>> class C:
...     def f(self):
...         """doc"""
...         name = inspect.getframeinfo(inspect.currentframe()).function
...         doc = eval(name + '.__doc__')
...         return name, doc
... 
>>> C().f()
('f', 'doc')
于 2013-10-25T21:45:32.490 回答
2

对于我的个人项目,我为函数和类方法开发了函数名称和文档恢复技术。这些是在一个可导入的模块(SelfDoc.py)中实现的,该模块在其main中有自己的自检。它包含在下面。此代码在 Linux 和 MacOS 上的 Python 2.7.8 中执行。它正在积极使用中。

#!/usr/bin/env python

from inspect import (getframeinfo, currentframe, getouterframes)

class classSelfDoc(object):

    @property
    def frameName(self):
        frame = getframeinfo(currentframe().f_back)
        return str(frame.function)

    @property
    def frameDoc(self):
        frame = getframeinfo(currentframe().f_back)
        doc = eval('self.'+str(frame.function)+'.__doc__')
        return doc if doc else 'undocumented'

def frameName():
    return str(getframeinfo(currentframe().f_back).function)

def frameDoc():
    doc = eval(getframeinfo(currentframe().f_back).function).__doc__
    return doc if doc else 'undocumented'

if __name__ == "__main__":

    class aClass(classSelfDoc):
        "class documentation"

        def __init__(self):
            "ctor documentation"
            print self.frameName, self.frameDoc

        def __call__(self):
            "ftor documentation"
            print self.frameName, self.frameDoc

        def undocumented(self):
            print self.frameName, self.frameDoc

    def aDocumentedFunction():
        "function documentation"
        print frameName(), frameDoc()

    def anUndocumentedFunction():
        print frameName(), frameDoc()

    anInstance = aClass()
    anInstance()
    anInstance.undocumented()

    aDocumentedFunction()
    anUndocumentedFunction()
于 2014-10-04T15:43:32.183 回答
1

您必须使用函数的名称来获取它:

def function():
    "Docstring"

    name = function.__name__
    doc = function.__doc__

    return name, doc

还有一个名为检查的模块:http: //docs.python.org/library/inspect.html。这对于获取有关函数(或任何 python 对象)的更多信息很有用。

于 2012-04-06T00:35:06.687 回答
1
def function():
    "Docstring"

    name = function.__name__
    doc = function.__doc__

    return name, doc    

这应该做到这一点,在你的情况下使用函数的名称,function.

这是一个非常好的教程,讨论它:http ://epydoc.sourceforge.net/docstrings.html

当然:http ://docs.python.org/tutorial/controlflow.html#documentation-strings

编辑:请参阅您编辑的问题版本,我认为您可能不得不inspect.stack() 从这个 SO question中弄乱。Ayman Hourieh 的回答举了一个小例子。

于 2012-04-06T00:35:38.063 回答
1
>>> def function():
        "Docstring"

        name = function.__name__
        doc = function.__doc__

        return name, doc

>>> function()
('function', 'Docstring')
于 2012-04-06T00:36:30.213 回答
1

对于与“表现良好”的装饰器一起工作的硬编码版本。它必须在函数之后声明。如果函数稍后反弹,则会在此处更新更改。

def get_name_doc():
    # global function # this is optional but makes your intent a bit more clear.
    return function.__name__, function.__doc__

这是一个相当讨厌的 hack,因为它滥用了默认 args 的工作方式。它将使用在此函数“初始化”时绑定到的任何函数,并且即使该函数被反弹,也要记住它。用 args 调用它会导致有趣的结果。

def get_name_doc(fn=function):
    return fn.__name__, fn.__doc__

还有一个动态的,它仍然是硬编码的,但在函数被调用时会更新,参数为 True。基本上这个版本只会在被告知这样做时更新。

def get_name_doc(update=False):
    global fn
    if update:
        fn = function
    return fn.__name__, fn.__doc__

现在当然也有装饰器的例子。

@decorator  # applying the decorator decorator to make it well behaved
def print_name_doc(fn, *args, **kwargs):
    def inner(*args, **kwargs):
         print(fn.__doc__, fn.__name__)  # im assuming you just want to print in this case
         return fn(*args, **kwargs)
return inner

你应该阅读装饰器装饰器(至少)。查看 NamedTuple 源(来自集合模块),因为它涉及到硬编码。可悲的是,命名元组代码相当奇怪。它是一种与 eval 而非传统代码一起使用的字符串格式,但它工作得非常巧妙。这似乎是最有希望的变体。您也可以使用元类来做到这一点,这会导致代码简洁,但隐藏在幕后的讨厌的东西,您需要编码。这个 id 建议不要

我怀疑可能有一种比进入检查/反射/模板/元类更简单的方法,只需在模块末尾添加以下行。

help(<module>)

您正在处理的模块的名称在哪里(字符串)。甚至是变量 __name__。如果使用多个模块或单个类,我认为这也可以在 __init__.py 文件中完成。

于 2014-04-29T18:20:20.380 回答
1

正如多次提到的,在函数内部使用函数名实际上是在当前模块的 globals() 中动态查找。使用任何类型的 eval() 只是它的一种变体,因为它的名称解析将再次与 globals() 字典一起使用。大多数示例都会因成员函数而失败 - 您需要先从 globals() 查找类名,然后才能从中访问成员函数。所以实际上

def function():
    """ foo """
     doc = function.__doc__

class Class:
    def function():
        """ bar """
        doc = Class.function.__doc__

相当于

def function():
    """ foo """
     doc = globals()["function"].__doc__

class Class:
    def function():
        """ bar """
        doc = globals()["Class"].function.__doc__

在许多情况下,这种动态查找就足够了。但实际上你必须在函数中重新输入函数名。但是,如果您编写一个辅助函数来找出调用者的文档字符串,那么您将面临这样一个事实,即辅助函数可能存在于具有不同 globals() 字典的不同模块中。所以唯一正确的方法是使用当前的帧信息来查找函数——但是 Python 的帧对象没有对函数对象的引用,它只携带对它使用的“f_code”代码的引用。它需要通过引用的“f_globals”字典来查找从f_code到函数对象的映射,例如像这样:

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__

它被命名为 get_caller_doc() 而不是 get_my_doc() 因为在绝大多数情况下,您确实希望将文档字符串作为参数传递给某个辅助函数。但是辅助函数可以很容易地从它的调用者那里获取文档字符串——我在我的单元测试脚本中使用它,其中一个辅助函数可以使用测试的文档字符串将其发布到一些日志中或将其用作实际的测试数据。这就是为什么提供的帮助程序只查找测试函数和测试成员函数的文档字符串。

class MyTest:
    def test_101(self):
        """ some example test """
        self.createProject("A")
    def createProject(self, name):
        description = get_caller_doc()
        self.server.createProject(name, description)

留给读者扩展其他用例的示例。

于 2014-08-28T13:28:53.837 回答
1

参考http://stefaanlippens.net/python_inspect

import inspect
# functions
def whoami():
    return inspect.stack()[1][3]
def whocalledme():
    return inspect.stack()[2][3]
def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme())
    bar()
def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme())
johny = bar
# call them!
foo()
bar()
johny()

输出:

hello, I'm foo, daddy is ?
hello, I'm bar, daddy is foo
hello, I'm bar, daddy is ?
hello, I'm bar, daddy is ?
于 2015-01-14T23:19:53.167 回答
0

你可以试试这个:

import inspect


def my_function():
    """
    Hello World!
    """
    print(eval(inspect.currentframe().f_code.co_name).__doc__)


my_function()  # Hello World!
于 2019-10-21T03:33:46.867 回答