考虑以下python代码:
def function():
"Docstring"
name = ???
doc = ???
return name, doc
>>> function()
"function", "Docstring"
我需要更换问号,以便从同一个函数中获取函数的名称和文档字符串?
编辑: 到目前为止,大多数答案都明确地在其定义中硬编码函数的名称。是否有可能执行类似下面的操作,新函数get_name_doc将从调用它的外框访问该函数,并返回其名称和doc?
def get_name_doc():
???
def function():
"Docstring"
name, doc = get_name_doc()
return name, doc
>>> function()
"function", "Docstring"
答案 0 :(得分:14)
这是不可能以一致的方式干净地进行,因为名称可以更改和重新分配。
但是,只要不重命名或修饰该功能,就可以使用它。
>>> 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
,并且该函数是函数中的全局引用。因此,它会在每次执行时都在全局范围内查找。因此,对全局范围中的名称(例如装饰器)的更改将破坏您的代码。
答案 1 :(得分:6)
下面的代码解决了函数名称的问题。但是,它无法检测到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')
答案 2 :(得分: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()
答案 3 :(得分: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')
答案 4 :(得分:2)
对于我的个人项目,我为函数和类方法开发了函数名和 doc 恢复技术。这些是在可导入的模块(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()
答案 5 :(得分:1)
您必须使用该函数的名称来获取它:
def function():
"Docstring"
name = function.__name__
doc = function.__doc__
return name, doc
还有一个名为inspect的模块: http://docs.python.org/library/inspect.html。 这对于获取有关函数(或任何python对象)的更多信息非常有用。
答案 6 :(得分: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()
from this SO question。 Ayman Hourieh的答案给出了一个小例子。
答案 7 :(得分:1)
>>> def function():
"Docstring"
name = function.__name__
doc = function.__doc__
return name, doc
>>> function()
('function', 'Docstring')
答案 8 :(得分:1)
这个怎么样:
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
装饰器将(装饰的)函数对象添加为第一个参数。这样,函数可以在调用时访问自己的名称和docstring。
由于装饰,原始myfunc
功能被decor
取代。要使第一个参数与myfunc
完全相同,传递给函数的是decor
而不是func
。
functools.wraps
装饰器用于为decor
提供原始myfunc
函数的属性(名称,文档字符串等)。
答案 9 :(得分:1)
对于硬编码版本,与“表现良好”的装饰者合作得体。 它必须在函数后声明。如果函数稍后反弹,则更新会在此处更新。
def get_name_doc():
# global function # this is optional but makes your intent a bit more clear.
return function.__name__, function.__doc__
这是一个相当讨厌的黑客,因为它滥用默认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一起使用的字符串格式,而不是传统的代码,但它的工作原理非常整齐。这似乎是最有希望的变种。 您也许可以使用metaclasess执行此操作,这会导致整洁的代码,但隐藏在幕后的令人讨厌的东西,您需要编写代码。这个id建议反对
我怀疑通过简单地在模块末尾添加以下行,可能比进入检查/反射/模板/ metaclasess更简单。
help(<module>)
您正在处理的模块的名称(字符串)。甚至是变量__name__。如果使用多个模块,也可以在__init__.py文件中完成 或者在个别课程上我认为。
答案 10 :(得分: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的框架对象没有对函数对象的引用,它只带有对&#34; f_code&#34的引用;它使用的代码。它需要搜索引用的&#34; f_globals&#34;用于查找从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(),因为在绝大多数情况下,您确实希望将doc字符串作为某个辅助函数的参数传递给它。但是辅助函数可以很容易地从调用者那里获取doc字符串 - 我在我的unittest脚本中使用它,其中帮助函数可以使用测试的doc字符串将其发布到某些日志中或将其用作实际测试数据。这就是为什么提供的帮助程序只查找测试函数和测试成员函数的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)
留给读者扩展其他用例的例子。
答案 11 :(得分: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 ?
答案 12 :(得分:0)
您可以尝试以下方法:
import inspect
def my_function():
"""
Hello World!
"""
print(eval(inspect.currentframe().f_code.co_name).__doc__)
my_function() # Hello World!