[英]Getting a Python function's source code without the definition lines
使用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
來刪除前兩行,然后刪除縮進,但這不適用於許多功能,我必須相信有更好的方法。
inspect
模塊或我可以pip install
其他模塊是否具有此功能?
您會發現所有想要的代碼之前都為空,因此您可以嘗試一下
print filter(lambda x:x.startswith(' '), inspect.getsourcelines(foo)[0])
您可以執行以下操作:
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)
使用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:]]
即使在混合情況下,這也應該安全地處理制表符和空格縮進。
如果定義占用多行並且使用了注釋,則接受的答案中的兩種解決方案都會中斷(因為注釋會引入額外的“:”)。 在下面的內容中,我也會注意這種情況(但是接受的答案的第二個函數中不包含異步情況)。
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}"
我喜歡@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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.