Python 静态类型 hint/check Iterable[AnyStr] 与 Iterable[str] 之间的不匹配 |可迭代[字节]
Python static type hint/check mismatch between Iterable[AnyStr] vs Iterable[str] | Iterable[bytes]
我 运行 遇到这个静态类型提示不匹配(与 Pyright):
from __future__ import annotations
from typing import AnyStr, Iterable
def foo(i: Iterable[AnyStr]):
return i
def bar(i: Iterable[str] | Iterable[bytes]):
return i
def baz(i: Iterable[str | bytes]):
return i
def main():
s = ['a']
# makes sense to me
baz(foo(s)) # allowed
foo(baz(s)) # not allowed
# makes sense to me
baz(bar(s)) # allowed
bar(baz(s)) # not allowed
bar(foo(s)) # allowed
foo(bar(s)) # nope -- why?
Iterable[AnyStr]
和Iterable[str] | Iterable[bytes]
有什么区别?
他们不应该是“等价的”吗? (除了 AnyStr
引用上下文中的单个一致类型)
更具体地说:输入提示的正确方法是什么?
import random
from typing import Iterable, AnyStr
def foo(i: Iterable[AnyStr]):
return i
def exclusive_bytes_or_str(): # type-inferred to be Iterator[bytes] | Iterator[str]
if random.randrange(2) == 0:
return iter([b'bytes'])
else:
return iter(['str'])
foo(iter([b'bytes'])) # fine
foo(iter(['str'])) # fine
foo(exclusive_bytes_or_str()) # same error
意译answer from erictraut@github:
This isn't really the intended use for a constrained TypeVar. I recommend using an @overload
instead:
@overload
def foo(i: Iterable[str]) -> Iterable[str]: ...
@overload
def foo(i: Iterable[bytes]) -> Iterable[bytes]: ...
def foo(i: Iterable[AnyStr]) -> Iterable[AnyStr]:
return i
因为:
The type Iterable[str] | Iterable[bytes] is not assignable to type Iterable[AnyStr]. A constrained type variable needs to be matched against one of its contraints, not multiple constraints. When a type variable is "solved", it needs to be replaced by another (typically concrete) type. If foo(bar(s)) were allowed, what type would the AnyType@foo type variable resolve to? If it were resolved to type str | bytes, then the concrete return type of foo would be Iterable[str | bytes]. That's clearly wrong.
我 运行 遇到这个静态类型提示不匹配(与 Pyright):
from __future__ import annotations
from typing import AnyStr, Iterable
def foo(i: Iterable[AnyStr]):
return i
def bar(i: Iterable[str] | Iterable[bytes]):
return i
def baz(i: Iterable[str | bytes]):
return i
def main():
s = ['a']
# makes sense to me
baz(foo(s)) # allowed
foo(baz(s)) # not allowed
# makes sense to me
baz(bar(s)) # allowed
bar(baz(s)) # not allowed
bar(foo(s)) # allowed
foo(bar(s)) # nope -- why?
Iterable[AnyStr]
和Iterable[str] | Iterable[bytes]
有什么区别?
他们不应该是“等价的”吗? (除了 AnyStr
引用上下文中的单个一致类型)
更具体地说:输入提示的正确方法是什么?
import random
from typing import Iterable, AnyStr
def foo(i: Iterable[AnyStr]):
return i
def exclusive_bytes_or_str(): # type-inferred to be Iterator[bytes] | Iterator[str]
if random.randrange(2) == 0:
return iter([b'bytes'])
else:
return iter(['str'])
foo(iter([b'bytes'])) # fine
foo(iter(['str'])) # fine
foo(exclusive_bytes_or_str()) # same error
意译answer from erictraut@github:
This isn't really the intended use for a constrained TypeVar. I recommend using an
@overload
instead:@overload def foo(i: Iterable[str]) -> Iterable[str]: ... @overload def foo(i: Iterable[bytes]) -> Iterable[bytes]: ... def foo(i: Iterable[AnyStr]) -> Iterable[AnyStr]: return i
因为:
The type Iterable[str] | Iterable[bytes] is not assignable to type Iterable[AnyStr]. A constrained type variable needs to be matched against one of its contraints, not multiple constraints. When a type variable is "solved", it needs to be replaced by another (typically concrete) type. If foo(bar(s)) were allowed, what type would the AnyType@foo type variable resolve to? If it were resolved to type str | bytes, then the concrete return type of foo would be Iterable[str | bytes]. That's clearly wrong.