在python中觉得
#python #todayilearned #typehints

什么是迭代?

typehints在
中是introduced Python自Python 3.5。作为本引言的一部分,一个新的模块typing
引入了内置集合和抽象
的各种类型的引入 基础类。 Iterable是一种在PEP中突出用于类型提示的类型,并由许多最常用的Python集合实现。由于Python 3.9,因此可以从collections.abc导入此类型,并且可以用作一种类型以及抽象的基类。

什么时候使用迭代?

您开始使用Python中的类型提示,并一直遵循最佳实践来接受参数中最通用的类​​型,那么您可能一直在使用Iterable。可以在您的代码期望实现__iter__的任何地方使用Iterable,换句话说,您需要通过某些类型的集合来迭代。

说您想将电子邮件发送给多个接收器:

class MailApi:
    def sendmail(self, sender: str, receiver: str, message: str) -> None:
        ...


api = MailApi()


def send_emails(message: str, sender: str, receivers: Iterable[str]) -> Optional[T]:
    if not receivers:
        raise ValueError("you need atleast one receiver")

    for receiver in receivers:
        api.sendmail(sender, receiver, message)

如果您使用以下代码发送电子邮件,您将发送电子邮件:

send_emails(
    "Hello",
    "me@example.com",
    ["steve@example.com", "larry@example.com", "elon@example.com"],
)

如果您使用以下代码,则会得到例外:

send_emails("Hello", "me@example.com", [])

陷阱

但是如果您这样做怎么办?

send_emails(
    "Hello",
    "me@example.com",
    filter(
        lambda x: not x.endswith("example.com"),
        ["steve@example.com", "larry@example.com", "elon@example.com"],
    ),
)

您会直观地期待一个例外,因为过滤器将删除所有匹配的电子邮件,但是什么也不会发生!

filter返回有效的Iterable,Mypy正确地断言没有类型错误。发生了什么?

pythonic方式,要检查集合是否为空的是使用if not <collection_name。之所以起作用,是因为Python返回False__len__返回0。但是,Iterable不需要实现__len____bool__。默认的真实值是True,因此,即使您的代码完全键入安全,它也不会做您可能期望的事情。解决此问题的解决方案并不简单,但是您可以通过使用较小的通用类型(例如Collection)来防止类似的错误,如果您想使用类型的检查器捕获这些错误,则确实需要__len__实现。

结论

新的Python类型提示是对语言的欢迎补充,并提高可维护性,减少错误并为Python提供更好的编辑器支持。但是,由于历史设计,有细微的案例可能会导致您无法从键入安全代码的方式导致错误。如果您在python中使用Iterable,请考虑使用Collection