如何检查某行是否是 python 中的有效函数调用?
How do I check if a line is a valid function call in python?
背景:我目前正在为 ipython 创建一个线条魔术。此魔法仅适用于将函数的 return 值分配给变量的行。
我正在寻找一种方法来确保某行是 python 中有效的函数调用 + 赋值。
例如应接受以下内容:
a = b()
a,b = c(d,e="f")
a = b(c()+c)
以下内容将被拒绝:
a = def fun() # no function call
b(a=2) # no assignment
a = b + c # no function call
a = b() + c() # top-level on right-hand-side must be function call
如果该行根本无效 python,我不关心它是否通过,因为这将在另一个阶段处理。
我想出的最好的:
- 应用此正则表达式
[A-z, ]*= *[A-z_]* *\(.*\)
- 在第一个“=”处拆分
- 确保括号是平衡的,但如果在最后一个括号之前括号计数为零则失败
需要第 3 步,才能使这个案例失败:
a = b() + c() # top-level on right-hand-side must be function call
第 3 步也有点像这样:
def matched(str):
count = 0
for i in str.strip():
if i == "(":
count += 1
elif i == ")":
count -= 1
if count < 0 or (count < 1 and i>=len(str)-1):
return False
return count == 0
(基于)
这个解决方案非常丑陋,因为如果顶层没有函数调用,我不知道如何很好地失败。
更好的解决方案可能是使用 python 的 AST,但我不知道如何访问它。
您可以使用 Python 自己的解析器(可通过 ast
模块访问)来直接检查每个语句,看看它是否是右侧为调用的赋值。
import ast
def is_call_assignment(line):
try:
node = ast.parse(line)
except SyntaxError:
return False
if not isinstance(node, ast.Module):
return False
if len(node.body) != 1 or not isinstance(node.body[0], ast.Assign):
return False
statement = node.body[0]
return isinstance(statement.value, ast.Call)
test_cases = [
'a = b()',
'a,b = c(d,e="f")',
'a = b(c()+c)',
'a = def fun()',
'b(a=2)',
'a = b + c',
'a = b() + c()'
]
for line in test_cases:
print(line)
print(is_call_assignment(line))
print("")
结果:
a = b()
True
a,b = c(d,e="f")
True
a = b(c()+c)
True
a = def fun()
False
b(a=2)
False
a = b + c
False
a = b() + c()
False
背景:我目前正在为 ipython 创建一个线条魔术。此魔法仅适用于将函数的 return 值分配给变量的行。
我正在寻找一种方法来确保某行是 python 中有效的函数调用 + 赋值。
例如应接受以下内容:
a = b()
a,b = c(d,e="f")
a = b(c()+c)
以下内容将被拒绝:
a = def fun() # no function call
b(a=2) # no assignment
a = b + c # no function call
a = b() + c() # top-level on right-hand-side must be function call
如果该行根本无效 python,我不关心它是否通过,因为这将在另一个阶段处理。
我想出的最好的:
- 应用此正则表达式
[A-z, ]*= *[A-z_]* *\(.*\)
- 在第一个“=”处拆分
- 确保括号是平衡的,但如果在最后一个括号之前括号计数为零则失败
需要第 3 步,才能使这个案例失败:
a = b() + c() # top-level on right-hand-side must be function call
第 3 步也有点像这样:
def matched(str):
count = 0
for i in str.strip():
if i == "(":
count += 1
elif i == ")":
count -= 1
if count < 0 or (count < 1 and i>=len(str)-1):
return False
return count == 0
(基于
这个解决方案非常丑陋,因为如果顶层没有函数调用,我不知道如何很好地失败。
更好的解决方案可能是使用 python 的 AST,但我不知道如何访问它。
您可以使用 Python 自己的解析器(可通过 ast
模块访问)来直接检查每个语句,看看它是否是右侧为调用的赋值。
import ast
def is_call_assignment(line):
try:
node = ast.parse(line)
except SyntaxError:
return False
if not isinstance(node, ast.Module):
return False
if len(node.body) != 1 or not isinstance(node.body[0], ast.Assign):
return False
statement = node.body[0]
return isinstance(statement.value, ast.Call)
test_cases = [
'a = b()',
'a,b = c(d,e="f")',
'a = b(c()+c)',
'a = def fun()',
'b(a=2)',
'a = b + c',
'a = b() + c()'
]
for line in test_cases:
print(line)
print(is_call_assignment(line))
print("")
结果:
a = b()
True
a,b = c(d,e="f")
True
a = b(c()+c)
True
a = def fun()
False
b(a=2)
False
a = b + c
False
a = b() + c()
False