You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使`(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")

关于假设的验证

你的假设完全正确:

  1. 在Python类型系统中,bytes注解会被类型检查器视为兼容bytearray和memoryview(二者均实现了缓冲协议),因此参数会被推断为bytes | bytearray | memoryview | str。
  2. isinstance(x, bytes)仅对bytes实例返回True,bytearray和memoryview不会被匹配,这是Python运行时的类型检查逻辑。

Pylance的行为符合PEP 484规范,该规范提到了缓冲协议类型的兼容处理;Python官方文档也明确说明memoryview、bytearray与bytes在IO等场景下可互换,但三者的类型属性存在差异(比如memoryview无decode方法)。


内容的提问来源于stack exchange,提问作者LeGEC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:10:54