使用inspect.getsourcelines
函数,我能够获得Python函数的源代码,如下所示:
import inspect
def some_decorator(x):
return x
@some_decorator
def foo():
print("bar")
print(inspect.getsourcelines(foo)[0])
此代码将以列表的形式正确输出函数的源代码行:
['@some_decorator\n', 'def foo():\n', ' print("bar")\n']
但是,我只希望代码在函数内,而不是整个函数声明。所以我只想要这个输出(还要注意正确的缩进):
['print("bar")\n']
我尝试使用切片和strip
来删除前两行,然后删除缩进,但这不适用于许多函数,我不得不相信有更好的方法。< / p>
inspect
模块或我可以pip install
的其他模块是否具有此功能?
答案 0 :(得分:2)
你可以发现你想要的代码之前都是空白的,所以你可以 试试这个
Path
答案 1 :(得分:2)
您可以这样做:
import inspect
from itertools import dropwhile
def get_function_body(func):
source_lines = inspect.getsourcelines(func)[0]
source_lines = dropwhile(lambda x: x.startswith('@'), source_lines)
def_line = next(source_lines).strip()
if def_line.startswith('def ') and def_line.endswith(':'):
# Handle functions that are not one-liners
first_line = next(source_lines)
# Find the indentation of the first line
indentation = len(first_line) - len(first_line.lstrip())
return ''.join([first_line[indentation:]] + [line[indentation:] for line in source_lines])
else:
# Handle single line functions
return def_line.rsplit(':')[-1].strip()
<强>演示:强>
def some_decorator(x):
return x
@some_decorator
def foo():
print("bar")
def func():
def inner(a, b='a:b'):
print (100)
a = c + d
print ('woof!')
def inner_inner():
print (200)
print ('spam!')
return inner
def func_one_liner(): print (200); print (a, b, c)
print (get_function_body(foo))
print (get_function_body(func()))
print (get_function_body(func_one_liner))
func_one_liner = some_decorator(func_one_liner)
print (get_function_body(func_one_liner))
<强>输出:强>
print("bar")
print (100)
a = c + d
print ('woof!')
def inner_inner():
print (200)
print ('spam!')
print (200); print (a, b, c)
print (200); print (a, b, c)
<强>更新强>
要处理async
,多行参数签名get_function_body
的函数应更新为:
import inspect
import re
from itertools import dropwhile
def get_function_body(func):
print()
print("{func.__name__}'s body:".format(func=func))
source_lines = inspect.getsourcelines(func)[0]
source_lines = dropwhile(lambda x: x.startswith('@'), source_lines)
source = ''.join(source_lines)
pattern = re.compile(r'(async\s+)?def\s+\w+\s*\(.*?\)\s*:\s*(.*)', flags=re.S)
lines = pattern.search(source).group(2).splitlines()
if len(lines) == 1:
return lines[0]
else:
indentation = len(lines[1]) - len(lines[1].lstrip())
return '\n'.join([lines[0]] + [line[indentation:] for line in lines[1:]])
<强>演示:强>
def some_decorator(x):
return x
@some_decorator
def foo():
print("bar")
def func():
def inner(a, b='a:b'):
print (100)
a = c + d
print ('woof!')
def inner_inner():
print (200)
print ('spam!')
return inner
def func_one_liner(): print (200); print (a, b, c)
async def async_func_one_liner(): print (200); print (a, b, c)
def multi_line_1(
a=10,
b=100): print (100); print (200)
def multi_line_2(
a=10,
b=100
): print (100); print (200)
def multi_line_3(
a=10,
b=100
):
print (100 + '\n')
print (200)
async def multi_line_4(
a=10,
b=100
):
print (100 + '\n')
print (200)
async def multi_line_5(
a=10,
b=100
): print (100); print (200)
def func_annotate(
a: 'x', b: 5 + 6, c: list
) -> max(2, 9): print (100); print (200)
print (get_function_body(foo))
print (get_function_body(func()))
print (get_function_body(func_one_liner))
print (get_function_body(async_func_one_liner))
func_one_liner = some_decorator(func_one_liner)
print (get_function_body(func_one_liner))
@some_decorator
@some_decorator
def foo():
print("bar")
print (get_function_body(foo))
print (get_function_body(multi_line_1))
print (get_function_body(multi_line_2))
print (get_function_body(multi_line_3))
print (get_function_body(multi_line_4))
print (get_function_body(multi_line_5))
print (get_function_body(func_annotate))
<强>输出:强>
foo's body:
print("bar")
inner's body:
print (100)
a = c + d
print ('woof!')
def inner_inner():
print (200)
print ('spam!')
func_one_liner's body:
print (200); print (a, b, c)
async_func_one_liner's body:
print (200); print (a, b, c)
func_one_liner's body:
print (200); print (a, b, c)
foo's body:
print("bar")
multi_line_1's body:
print (100); print (200)
multi_line_2's body:
print (100); print (200)
multi_line_3's body:
print (100 + '\n')
print (200)
multi_line_4's body:
print (100 + '\n')
print (200)
multi_line_5's body:
print (100); print (200)
func_annotate's body:
print (100); print (200)
答案 2 :(得分:1)
使用re
来处理def
和async def
:
def_regexp = r"^(\s*)(?:async\s+)?def foobar\s*?\:"
def get_func_code(func):
lines = inspect.getsourcelines(foo)[0]
for idx in range(len(lines)): # in py2.X, use range
def_match = re.match(line, def_regexp)
if def_match:
withespace_len = len(def_match.group(1)) # detect leading whitespace
return [sline[whitespace_len:] for sline in lines[idx+1:]]
请注意,这将不处理单行定义。在def和包含冒号后,需要匹配开始和结束括号(以避免元组和类型提示。)
原始版本:
只需查找包含def
语句的第一行。
def get_func_code(func):
lines = inspect.getsourcelines(foo)[0]
for idx in range(len(lines)): # in py2.X, use range
if line.lstrip().startswith('def %s' % func.__name__) or\
line.lstrip().startswith('async def %s' % func.__name__): # actually should check for `r"^async\s+def\s+%s" % func.__name__` via re
withespace_len = len(line.split('def'), 1)[0] # detect leading whitespace
return [sline[whitespace_len:] for sline in lines[idx+1:]]
这应该可以安全地处理制表符和空格缩进,即使在混合情况下也是如此。
答案 3 :(得分:1)
如果定义占用多于一行和注释,则接受的答案中的两个解决方案会中断(因为注释会引入额外的“:”)。在下面的内容中,我也会处理这种情况(但不会处理接受的答案的第二个功能中包含的异步情况。
import inspect
from itertools import dropwhile
def get_function_body(func):
source_lines = inspect.getsourcelines(func)[0]
source_lines = dropwhile(lambda x: x.startswith('@'), source_lines)
line = next(source_lines).strip()
if not line.startswith('def '):
return line.rsplit(':')[-1].strip()
elif not line.endswith(':'):
for line in source_lines:
line = line.strip()
if line.endswith(':'):
break
# Handle functions that are not one-liners
first_line = next(source_lines)
# Find the indentation of the first line
indentation = len(first_line) - len(first_line.lstrip())
return ''.join([first_line[indentation:]] + [line[indentation:] for line in source_lines])
例如,应用于:
# A pre comment
def f(a, b: str, c='hello',
d: float=0.0, *args, **kwargs) -> str:
"""The docs"""
return f"{c} {b}: {a + d}"
print(get_function_body(f))
我明白了
"""The docs"""
return f"{c} {b}: {a + d}"
答案 4 :(得分:0)
我喜欢@Daoctor的方法,并进行了一些改进:
以下是功能(经过测试):
def get_function_body(func):
"""
Get the body of a function
"""
def indentation(s):
"Get the indentation (spaces of a line)"
return len(s) - len(s.lstrip())
source = inspect.getsourcelines(func)[0]
# print(source)
# get the indentation of the first line
line_0 = source[0]
ind_0 = indentation(line_0)
body = []
for line in source[1:]:
ind = indentation(line)
if ind > ind_0:
# append to the body (minus the extra indentation)
body.append(line[ind_0:])
return ''.join(body)