如何在Python中链接可能返回None的属性查找?
我遇到的问题是,如何在一系列属性查找中连接起来,尤其是当其中一个中间的查找可能返回None
的时候。因为我是在使用Beautiful Soup时碰到这个问题,所以我想在这个上下文中提问。
Beautiful Soup是一个用来解析HTML文档的工具,它会返回一个对象,这个对象可以用来访问文档中结构化的内容。比如,如果解析后的文档存储在变量soup
中,我可以用下面的方式获取它的标题:
title = soup.head.title.string
我的问题是,如果文档没有标题,那么soup.head.title
就会返回None
,接下来再查找string
就会出错。我可以把这个链条拆开成:
x = soup.head
x = x.title if x else None
title = x.string if x else None
但在我看来,这样写显得冗长且难以阅读。
我可以这样写:
title = soup.head and soup.head.title and soup.title.head.string
但这样也显得冗长且效率低下。
我想到的一个解决方案是创建一个对象(叫它nil
),这个对象在查找任何属性时都会返回None
。这样我就可以写:
title = ((soup.head or nil).title or nil).string
但这样看起来很丑陋。有没有更好的方法呢?
7 个回答
3
我正在使用 Python 3.9
Python 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)]
而且 and
这个关键词解决了我的问题
memo[v] = short_combo and short_combo.copy()
根据我的理解,这种做法不太符合 Python 的风格,应该处理异常情况。
不过在我的解决方案中,函数内部存在 None
的模糊性,在这种情况下,我觉得处理大约 50% 的异常情况并不是一个好习惯。
在函数外部调用它的时候,我会处理这些异常。
24
最简单的方法就是把代码放在一个 try
...except
块里。
try:
title = soup.head.title.string
except AttributeError:
print "Title doesn't exist!"
其实没有必要在每个层级都进行测试,因为如果去掉每个测试,失败时都会抛出相同的错误。我觉得这样做在Python中是很常见的写法。
19
你可以试试用 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)
''
在 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
最后,我觉得最好的方法可能是这样的:
try:
title = foo.bar.baz.qux
except AttributeError:
title = None