【问题标题】:Is this a bad use of a `yield` statement?这是对“yield”语句的错误使用吗?
【发布时间】:2021-09-09 18:54:34
【问题描述】:

我正在查看一位同事的代码,我觉得这是对 yield 语句的不必要使用。是这样的:

def standardize_text(text: str):
    pattern = r"ABC" # some regex
    yield re.sub(pattern, "X", text)

def preprocess_docs(docs: List[str]):
    for doc in docs:
        yield standardize_text(doc)

我了解preprocess_docsyield 的使用,以便我可以返回一个生成器,如果docs 是一个大列表,这将很有帮助。但我不明白standardize_text 函数中yield 的值。对我来说,return 声明会做同样的事情。

yield 有用吗?

【问题讨论】:

  • 我认为你是对的 - 我看不到对字符串进行一次 yield 的好处。
  • 我很好奇你为什么不直接问你的同事......(作为记录,我没有看到一个很好的理由,但我不是 Python 生成器方面的专家)跨度>
  • return 不会做同样的事情,它会使preprocess_docs 成为一个简单的值生成器,而不是(单个元素)生成器的生成器。我无法想象为什么你会想要那个,但还是有区别的。将preprocess_docs 更改为使用yield from 而不是yield 会产生相同的效果(尽管它几乎肯定会比使用普通yieldreturn 慢)。
  • 是的,没用。此外,如果你要注释你的参数,你应该注释你的返回类型。如果您的同事对该代码进行了类型注释(使用 mypy 确保注释正确),他们可能会自己意识到返回 Generator[Generator[str]] 实际上并不是他们想要做的。

标签: python yield


【解决方案1】:

对我来说,return 声明会做同样的事情。

使用returnyield 不同,如ShadowRanger's comment 中所述。

使用yield,调用函数会给你一个generator object

>>> standardize_text("ABCD")
<generator object standardize_text at 0x10561f740>

生成器可以产生多个结果(与使用return 的函数不同)。这个生成器碰巧只产生一个项目,它是一个字符串(re.sub 的结果)。例如,您可以将生成器的结果收集到 list() 中,或者直接使用 next() 获取第一个结果:

>>> list(standardize_text("ABCD"))
['XD']

>>> g = standardize_text("ABCD")
>>> next(g)
'XD'
>>> next(g) # raises StopIteration, indicating the generator has finished

如果我们把函数改成使用return:

def standardize_text(text: str):
    pattern = r"ABC" # some regex
    return re.sub(pattern, "X", text)

然后调用该函数只会给我们单个结果——不需要list()next()

>>> standardize_text("ABCD")
'XD'

yield 有用吗?

standardize_text 函数中,不,不是。但是您的preprocess_docs 函数实际上确实利用yield 返回多个值:它为docs 中的每个值返回一个带有一个结果的生成器。这些结果要么是生成器本身(在您的原始代码中使用yield),要么是字符串(如果我们将standardize_text 更改为使用return)。

def preprocess_docs(docs: List[str]):
    for doc in docs:
        yield standardize_text(doc)

# returns a generator because the implementation uses "yield"
>>> preprocess_docs(["ABCD", "AAABC"])
<generator object preprocess_docs at 0x10561f820>

# with standardize_text using "yield re.sub..."
>>> for x in preprocess_docs(["ABCD", "AAABC"]): print(x)
... 
<generator object standardize_text at 0x1056cce40>
<generator object standardize_text at 0x1056cceb0>


# with standardize_text using "return re.sub..."
>>> for x in preprocess_docs(["ABCD", "AAABC"]): print(x)
... 
XD
AAX

注意:在 Python 3 的 async/await 之前,一些并发库使用 yield 的方式与现在使用 await 的方式相同。例如,Twisted's @inlineCallbacks。我认为这与您的问题没有直接关系,但为了完整起见,我将其包括在内。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    • 2013-02-09
    • 2013-05-24
    相关资源
    最近更新 更多