我想获得您关于用python以类型提示表示以下函数的最python方式的建议:
我希望将函数公开为接受输入参数并返回输出的库的一部分。输入参数的合同应该是:
我的函数可以迭代它,reference)
一个例子可能是一个函数,它接受一个URL序列,然后向这些URL发出请求,可能会使用一些重试逻辑,因此我必须多次迭代原始序列。但我的问题比这个例子更笼统。
乍一看,一个合适的签名是:
from typing import Iterable
def do_sth(input: Iterable[str]) -> SomeResult:
...
然而,这违反了第三个需求,因为在python中,不能保证您可以多次迭代一个Iterable,例如,因为迭代器和生成器本身就是可迭代的。
另一种尝试可能是:
from typing import Sequence
def do_sth(input: Sequence[str]) -> SomeResult:
...
但是,Sequence
契约超出了我的函数所需的范围,因为它包括索引访问和长度知识。
我想到的一个解决方案是使用Iterable
签名,然后在内部复制输入。但是,如果源序列很大,这似乎会带来潜在的内存问题。
对此是否有解决方案,即python是否知道Iterable
的概念,即每次返回一个新的迭代器?
发布于 2022-03-29 13:11:10
我可以想出两种自然的方法来表达这一点。
首先是使用Iterable[str]
,并在文档中提到,不应该使用Iterator
和Generator
对象,因为您可能有多个对__iter__
的调用。Iterable
的全部要点是,您可以在它上获得一个迭代器,而且可以说,首先使Iterator
支持Iterable
是一个错误。它不是完美的,但很简单,这通常更多的是“琵琶”,而不是一个技术上更正确的注释,这是非常复杂的。
您可以添加一些运行时检查,这些检查将提醒用户,如果他们传递了错误的东西,就会出现问题:
iter1 = iter(input)
for item in iter1:
do_something(item)
iter2 = iter(input)
if iter2 is iter1:
raise ValueError("Must pass an iterable that can be iterated multiple times. Got {input}.")
或者检查是否有Iterator,并使用内存惩罚来处理它:
if isinstance(input, Iterator):
input = list(input) # or itertools.tee or whatever
warn("This may eat up a lot of memory")
另一种选择是使用io.TextIOBase
。这可以通过寻求开始重复多次。这取决于您的用例,可能不是很合适。如果输入在概念上是字符序列上的某种块视图,则io流是一个很好的匹配,即使迭代器在技术上不返回文本行。如果它在概念上是一个不连续的字符串序列,那么流就不是一个很好的匹配。
发布于 2022-11-14 16:17:50
您可以使用不接受输入并返回可迭代的函数。在输入提示方面,您将使用Callable
。
在文档中,如果您不熟悉Callable
需要特定签名的回调函数的
框架可以使用
Callable[[Arg1Type, Arg2Type], ReturnType]
进行类型提示。
解决方案:
from typing import Callable, Iterable
def do_sth(get_input: Callable[[], Iterable[str]]) -> SomeResult:
# ...
pass
def main():
do_sth(lambda : (str(i) for i in range(10)))
我的函数可以迭代它
def do_sth(get_input: Callable[[], Iterable[str]]) -> SomeResult:
for item in get_input():
pass
如果我的函数维护对输入的引用(例如,通过返回保存该引用的对象),这是可以的。
为什么不呢。
def do_sth(get_input: Callable[[], Iterable[str]]) -> SomeResult:
return dict(reference=get_input)
可以多次迭代输入
def do_sth(get_input: Callable[[], Iterable[str]]) -> SomeResult:
for i in range(10**82):
for item in get_input():
pass
https://stackoverflow.com/questions/63104689
复制