为什么迭代器开始引发StopIteration而不是我的自定义异常



我有一个迭代器,它可能会引发自定义异常。

def raising_iter():
yield 123
while True:
raise ValueError("My custom exception")

it = raising_iter()
# prints 123
print(next(it))
try:
next(it)
except ValueError as e:
print(e)
# raises StopIteration
next(it)

在引发该异常之后,对next()的后续调用会引发StopIteration,尽管迭代器尚未耗尽,而且以前也没有引发Stop迭代。规则似乎是这样的:;如果迭代器引发了任何东西,它将在随后的调用上引发StopIteration"。

第一个问题为什么?医生没有提到这种行为。他们只说

一旦迭代器的next((方法引发StopIteration,它必须在以后的通话中继续这样做。没有服从这一财产即视为违约。

第二个问题如何使迭代器引发我的自定义异常?其基本原理是向客户传达迭代停止的确切原因。一个解决方法是定义我自己的迭代器协议。它只需要有一个方法——next(),但不会有不希望的行为。不过,这是有问题的,因为我需要重写代码的其他部分,这些部分需要一个正常的迭代器。

我知道这会破坏LSP,因此这是对迭代器的滥用(尽管,我没想到Python会做出如此严厉的惩罚(。也许,我的用例需要一些不同的解决方案?

使用Python 3.7 进行测试

我想出了一个不错的解决方案。我将生成一个特殊的包装器,里面可以有值,也可以有异常,而不是生成值和引发异常。

import abc
from typing import TypeVar, Generic, Optional, Iterator
T = TypeVar("T")

class Wrapped(Generic[T]):
def __init__(
self,
value: Optional[T] = None,
exception: Optional[BaseException] = None
):
self._value = value
self._exception = exception
if value is not None and exception is not None:
raise ValueError()
def unwrap(self) -> T:
if self._exception:
raise self._exception
return self._value
@classmethod
def with_value(cls, value: T) -> 'Wrapped[T]':
return cls(value=value)
@classmethod
def with_exception(cls, exception: BaseException) -> 'Wrapped':
return cls(exception=exception)

class WrappedItemsIterator(Iterator[Wrapped[T]], Generic[T]):
def __iter__(self):
return self
def __next__(self) -> Wrapped[T]:
try:
value = self._real_next()
except BaseException as e:
return Wrapped.with_exception(e)
else:
return Wrapped.with_value(value)
@abc.abstractmethod
def _real_next(self) -> Wrapped[T]:
raise NotImplementedError()

class MyIterator(WrappedItemsIterator):
def _real_next(self) -> Wrapped[T]:
raise ValueError("Custom exception")

it = MyIterator()
value = next(it).unwrap()

当您调用unwrap()时,它将返回一个值或引发一个异常。

最新更新