python输入函数表达式_如何在Python中获取函数的调用表达式?

出于好奇,这是我的最终工作代码,用于非生产性目的.乐趣无处不在! (几乎)

我不会立即将此标记为已接受的答案,希望有人能在不久的将来为我们提供更好的选择.

它按预期提取整个调用表达式.此代码假定调用表达式是裸函数调用,没有任何魔术,特殊技巧或嵌套/递归调用.这些特殊情况显然会使检测部分变得不那么琐碎,并且无论如何都是不合时宜的.

详细地讲,我使用当前函数名称来帮助定位调用表达式的AST节点,以及inspect作为起点提供的行号.

我无法使用inspect.getsource()来隔离调用者的块,因为我发现它返回了不完整的源代码,所以本来可以对其进行优化.例如,当调用者的代码直接位于main的作用域中时.不知道它应该是错误还是功能…

有了源代码后,我们只需输入ast.parse()即可获取AST根节点,然后遍历树以查找对当前函数的最新调用,瞧!

#!/usr/bin/env python3

import inspect

import ast

def print_callexp(*args, **kwargs):

def _find_caller_node(root_node, func_name, last_lineno):

# init search state

found_node = None

lineno = 0

def _luke_astwalker(parent):

nonlocal found_node

nonlocal lineno

for child in ast.iter_child_nodes(parent):

# break if we passed the last line

if hasattr(child, "lineno"):

lineno = child.lineno

if lineno > last_lineno:

break

# is it our candidate?

if (isinstance(child, ast.Name)

and isinstance(parent, ast.Call)

and child.id == func_name):

# we have a candidate, but continue to walk the tree

# in case there's another one following. we can safely

# break here because the current node is a Name

found_node = parent

break

# walk through children nodes, if any

_luke_astwalker(child)

# dig recursively to find caller's node

_luke_astwalker(root_node)

return found_node

# get some info from 'inspect'

frame = inspect.currentframe()

backf = frame.f_back

this_func_name = frame.f_code.co_name

# get the source code of caller's module

# note that we have to reload the entire module file since the

# inspect.getsource() function doesn't work in some cases (i.e.: returned

# source content was incomplete... Why?!).

# --> is inspect.getsource broken???

# source = inspect.getsource(backf.f_code)

#source = inspect.getsource(backf.f_code)

with open(backf.f_code.co_filename, "r") as f:

source = f.read()

# get the ast node of caller's module

# we don't need to use ast.increment_lineno() since we've loaded the whole

# module

ast_root = ast.parse(source, backf.f_code.co_filename)

#ast.increment_lineno(ast_root, backf.f_code.co_firstlineno - 1)

# find caller's ast node

caller_node = _find_caller_node(ast_root, this_func_name, backf.f_lineno)

# now, if caller's node has been found, we have the first line and the last

# line of the caller's source

if caller_node:

#start_index = caller_node.lineno - backf.f_code.co_firstlineno

#end_index = backf.f_lineno - backf.f_code.co_firstlineno + 1

print("Hoooray! Found it!")

start_index = caller_node.lineno - 1

end_index = backf.f_lineno

lineno = caller_node.lineno

for ln in source.splitlines()[start_index:end_index]:

print(" {:04d} {}".format(lineno, ln))

lineno += 1

def main():

a_var = "but"

print_callexp(

a_var, "why?!",

345, (1, 2, 3), hello="world")

if __name__ == "__main__":

main()

您应该得到这样的内容:

Hoooray! Found it!

0079 print_callexp(

0080 a_var, "why?!",

0081 345, (1, 2, 3), hello="world")

感觉还是有些混乱,但是太太,这是一个不寻常的目标.似乎在Python中至少足够不寻常.

例如,乍一看,我希望找到一种直接访问已经加载的AST节点的方法,该方法可以通过检查帧对象或以类似的方式进行服务,而不必手动创建新的AST节点.

请注意,我绝对不知道这是否是CPython特定的代码.它不应该.至少从我从文档中读到的内容来看.

另外,我想知道为什么ast模块(或作为side模块)没有正式的漂亮打印功能. ast.dump()可能会使用附加的indent参数完成此工作,以允许格式化输出并更轻松地调试AST.

附带说明一下,我发现这个非常小巧的function可以帮助使用AST.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值