如何正确检查 collections.abc 类是否为空?

huangapple go评论71阅读模式
英文:

How can I properly check if a collections.abc class is empty?

问题

在Python中开始时,我们都被告知这是如何检查序列(列表、元组、集合、字典等)是否为空的方式:

if not cookies:
    print("饼干怪兽需要他的饼干")

而不是这样:

if not len(cookies):
    print("这是不好的")
if cookies == []:
    print("这是错误的")

一旦我们学习了更多的Python,我们可能会了解编写代码以适应 collections.abc 接口比假设内置集合(如 listdictsettuple 等)更好。现在我们可能会认为编写像这样的函数是正确的:

def eat(cookies: Sequence[Cookie]):
    if not cookies:
        raise ValueError("饼干怪兽需要他的饼干")
    print("嗯嗯")

甚至像这样的东西,实际上是错误的,因为例如生成器始终返回 bool(gen) == True

def eat(cookies: Iterable[Cookie]):
    if not cookies:
        raise ValueError("饼干怪兽需要他的饼干")
    print("嗯嗯")

cookies = (ChocoChipCookie(), OatmealCookie(), PeanutButterCookie(), SugarCookie())

eat(c for c in cookies if isinstance(c, RasinCookie)) # 错误地打印 "嗯嗯"
eat([c for c in cookies if isinstance(c, RasinCookie)]) # 正确地引发 ValueError

我一直以为 collections.abc 中的某些接口定义了 __bool__,甚至从 __len__ 推断出它,但我今天注意到,collections.abc 的文档中甚至没有提到 bool,即使对于像 Sequence 这样最完整的接口也是如此,尽管它可以很容易地由定义了 __len__Sized 提供。

缺少 __bool__ 甚至使得 eat() 的第一个实现错误,因为不能保证 Sequence 的子类实现了 __bool__ 并使其行为符合我们的预期。

collections.abc 中检查所有不同集合的空值的正确方式是什么,如果有的话,为什么它们(至少其中一些)不定义 __bool__

英文:

When starting out in Python we're all told that this is how you check if a sequence (list, tuple, set, dict, etc.) is empty:

if not cookies:
    print("The cookie monster needs his cookies")

and not like this:

if not len(cookies):
    print("This is bad")
if cookies == []:
    print("This is just wrong")

Once we learn some more Python we might learn that it's good to write code that caters to interfaces from collections.abc instead of assuming the built-in collections like list, dict, set, tuple, etc. Now we might think it's proper to write a function like this one:

def eat(cookies: Sequence[Cookie]):
    if not cookies:
        raise ValueError("The cookie monster needs his cookies")
    print("Yum yum")

or even something like this, which is actually wrong because generators for example always give bool(gen) == True:

def eat(cookies: Iterable[Cookie]):
    if not cookies:
        raise ValueError("The cookie monster needs his cookies")
    print("Yum yum")

cookies = (ChocoChipCookie(), OatmealCookie(), PeanutButterCookie(), SugarCookie())

eat(c for c in cookies if isinstance(c, RasinCookie)) # Wrongly prints "Yum yum"
eat([c for c in cookies if isinstance(c, RasinCookie)]) # Correctly raises ValueError

I'd always assumed that some interface in collections.abc defined __bool__ or even inferred it from __len__ but I noticed today that bool is not even mentioned in collections.abc's documentation, not even for the most complete ones like Sequence even though it could easily be provided by Sized which defines __len__.

The absence of __bool__ makes even the first implementation of eat() wrong, since there's no guarantee that a subclass of Sequence implements __bool__ and has it behave like we'd expect.

What's the correct way to check emptiness of all the different collections in collections.abc, if any, and why don't they (at least some of them) define __bool__?

答案1

得分: 1

collections.abc中定义的许多类别中,检查“空值”的概念根本没有意义。特别是在您在问题中提到的示例中,即Iterable类,只有在尝试从中消耗项目时才能检查它是否为空 - 如果它最终为空,那太好了,但如果不是,那么糟糕,仅仅尝试检查是否为空将不可逆地改变可迭代/迭代器/生成器的状态。这就是为什么__bool__方法只在有意义的类中实现的原因。

英文:

The idea of checking for "emptiness" simply does not make sense for many of the classes defined in collections.abc. In particular, the example you give in the question, the Iterable class, can only be checked for emptiness when an actual attempt to consume an item from it is made--if it turns out to be empty, great, but if it is not, then oops, too bad, the mere attempt to check for emptiness will have irreversibly altered the state of the iterable/iterator/generator. This is why the __bool__ method is implemented only in classes where it makes sense.

答案2

得分: 0

我意识到,在玩弄一些自定义集合并查看更多文档后,bool()文档涵盖了这一点:

默认情况下,除非其类定义了一个返回False__bool__()方法或者在调用对象时返回零的__len__()方法,否则对象被认为为真。

英文:

After playing around with some custom collections and looking at some more documentation, I realised that the bool() documentation covers this:

> By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero, when called with the object.

huangapple
  • 本文由 发表于 2023年6月15日 16:55:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76480792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定