给Python的NoneType增加方法

2 投票
6 回答
1093 浏览
提问于 2025-04-17 21:43

我正在使用BeautifulSoup进行网页爬虫,想要把多个查找操作连在一起,比如:

soup.find('div', class_="class1").find('div', class_="class2").find('div', class_="class3")

当然,如果找不到某个div,这个链式调用就会出错,抛出一个

AttributeError: 'NoneType' object has no attribute 'find'

有没有办法修改NoneType,让它也能有一个像这样的查找方法

class NoneType:
    def find(*args):
        return None

这样我就可以做类似这样的操作

thing = soup.find('div', class_="class1").find('div', class_="class2").find('div', class_="class3")
if thing:
    do more stuff

而不是

thing1 = soup.find('div', class_="class1")
if thing1:
    thing2 = thing1.find('div', class_="class2")
    if thing2:
        thing3 = thing2.find('div', class_="class3")
        etc.

我觉得我可以通过使用支持XPath的解析器来实现类似的功能,但这个问题并不局限于这个用例,更是关于如何修改或重写内置类。

6 个回答

0

你不能从None继承:

>>> class Noneish(type(None)):
...   pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type 'NoneType' is not an acceptable base type
1

你不能修改这个类,真正的问题是你为什么会想要这么做?NoneType表示这里没有数据,所以当你在这种类型上尝试使用 .find() 方法时,即使它存在,你也只会得到空值或者没有任何值。我建议你可以试试下面的这个方法。

try:
    var = soup.find('div', class_="class1").find('div', class_="class2").find('div', class_="class3")
except AttributeError:
    do something else instead or message saying there was no div
1

一种方法可能是先准备一个

class FindCaller(object):
    def __init__(self, *a, **k):
        self.a = a
        self.k = k
    def __call__(self, obj):
        return obj.find(*self.a, **self.k)

def callchain(root, *fcs):
    for fc in fcs:
        root = fc(root)
        if root is None: return
    return root

然后再执行

thing = callchain(soup,
    FindCaller('div', class_="class1"),
    FindCaller('div', class_="class2"),
    FindCaller('div', class_="class3"),
)
2

为什么不直接用try/except语句呢?因为你不能修改NoneType的内容。

try:
    thing = soup.find('div', class_="class1").find('div', class_="class2").find('div', class_="class3")
    do more stuff
except AttributeError:
    thing = None  # if you need to do more with thing
1

你不能修改像 NoneTypestr 这样的内置类:

>>> nt = type(None)
>>> nt.bla = 23
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'NoneType'

对于其中一些类(比如 str),你可以通过继承来扩展:

>>> class bla(str):
...      def toto(self): return 1
>>> bla('2123').toto()
1

但是对于 NoneType,这是不可能的。而且这样做也没有任何帮助:

>>> class myNoneType(nt):
...      def find(self): return 1
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    type 'NoneType' is not an acceptable base type

撰写回答