Python 短路法合并链
Python short-circuiting method coalesce chain
我经常看到这种以容错方式在字典中导航的模式:
a.get('k1', {}).get('k2', {}).get('k3', '')
但是,如果中间对象是 class 个实例而不是字典,则这是不可能的。是否有一种简洁的方法来执行(类似)以下操作:
a?.method1()?.method2()?.method3()...
扩展到:
if a is None:
return None
obj = a.method1()
if obj is None:
return None
obj = obj.method2()
if obj is None:
return None
obj = obj.method3()
return obj
我想这可能是构建器模式的一些变体,但如果上述方法处于不受用户控制的 API 中,那将是不可行的。
最接近的等效项是使用具有默认值的 getattr
:
>>> class A: pass
...
>>> a = A()
>>> getattr(getattr(getattr(a, 'k1', A()), 'k2', A()), 'k3', A())
<__main__.A object at 0x104a7de10>
但是,上面的构造对我来说有很大的代码味道......为什么你不能依赖具有属性的对象?
尽管如此,如果您正在链接 方法调用 那么您可能需要一个默认可调用对象 returns 一些方便的默认值,例如:
>>> def empty(): return A()
...
>>> getattr(getattr(getattr(a, 'k1', empty)(), 'k2', empty)(), 'k3', empty)()
<__main__.A object at 0x104a7dda0>
>>>
如果担心简洁,可以使用 reduce
:
>>> def getter(obj, k):
... return getattr(obj, k, A())
...
>>> functools.reduce(getter, ('k1','k2','k3'), a)
<__main__.A object at 0x104a7dda0>
老实说,我会使用中间变量并显式调用方法并使用异常处理,这将是更 Pythonic 的方式。
因为使用 eval 把我活活烧死,但我偶尔喜欢在有嵌套属性的情况下使用 try...eval。
try:
val = eval("obj.{a1}.{a2}.{a3}".format(a1=a1, a2=a2, a3=a3))
except AttributeError:
val = default
print('Attribute not found')
except Exception as e:
print('Something went wrong')
这看起来比多个 getattr 的 imo 更好,我喜欢捕捉 AttributeErrors。此外,它非常强大,因为您可以进行大量字符串操作来获得您想要的内容。我也觉得这与 "ask for forgiveness" schtick 一致。
我经常看到这种以容错方式在字典中导航的模式:
a.get('k1', {}).get('k2', {}).get('k3', '')
但是,如果中间对象是 class 个实例而不是字典,则这是不可能的。是否有一种简洁的方法来执行(类似)以下操作:
a?.method1()?.method2()?.method3()...
扩展到:
if a is None:
return None
obj = a.method1()
if obj is None:
return None
obj = obj.method2()
if obj is None:
return None
obj = obj.method3()
return obj
我想这可能是构建器模式的一些变体,但如果上述方法处于不受用户控制的 API 中,那将是不可行的。
最接近的等效项是使用具有默认值的 getattr
:
>>> class A: pass
...
>>> a = A()
>>> getattr(getattr(getattr(a, 'k1', A()), 'k2', A()), 'k3', A())
<__main__.A object at 0x104a7de10>
但是,上面的构造对我来说有很大的代码味道......为什么你不能依赖具有属性的对象?
尽管如此,如果您正在链接 方法调用 那么您可能需要一个默认可调用对象 returns 一些方便的默认值,例如:
>>> def empty(): return A()
...
>>> getattr(getattr(getattr(a, 'k1', empty)(), 'k2', empty)(), 'k3', empty)()
<__main__.A object at 0x104a7dda0>
>>>
如果担心简洁,可以使用 reduce
:
>>> def getter(obj, k):
... return getattr(obj, k, A())
...
>>> functools.reduce(getter, ('k1','k2','k3'), a)
<__main__.A object at 0x104a7dda0>
老实说,我会使用中间变量并显式调用方法并使用异常处理,这将是更 Pythonic 的方式。
因为使用 eval 把我活活烧死,但我偶尔喜欢在有嵌套属性的情况下使用 try...eval。
try:
val = eval("obj.{a1}.{a2}.{a3}".format(a1=a1, a2=a2, a3=a3))
except AttributeError:
val = default
print('Attribute not found')
except Exception as e:
print('Something went wrong')
这看起来比多个 getattr 的 imo 更好,我喜欢捕捉 AttributeErrors。此外,它非常强大,因为您可以进行大量字符串操作来获得您想要的内容。我也觉得这与 "ask for forgiveness" schtick 一致。