如何解决dict.get(None) 在具有 None 值的 dict 上采用默认 arg
我试图概括这种情况:
def func(t):
# code which doesn't accept None's
def foo(a):
if a in ('',None):
return None
return func(a)
def foo_b(a):
if a in ('',None):
return None
elif a == 'b'
return 0
return func(a)
我试图解决它的方法是定义一个字典:
defaults = {None: None,'': None}
def foo(a):
return defaults.get(a,func(a))
def foo_b(a):
defaults_b = {'bar': 0,**defaults}
return defaults_b.get(a,func(a))
运行脚本时:
foo_b(None)
它进入 get
方法,
从 None
字典取回 defaults
并使用 func()
进入 None
方法
我得到错误原因 func()
方法不排除 None
的
用python解决这个问题的最佳实践是什么?
解决方法
这行不通,因为函数的所有参数在调用函数之前都已计算。所以当你写
defaults.get(a,func(a))
它首先计算 a
和 func(a)
,并将这两个结果传递给 defaults.get()
。由于 func(a)
不适用于某些 a
值,因此在进入 defaults.get()
之前您会收到错误消息。
解决这个问题的一般方法是默认为一个函数,只有在必要时才会调用。您需要编写自己的 dict.get()
版本,以这种方式工作。
def dict_get_f(d,key,default_fun):
if key in d:
return d[key]
return default_fun()
然后你可以像这样使用它:
def foo(a):
return dict_get_f(defaults,a,lambda: func(a))
,
为什么不直接检查函数本身?
def func(t):
if t is None:
return None
# go on with the code
,
问题
正如@Barmar 在他们的回答中指出的那样,问题不在于 dict.get
的行为,而是在调用函数之前评估所有参数的事实,因此 defaults.get(a,func(a))
调用 {{1} } 在使用该值调用 func(a)
之前。
解决方案 1:defaults.get
解决此问题的另一种方法是使用 collections.defaultdict
,它提供了一种在值不在字典中时调用函数的方法。
因为你的字典很小,复制它们的成本很低,所以这个解决方案可以做到这一点。
collections.defaultdict
解决方案 2:无处不在的函数!
您可以采用的另一种方法是将 from collections import defaultdict
defaults = {None: None,'': None}
def func(arg):
"""Some function that cannot be called with None"""
if arg is None:
raise ValueError()
print(f'func called with {arg}')
def foo(a):
return defaultdict(lambda: func(a),defaults)[a]
def foo_b(a):
defaults_b = {'bar': 0,**defaults}
return defaultdict(lambda: func(a),defaults_b)[a]
if __name__ == '__main__':
print(foo(None))
print(foo(''))
print(foo(123))
print(foo(123))
print(foo_b(None))
print(foo_b('bar'))
字典设为函数字典,并使用默认值 defaults
,以避免使用 lambda: func(a)
调用函数。因此,None
将返回一个返回正确值的函数,然后您可以调用该函数来获取您想要的值:defaults.get(a,lambda: func(a))
。
完整的程序:
defaults.get(a,lambda: func(a))()
解决方案 3:浮华且有些混淆的布尔运算短路
作为第三种选择,您可以在 python 中使用 defaults = {None: (lambda: None),'': (lambda: None)}
def func(arg):
"""Some function that cannot be called with None"""
if arg is None:
raise ValueError()
print(f'func called with {arg}')
def foo(a):
return defaults.get(a,lambda: func(a))()
def foo_b(a):
defaults_b = {'bar': (lambda: 0),**defaults}
return defaults_b.get(a,lambda: func(a))()
if __name__ == '__main__':
print(foo(None))
print(foo(''))
print(foo(123))
print(foo(123))
print(foo_b(None))
print(foo_b('bar'))
的行为,根据您列出的所有默认值都是“falsy”(What is Truthy and Falsy? How is it different from True and False?)这一事实进行回复。在python中,and
是短路的,这意味着如果a and b
是a
或falsy,那么它已经知道False
不能是a and b
,所以它返回 True
,或者如果 a
为真,则返回 a
。
因此,如果 b
为 a
,则 None
等于 defaults.get(a,True) and func(a)
。 None and func(a)
是假的,所以 None
等于 None and func(a)
。如果 None
不在 a
中,则 defaults
等于 defaults.get(a,True) and func(a)
,它等于 True and func(a)
,因此函数被调用。
请注意,如果您使用真实的默认值,例如,这将不起作用。 if func(a)
,对于可维护性来说可能很难理解 - 我只是想我会给出另一个选择。
defaults = {None: None,'': None,'bar': 'something'}