【问题标题】:Python short-circuiting method coalesce chainPython短路方法合并链
【发布时间】:2018-02-13 20:59:47
【问题描述】:

我经常看到这种模式以容错方式浏览字典:

a.get('k1', {}).get('k2', {}).get('k3', '')

但是,如果中间对象是类实例而不是字典,则这是不可能的。是否有一种简洁的方法来执行(类似)以下操作:

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 中,这将是不可行的。

【问题讨论】:

  • 您可能对我对另一个类似问题here 的回答感兴趣。虽然它使用自定义字典实现。
  • @Skam 也许,但在这种情况下,查询不能是键字符串的简单可迭代;它必须是 lambdas 的可迭代对象。
  • 你需要一个“也许”的单子

标签: python python-3.x nonetype


【解决方案1】:

最接近的等价物是使用带有默认值的getattr

>>> class A: pass
...
>>> a = A()
>>> getattr(getattr(getattr(a, 'k1', A()), 'k2', A()), 'k3', A())
<__main__.A object at 0x104a7de10>

但是,上面的这个结构对我来说有很大的代码味道......为什么你不能依赖一个具有属性的对象?

虽然,如果您正在链接 方法调用,那么您可能需要一个返回一些方便的默认值的默认可调用对象,例如:

>>> 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 的方式。

【讨论】:

  • 并不是不能依赖一个有属性的对象;就是对象方法的返回值往往是None。
  • @Reinderien 像 Node 类与 left_child,例如?
  • 是的,实际上是来自 BeautifulSoup 的一个非常相似的应用程序
【解决方案2】:

使用 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。此外,它非常强大,因为您可以进行大量字符串操作来获得您想要的内容。我也觉得这符合“请求宽恕”的诡计。

【讨论】:

  • 有些方法比其他方法需要更多的宽恕......:P
  • eval 在这种情况下的目的是什么?
猜你喜欢
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 2018-04-26
  • 2021-12-09
相关资源
最近更新 更多