如何使`(bytes | str) -> str`函数通过类型检查器?附假设验证
问题:编写能通过类型检查的
from_utf8函数 我编写了带类型注解的函数:
def from_utf8(string: bytes | str) -> str: if isinstance(string, bytes): return string.decode("utf-8") else: return string # <- 此处有类型警告
Pylance在return string行给出类型警告:
Type "bytearray | memoryview[_I@memoryview] | str" is not assignable to return type "str" Type "bytearray | memoryview[_I@memoryview] | str" is not assignable to type "str" "bytearray" is not assignable to "str"
我的理解是:类型注解x: bytes实际对应运行时的bytes | bytearray | memoryview[_I@memoryview]类型集合,但isinstance(x, bytes)仅检查bytes类型,不包含另外两种。
我尝试调换类型检查顺序:
def from_utf8(string: bytes | str) -> str: if isinstance(string, str): return string else: return string.decode("utf-8") # <- memoryview无decode属性
此时错误变为:
Cannot access attribute "decode" for class "memoryview[_I@memoryview]" Attribute "decode" is unknown
上下文信息:
- 项目使用Python 3.11
- 在VSCode中使用Pylance 2025.2.1和Python扩展2025.0.0
请问是否有便捷方式编写能通过类型检查的from_utf8(string)函数?另外我的上述假设是否正确,有没有相关文档说明?
解决方案
方法1:明确处理所有字节兼容类型
直接在参数注解中列出所有兼容类型,并分别处理:
def from_utf8(string: bytes | bytearray | memoryview | str) -> str: if isinstance(string, str): return string elif isinstance(string, memoryview): return string.tobytes().decode("utf-8") else: # 匹配bytes或bytearray return string.decode("utf-8")
方法2:利用str()的通用转换
Python的str()构造函数支持传入所有缓冲协议类型,并通过encoding参数解码,类型检查器能识别该用法:
def from_utf8(string: bytes | str) -> str: if isinstance(string, str): return string return str(string, encoding="utf-8")
这种方式对bytes、bytearray、memoryview均有效,无需额外类型判断。
方法3:用类型守卫缩小类型范围
如果不想修改参数注解,可通过类型守卫告诉检查器分支的具体类型:
from typing import TypeGuard def is_str(s: bytes | str) -> TypeGuard[str]: return isinstance(s, str) def is_bytes_or_bytearray(s: bytes | str) -> TypeGuard[bytes | bytearray]: return isinstance(s, (bytes, bytearray)) def from_utf8(string: bytes | str) -> str: if is_str(string): return string elif is_bytes_or_bytearray(string): return string.decode("utf-8") else: # 处理memoryview,转换为bytes后解码 return bytes(string).decode("utf-8")
关于假设的验证
你的假设完全正确:
- 在Python类型系统中,
bytes注解会被类型检查器视为兼容bytearray和memoryview(二者均实现了缓冲协议),因此参数会被推断为bytes | bytearray | memoryview | str。 isinstance(x, bytes)仅对bytes实例返回True,bytearray和memoryview不会被匹配,这是Python运行时的类型检查逻辑。
Pylance的行为符合PEP 484规范,该规范提到了缓冲协议类型的兼容处理;Python官方文档也明确说明memoryview、bytearray与bytes在IO等场景下可互换,但三者的类型属性存在差异(比如memoryview无decode方法)。
内容的提问来源于stack exchange,提问作者LeGEC
相关产品推荐
相关产品推荐

