首页 > 解决方案 > 2 和 3 组合代码库中的 `__str__` 的类型是什么?

问题描述

我正在尝试转换这个 py2 类:

class Example(object):
    def __unicode__(self):
        return unicode(42)   # unicode(self.id)

    def __str__(self):
        return unicode(self).encode('u8')

    __repr__ = __str__

到具有以下类型的组合 2/3 版本:

import sys
from typing import Text
from builtins import str as text

class Example(object):

    def _as_unicode(self):  # type: () -> Text
        return text(42)

    __unicode__ = _as_unicode

    def _as_bytes(self):  # type: () -> bytes
        return self._as_unicode().encode('utf-8')

    def __str__(self):  # type: () -> str
        if sys.version_info.major == 2:
            return self._as_bytes()      # line 17
        else:
            return self._as_unicode()    # line 19

    __repr__ = __str__

这会产生以下错误:

c:\tmp> py -3 -m  mypy example.py
example.py:17: error: Incompatible return value type (got "bytes", expected "str")

c:\tmp> py -3 -m  mypy example.py -2
example.py:19: error: Incompatible return value type (got "unicode", expected "str")

有没有办法让 mypy 相信它的类型__str__是乙酸?(有没有更好的方法以 2 + 3 兼容的方式编写此代码?)

标签: pythonmypy

解决方案


这里的正确方法实际上不是尝试找到可以桥接两个 Python 版本的某种类型,而是让 mypy 了解您的分支将仅在某些版本的 Python 上运行。

为此,请更改sys.version_info.major == 2为如下所示的检查,sys.version_info[0] == 2例如:

import sys
from typing import Text
from builtins import str as text

class Example(object):

    def _as_unicode(self):  # type: () -> Text
        return text(42)

    def _as_bytes(self):  # type: () -> bytes
        return self._as_unicode().encode('utf-8')

    def __str__(self):  # type: () -> str
        if sys.version_info[0] == 2:
            return self._as_bytes()
        else:
            return self._as_unicode()

    __unicode__ = _as_unicode
    __repr__ = __str__

这最终完全避开了您的问题。例如,由于在 Python 2.7 模式下类型检查时“else”分支被标记为不可访问,mypy 不会尝试分析该分支,因此不会报告错误。

如果您愿意,您可以更进一步,只需围绕 的整个定义执行 if-else __str__

import sys
from typing import Text
from builtins import str as text

class Example(object):

    def _as_unicode(self):  # type: () -> Text
        return text(42)

    def _as_bytes(self):  # type: () -> bytes
        return self._as_unicode().encode('utf-8')

    if sys.version_info[0] == 2:
        __str__ = _as_bytes
    else:
        __str__ = _as_unicode

    __unicode__ = _as_unicode
    __repr__ = __str__

这是有关mypy 支持的版本和平台检查的更多信息。

mypy 没有具体理解该表单的事实sys.version_info.major可能是一个疏忽。您可以尝试在mypy 的问题跟踪器consider_sys_version_info上提交有关此问题的问题(尽管考虑到有简单的解决方法,我知道这个问题的优先级有多高),或者尝试通过修改.mypy/reachability.py


推荐阅读