跳到内容

return-in-generator (B901)

源自 flake8-bugbear linter。

此规则不稳定且处于预览状态。使用需要 --preview 标志。

作用

检查同时包含 yieldyield from 语句的函数中是否有 return {value} 语句。

为什么这不好?

在生成器函数中使用 return {value} 在 Python 2 中是语法上无效的。在 Python 3 中,return {value} *可以*在生成器中使用;但是,yieldreturn 的组合可能会导致令人困惑的行为,因为 return 语句将导致生成器引发带有提供的值的 StopIteration,而不是将值返回给调用者。

例如,给定

from collections.abc import Iterable
from pathlib import Path


def get_file_paths(file_types: Iterable[str] | None = None) -> Iterable[Path]:
    dir_path = Path(".")
    if file_types is None:
        return dir_path.glob("*")

    for file_type in file_types:
        yield from dir_path.glob(f"*.{file_type}")

读者可能会认为 get_file_paths() 将返回目录中 Path 对象的可迭代对象;但实际上,list(get_file_paths()) 的计算结果为 [],因为 return 语句导致生成器引发带有值 dir_path.glob("*")StopIteration

>>> list(get_file_paths(file_types=["cfg", "toml"]))
[PosixPath('setup.cfg'), PosixPath('pyproject.toml')]
>>> list(get_file_paths())
[]

对于生成器中故意使用 return 的情况,请考虑抑制此诊断。

示例

from collections.abc import Iterable
from pathlib import Path


def get_file_paths(file_types: Iterable[str] | None = None) -> Iterable[Path]:
    dir_path = Path(".")
    if file_types is None:
        return dir_path.glob("*")

    for file_type in file_types:
        yield from dir_path.glob(f"*.{file_type}")

建议改为

from collections.abc import Iterable
from pathlib import Path


def get_file_paths(file_types: Iterable[str] | None = None) -> Iterable[Path]:
    dir_path = Path(".")
    if file_types is None:
        yield from dir_path.glob("*")
    else:
        for file_type in file_types:
            yield from dir_path.glob(f"*.{file_type}")