如何链接可能在Python中返回None的属性查找?

Dav*_*ull 18 python

我的问题是一般的问题,当一个中间问题可能会返回时如何链接一系列属性查找None,但是由于我遇到了试图使用Beautiful Soup的问题,我将在那个环境中问它.

Beautiful Soup解析HTML文档并返回一个对象,该对象可用于访问该文档的结构化内容.例如,如果解析的文档在变量中soup,我可以获得其标题:

title = soup.head.title.string
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果文档没有标题,则soup.head.title返回None并且后续string查找会引发异常.我可以打破链条:

x = soup.head
x = x.title if x else None
title = x.string if x else None
Run Code Online (Sandbox Code Playgroud)

但在我看来,这是冗长而难以阅读的.

我可以写:

title = soup.head and soup.head.title and soup.title.head.string
Run Code Online (Sandbox Code Playgroud)

但这是冗长而低效的.

如果想到的话,我认为可能的一个解决方案是创建一个nil可以返回None任何属性查找的对象(调用它).这将允许我写:

title = ((soup.head or nil).title or nil).string
Run Code Online (Sandbox Code Playgroud)

但这很难看.有没有更好的办法?

jef*_*upp 12

最直接的方法是包装try... except块.

try:
    title = soup.head.title.string
except AttributeError:
    print "Title doesn't exist!"
Run Code Online (Sandbox Code Playgroud)

在删除每个测试时,确实没有理由在每个级别进行测试会在失败的情况下引发相同的异常.我会在Python中考虑这个惯用语.

  • 请注意,Mypy 会抱怨:`“Optional[Head]”的项“None”没有属性“head”`。 (2认同)

mgi*_*son 8

您可能可以使用reduce此:

>>> class Foo(object): pass
... 
>>> a = Foo()
>>> a.foo = Foo()
>>> a.foo.bar = Foo()
>>> a.foo.bar.baz = Foo()
>>> a.foo.bar.baz.qux = Foo()
>>> 
>>> reduce(lambda x,y:getattr(x,y,''),['foo','bar','baz','qux'],a)
<__main__.Foo object at 0xec2f0>
>>> reduce(lambda x,y:getattr(x,y,''),['foo','bar','baz','qux','quince'],a)
''
Run Code Online (Sandbox Code Playgroud)

在python3.x中,我认为这reduce是移动到functools:(


我想你也可以用一个更简单的函数做到这一点:

def attr_getter(item,attributes)
    for a in attributes:
        try:
            item = getattr(item,a)
        except AttributeError:
            return None #or whatever on error
    return item
Run Code Online (Sandbox Code Playgroud)

最后,我想最好的方法是这样的:

try:
   title = foo.bar.baz.qux
except AttributeError:
   title = None
Run Code Online (Sandbox Code Playgroud)

  • 我发现这个解决方案比问题中提出的“详细”解决方案丑陋得多。 (3认同)
  • 从 2.6 开始,`reduce` 可以作为 `functools.reduce` 使用 - 所以导入可能不会有太大的伤害...... (2认同)