我想编写一个具有动态创建的文档字符串的python函数。本质上对于函数func()
我希望func.__doc__
是一个调用自定义__get__
函数的描述符,根据请求创建文档字符串。然后help(func)
应该返回动态生成的文档字符串。
这里的上下文是在现有的分析包中编写一个包含大量命令行工具的python包。每个工具都成为一个类似命名的模块函数(通过函数工厂创建并插入到模块命名空间中),通过分析包动态生成函数文档和接口参数。
答案 0 :(得分:11)
你不能按照你想要的方式做你想做的事。
根据您的描述,您似乎可以这样做:
for tool in find_tools():
def __tool(*arg):
validate_args(tool, args)
return execute_tool(tool, args)
__tool.__name__ = tool.name
__tool.__doc__ = compile_docstring(tool)
setattr(module, tool.name, __tool)
即。在创建函数时,请事先创建文档字符串。
这是为什么docstring从一个__doc__
到下一个调用必须是动态的?
假设存在,则必须使用__call__
触发操作,将您的功能包装在一个类中。
但即便如此,你也遇到了问题。当调用help()来查找docstring时,会在类而不是实例上调用它,所以这样的事情:
class ToolWrapper(object):
def __init__(self, tool):
self.tool = tool
self.__name__ = tool.name
def _get_doc(self):
return compile_docstring(self.tool)
__doc__ = property(_get_doc)
def __call__(self, *args):
validate_args(args)
return execute_tool(tool, args)
不起作用,因为属性是实例,而不是类属性。您可以通过将doc属性放在元类而不是类本身
来使doc属性工作for tool in find_tools():
# Build a custom meta-class to provide __doc__.
class _ToolMetaclass(type):
def _get_doc(self):
return create_docstring(tool)
__doc__ = property(_get_doc)
# Build a callable class to wrap the tool.
class _ToolWrapper(object):
__metaclass__ = _ToolMetaclass
def _get_doc(self):
return create_docstring(tool)
__doc__ = property(_get_doc)
def __call__(self, *args):
validate_args(tool, args)
execute_tool(tool, args)
# Add the tool to the module.
setattr(module, tool.name, _ToolWrapper())
现在你可以做到
help(my_tool_name)
并获取自定义文档字符串或
my_tool_name.__doc__
同样的事情。 <{1}}属性位于__doc__
类中,需要捕获后一种情况。
答案 1 :(得分:0)
为什么不编写自己的help
函数?
my_global=42
def help(func):
print('%s: my_global=%s'%(func.func_name,my_global))
def foo():
pass
help(foo)
答案 2 :(得分:0)
(Python 3解决方案)
您可以利用Python的duck类型来实现动态字符串:
import time
def fn():
pass
class mydoc( str ):
def expandtabs( self, *args, **kwargs ):
return "this is a dynamic strting created on {}".format( time.asctime() ).expandtabs( *args, **kwargs )
fn.__doc__ = mydoc()
help( fn )
注意事项:
假设help
函数正在调用.expandtabs
以从__doc__
对象获取文本,该对象在Python 3.7中有效。更加健壮的解决方案将实现其他str
方法,以便即使help
方法发生更改,我们的鸭子也可以像鸭子一样继续工作。还要注意,我们的mydoc
类是从str
派生的,这是因为help
在某种程度上是非典型的,它通过断言isinstance(thing.__doc__, str)
来强制强类型化。像所有解决方案一样,这有点棘手,但是是否存在问题很大程度上取决于整个项目的需求。