假设您有一个可以返回某个对象的函数或者没有:
def foobar(arg):
if arg == 'foo':
return None
else:
return 'bar'
现在你调用这个方法,你想对这个对象做一些事情,对于这个例子,我得到一个str
,所以我可能想调用upper()
函数。
现在有两种情况可能发生,第二种情况会失败,因为None没有方法upper()
foobar('sdfsdgf').upper()
foobar('foo').upper()
当然现在很容易解决这个问题:
tmp = foobar('foo')
if tmp is not None:
tmp.upper()
# or
try:
foobar('foo').upper()
except ArgumentError:
pass
# or
caller = lambda x: x.upper() if type(x) is str else None
caller(foobar('foo'))
但是异常处理可能不够具体,并且可能发生我捕获可能很重要的异常(=调试变得更难),而第一种方法是好的但可以导致更大的代码而第三种选择看起来相当很好,但你必须为所有可能的功能做到这一点,所以方法一可能是最好的。
我想知道是否有任何语法糖可以很好地处理这个问题?
答案 0 :(得分:13)
您可以使用:
(foobar('foo') or '').upper()
括号内的表达式返回一个字符串,即使foobar()
返回一个假值。
此 导致None
被空字符串替换。如果您的代码依赖None
保留,那么您的最佳选择仍然是使用单独的if
语句仅在返回值不是.upper()
时才调用None
。
答案 1 :(得分:5)
由于这个问题得到了解答,有几个新的发展使这种模式更容易。
pymaybe包允许您使用maybe
简单地包装值,以静默传播None
值:
from pymaybe import maybe
maybe(foobar('foo')).upper()
这与Martijn Pieters的答案非常相似,只是它保留None
值而不是用空字符串替换它们,并且在存在多个falsey值时它会更健壮。
此外,看起来PEP 505处的标准跟踪草案提案似乎添加了类似于C#语言本身的?.
访问者的语法。我不确定它有多少支持,但它是一个寻找可能进一步增强的地方。
答案 2 :(得分:0)
另一种方法 - 可以包装第一个/第三个选项:
def forward_none(func):
def wrapper(arg):
return None if arg is None else func(arg)
return wrapper
请记住,方法不必用作方法 - 它们仍然是类的属性,当在类中查找时,它们是简单的函数:
forward_none(str.upper)(foobar('foo'))
我们也可以将它用作装饰者:
@forward_none
def do_interesting_things(value):
# code that assumes value is not None...
do_interesting_things(None) # ignores the original code and evaluates to None
do_interesting_things("something") # as before decoration
虽然在实践中,如果必须的话,我可能会做Martijn建议的事情。而且我会努力不去;进入这种情况是一种代码气味,表明我们应该提出异常,而不是首先返回None
。 :)