这是使用 Protocol 的正确方法吗?如果这是 mypy 失败的原因?

问题描述 投票:0回答:1

我有以下两个课程:

@runtime_checkable
class AbstractFolder(Protocol):
    def __iter__(self) -> "AbstractFolder":
        raise NotImplementedError

    def __next__(self) -> AbstractFileReadable:
        raise NotImplementedError

及其实施:

class FileSystemFolder(AbstractFolder, Iterator):
    def __init__(self, path: str):
        self.path: str = path

    def __iter__(self) -> "FileSystemFolder":
        self.jobs: List[AbstractFileReadable] = [
            FileSystemFileReadable(path)
            for path in [*glob(os.path.join(self.path, "*"))]
        ]
        self.current: int = -1
        return self

    def __next__(self) -> FileSystemFileReadable:
        self.current += 1

        if self.current >= len(self.jobs):
            raise StopIteration

        return self.jobs[self.current]

和以下功能

def process(folder: AbstractFolder) -> None:
...

孩子们正在返回实现的实例,这可能很多,但是当我执行 mypy 时,我得到以下错误:

error: Incompatible return value type (got "AbstractFileReadable", expected "FileSystemFileReadable")

这是实施和使用协议和打字的正确方法吗?

谢谢

python mypy
1个回答
0
投票

mypy
检测到的打字错误与您使用
Protocol
无关。相反,它是
self.jobs
的类型(在
__iter__
中定义)和
__next__
的返回值之间的冲突。

尽管代码仅将

FileSystemFileReadable
的实例加载到
self.jobs
列表中,但其类型被暗示为
List[AbstractFileReadable]
,因此从理论上讲,其他一些代码可以在不违反类型声明的情况下向其添加不同类型的对象。当您从列表中取出值时,在
__next__
中,您将返回它们,并且返回类型被注释为
FileSystemFileReadable
,但是
mypy
认为列表可以包含更广泛的抽象类型。

解决方法是更改其中一个注释。可能

self.jobs
应该被暗示为
List[FileSystemFileReadable]
。不太可能,
__next__
应该返回更宽松的
AbstractFileReadable
,就像协议一样。

另一方面,与您询问的类型提示问题无关,

__iter__
在代码中设置迭代的繁重工作可能是不好的形式。一个更好的主意是在
__init__
中进行,而
__iter__
只有
return self
作为它的整个身体。在当前的实现中,像这样的习语将无法按预期工作:

f = FileSystemFolder("some/path")

first = next(f) # get one value for special processing
rest = list(f)  # put the rest of the values in a list

使用您的代码,迭代器生成的第一个元素将与所有其他元素一起出现在

rest
中,因为
__iter__
构造函数进行的
list
调用会将迭代重置为开始。

© www.soinside.com 2019 - 2024. All rights reserved.