对于您最初的问题:引发异常而不是返回 None。这是 Python 中的一个习惯用法,因此很容易被用户理解。例如
def get_surname(name):
if name == "Monty":
return "Python"
else:
raise KeyError(name)
你会在 Python 中经常看到这种使用模式:
try:
print(get_surname("foo"))
except KeyError:
print("Oops, no 'foo' found")
根据您的反馈,您似乎还想确保实际使用了某些返回值。这很棘手,我认为在 Python 中没有一种优雅的方式来强制执行此操作,但我会尝试一下。
首先,我们将返回值放在一个属性中,以便我们可以跟踪它是否确实已被读取。
class Result(object):
def __init__(self, value):
self._value = value
self.used = False
@property
def value(self):
self.used = True # value was read
return self._value
其次,我们要求必须在with-block 中检索Result 对象。当块退出时(__exit__),我们检查该值是否已被读取。为了处理用户不使用with-statement 的情况,我们还检查在垃圾收集时是否已读取该值(__del__)。出现的异常将转换为警告。不过我通常会回避__del__。
class RequireUsage(object):
def __init__(self, value):
self._result = Result(value)
def __enter__(self):
return self._result
def __exit__(self, type, value, traceback):
if type is None: # No exception raised
if not self._result.used:
raise RuntimeError("Result was unused")
def __del__(self):
if not self._result.used:
raise RuntimeError("Result was unused (gc)")
要使用这个系统,只需将返回值包装在RequireUsage 中,如下所示:
def div2(n):
return RequireUsage(n/2)
要使用这些函数,请将它们包装在一个块中并提取value:
with div2(10) as result:
print(result.value)
如果你不在任何地方调用result.value,你现在会得到一个异常:
with div2(10) as result:
pass # exception will be thrown
同样,如果你只调用div2 而没有with,你也会得到一个异常:
div2(10) # will also raise an exception
我不会特别推荐在实际程序中使用这种确切的方法,因为我觉得它在添加很少的情况下使使用变得更加困难。此外,还有一些未涵盖的边缘情况,例如您只需执行 res = div2(10),因为保留的引用会阻止调用 __del__。