如何验证函数是否包含return
关键字?我经常忘记返回行,所以我担心我的包的用户在提供基于函数的输入时也会忘记。
def appler():
a = "apple"
# `return` is missing
def bananer():
b = "banana"
return b
我可以解析函数的实际代码字符串,最后一行包含"return"
,但这不是很健壮(可能是由注释触发的(。
def validate_funk(funk):
if condition_to_check_that_it_contains_rtrn:
pass
else:
raise ValueError(f"Yikes - The function you provided not contain a `return` statement:nn{funk}")
>>> validate_funk(appler)
#triggers ValueError
>>> validate_funk(bananer)
# passes
EDIT:理想情况下不运行函数。
您真正关心的可能不是返回语句本身,而是函数返回某种类型的东西。这可以通过使用类型提示(PEP484(最容易地实现:
def appler() -> str:
a = "apple"
# `return` is missing
def bananer() -> str:
b = "banana"
return b
现在,运行像mypy或Pyre(或许多其他(这样的静态分析工具:将发出关于函数appler
中错误返回类型的警告(预期为str
,实际为NoneType
(。
寻找萨比克的答案,以获得更一般的答案。编写(单元(测试是另一种可以解决更多问题的好做法,如果做得好,这是对代码可维护性的投资。
没有return语句的函数默认返回None
。
>>> def abc():
pass
>>> print(abc())
None
>>>
您可以使用以下选项添加支票:
def validate_func(function):
if function() == None:
raise ValueError("Yikes - Does not contain a `return` statement")
不过也有一些缺点。
- 您必须执行函数
- 如果您在函数中使用
return
和None
,它将不起作用
不太实用,但是的,这是一种方法。您还可以获得类中的局部函数列表或方法列表,并在它们之间循环,而不必单独检查每个函数。
对于所问的问题,ast
模块将允许您进行检查。
然而,它本身似乎并不是很有用——正如其他人所指出的,没有return
的函数是有效的(它返回None
(,仅仅因为函数有return
并不意味着它返回正确的值,甚至任何值(return
可能在if
语句中(。
有两种标准的处理方法:
-
单元测试-单独的代码,使用各种输入组合(可能只有一个,可能有数百个(调用函数,并检查答案是否与手动计算的答案匹配,或者是否满足要求。
-
检查CCD_ 15语句的思想的更一般的实现是";皮棉";,在Python
pylint
的情况下;它会查看您的代码,并检查各种看起来可能是错误的模式。一个附带的好处是它已经存在,它检查了几十种常见的模式。 -
另一个不同的更通用的实现是
mypy
类型检查器;它不仅检查是否有return
语句,而且还检查它是否返回了正确的类型,如函数头中所注释的那样。
通常;选通干线";发展过程;禁止手动更改main
版本,并且只有通过测试、lint和/或mypy的更改才能被接受为main
版本。
return
语句可能只存在于条件中,因此,需要传递特定的输入才能执行return
语句。这也不是return
存在的良好指标,因为它可能是return
None
,导致更大的模糊性。相反,可以使用inspect
和ast
模块:
测试功能:
def appler():
a = "apple"
# `return` is missing
def bananer():
b = "banana"
return b
def deeper_test(val, val1):
if val and val1:
if val+val1 == 10:
return
def gen_func(v):
for i in v:
if isinstance(i, list):
yield from gen_func(i)
else:
yield i
inspect.getsource
将函数的整个源作为字符串返回,然后可以将其传递给ast.parse
。从那里,可以递归遍历语法树,搜索return
语句的存在:
import inspect, ast
fs = [appler, bananer, deeper_test, gen_func]
def has_return(f_obj):
return isinstance(f_obj, ast.Return) or
any(has_return(i) for i in getattr(f_obj, 'body', []))
result = {i.__name__:has_return(ast.parse(inspect.getsource(i))) for i in fs}
输出:
{'appler': False, 'bananer': True, 'deeper_test': True, 'gen_func': False}
具有定义的validate_funk
:
def validate_funk(f):
if not has_return(ast.parse(inspect.getsource(f))):
raise ValueError(f"function '{f.__name__}' does not contain a `return` statement")
return True
注:
- 此解决方案不需要调用测试函数
- 解决方案必须在文件中运行。如果它在shell中运行,则会引发一个
OSError
。有关该文件,请参阅此Github Gist
您可以使用装饰器简化返回检查:
def ensure_return(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
if res is None:
raise ValueError(f'{func} did not return a value')
return res
return wrapper
@ensure_return
def appler():
a = "apple"
# `return` is missing
@ensure_return
def bananer():
b = "banana"
return b
然后:
>>> appler()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in wrapper
ValueError: <function appler at 0x7f99d1a01160> did not return a value
>>> bananer()
'banana'