首页 > 解决方案 > 条件返回值——但使用 `yield from` 而不是 `return`

问题描述

在 Python 中,经常使用这种流控制编写小函数:

def get_iterstuff(source: Thingy, descr: Optional[str]) -> Iterable[Stuff]:
    """ Return an iterator of Stuff instances from a given Thingy """
    key = prepare_descr(descr)        # may be a “str”, may be None
    val = getattr(source, key, None)  # val has type Optional[Iterable[Stuff]]
    if not val:                       # None, or (in some cases) an empty
        return IterStuff.EMPTY        # sentinel, with the same type as val
    return val                        # the likeliest codepath

… 一个if语句可以提前返回——在一些这样的函数中,早于任何准备函数调用,就像这里示例的前两行中的那些。

这工作正常。return我的意思是,我相信在具有多个语句的函数中会出现一些与窥视孔优化相关的并发症†</sup>。但总的来说,就像许多其他 Pythoneers 一样,我一直在做这种事情,以生成可测试、可维护和清晰易读的流控制函数,这些函数可以在不引起任何大的愤怒的情况下运行。

但是所以,我的问题是关于这种逻辑采用稍微不同的形式的方式,例如,当函数是使用一个或多个yield from语句的生成器时:

def iterstuff(source: Thingy, descr: Optional[str]) -> YieldFrom[Stuff]: # ‡
    key = prepare_descr(descr)          # may be a “str”, may be None
    val = getattr(source, key, None)    # val has type Optional[Iterable[Stuff]]
    if not val:
        yield from tuple()              # … or suchlike
    else:
        yield from val                  # the likeliest codepath

......在这个版本中,最重要的收获是:

  1. 第二条yield from语句构成函数最可能的代码路径的结尾,位于else子句中,而不是函数代码块的顶层。
  2. tuple(…)构造函数对yield from空的和可迭代的东西有这种奇怪的用法。

......现在,所以,我确实知道,由于控制失效声明用尽后else的事实(在其他情况下,我喜欢使用和滥用这种品质),因此必须使用 。把东西塞进去比,从第一个功能开始,为了炮制那种哨兵所需要的所有跑腿工作要容易得多。yieldyield fromtuple(…)IterStuff.EMPTY

但是该yield from示例确实比return基于它的对应示例更笨拙。更脆弱。考虑较少。如果你愿意的话,代码很臭。

所以我问:yield from构建这个版本的最清晰、最不重要、最优化的 Pythonic 方式是什么?

那个tuple()代码疣可以吗,还是有更少的编程气味替代品?

是否存在更好的方法来进行这样的流量控制?他们中的任何一个(或我的任何例子)是否都背负着有问题的时间复杂性?


† –(即它们很难窥视孔优化;我不知道——编译器设计比我的工资等级高出很多)

‡ – “YieldFrom” 泛型类型别名简化了对这些生成器函数的注释——typing.Generator正如所写的那样,有点过头了。它看起来像这样:

class YieldFrom(Generator[T_co, None, None]):
    """ Simple “typing.Generator” alias. The generic Generator
        from “typing” requires three type params:

            • “yield_type” (covariant),
            • “send_type” (contravariant), and
            • “return_type” (covariant).

        … a function containing 1..n “yield” or “yield from”
        statements – without returning anything and unburdened
        by any expectations of calls to its “send(…)” method
        showing up in any type-festooned code – can make use
        of this “YieldFrom[T]” alias. ÷Explict beats implict÷
    """
    pass

标签: pythonpython-3.xyieldflow-controlmultiple-return-values

解决方案


推荐阅读