您如何获得功能主体?

时间:2019-07-15 07:20:16

标签: python

我需要将签名与Python函数的主体分开,即:

def mult(a, b):
    "Multiplication"
    if a or b is None:
       # border case
       return None
    else:
       return a * b

(该功能仅用于演示)。

我知道inspect.getsourcelines(mult)将为我提供完整的代码,但我只想拥有正文,即少签名和文档字符串:

if a or b is None:
   # border case
   return None
else:
   return a * b

是否有一种理想的方式来实现这一点,理想情况下是使用Python3内置的解析工具,而不是字符串操作?

4 个答案:

答案 0 :(得分:2)

由于我不知道执行此操作的任何函数,因此这是一个应该起作用的自制函数。 我没有时间讨论更深入的用例。因此,我将在这里留下我不完整的答案。

def get_body(func):
    """ Using the magic method __doc__, we KNOW the size of the docstring.
        We then, just substract this from the total length of the function
    """
    try:
        lines_to_skip = len(func.__doc__.split('\n'))
    except AttributeError:
        lines_to_skip = 0

    lines = getsourcelines(func)[0]

    return ''.join( lines[lines_to_skip+1:] )

def ex_0(a, b):
    """ Please !
        Don't

    Show
This
    """
    if a or b is None:
       # border case
       return None
    else:
       return a * b

def ex_1(a, b):
    ''' Please !Don'tShowThis'''
    if a or b is None:
       # border case
       return None
    else:
       return a * b

def ex_2(a, b):
    if a or b is None:
       # border case
       return None
    else:
       return a * b

def ex_3(bullshit, hello):
    pass

get_body(ex_0)
    # if a or b is None:
    #    # border case
    #    return None
    # else:
    #    return a * b

get_body(ex_1)
    # if a or b is None:
    #    # border case
    #    return None
    # else:
    #    return a * b

get_body(ex_2)
    # if a or b is None:
    #    # border case
    #    return None
    # else:
    #    return a * b

get_body(ex_3)
    # pass

答案 1 :(得分:0)

如果您知道自己的代码将始终具有非空的docstring(如上),则可以使用内置的inspect库来做到这一点。

下面的代码片段应使您熟悉如何查看函数的源代码主体。

def hello_world():
   """Python port of original Fortran code"""
   print("hello_world")

import inspect
source = inspect.getsource(hello_world)
doc = hello_world.__doc__
code = source.split(doc)
body = code[1]
print(body)

编辑:

我对此进行了更多考虑,您可以先删除定义行,然后删除文档字符串(如果存在)

def hello_world():
   """Python port of original Fortran code"""
   print("hello_world")

import inspect
source = inspect.getsource(hello_world)
doc = hello_world.__doc__
code = source.split(':',1)
body= code[1].replace(doc, "")
body = body.replace('""""""',"")  
print(body)

答案 2 :(得分:0)

您也可以使用此

i = inspect.getsource(fun_name).index('\n')
j = inspect.getsource(fun_name).rindex(':',0,i)

print(inspect.getsource(fun_name)[j+1:])

答案 3 :(得分:0)

非常感谢启发性的答案!我尝试进一步,找到了一个使用Python ast工具的解决方案。

import inspect, ast
from astunparse import unparse
def mult(a,
    b): # comment
    """
    This is a 'doctstring', "hello"
    """
    if not a is None or b is None:
        # border case
        return None
    else:
        return a * b


def get_body(f, doc_string=False):
    """
    Get the body text of a function, i.e. without the signature.
    NOTE: Comments are stripped.
    """
    COMMENT_START = ("'", '"')
    code = inspect.getsource(mult)
    # print("Function's code:\n", code)
    module_tree = ast.parse(code) # this creates a module
    # print("Dump:\n", ast.dump(module_tree))
    # the first element of the module is evidently the function:
    function_body = module_tree.body[0].body
    # strip the code lines to remove the additional lines:
    lines = [unparse(code_line).strip() for code_line in function_body]
    # for i, line in enumerate(lines): print("%s: %s" % (i, line.strip()))
    # in principle the notion of body contains the docstring:
    if not doc_string:
        lines = (line for line in lines if not line.startswith(COMMENT_START))
    return '\n'.join(lines).strip()


s =get_body(mult)
print("---------")
print(s)

这是结果:

$ python3 function_body.py
---------
if ((not (a is None)) or (b is None)):
    return None
else:
    return (a * b)

您可以选择是否要使用文档字符串。这种方法的缺点(在我的用例中不应该是缺点)是删除注释。

我还留下了一些带有注释的印刷声明,以防有人想探索各个步骤。