EAFP 和 mypy 可以共存吗?

Mic*_*iac 6 python duck-typing mypy python-typing

EAFP

请求原谅比请求许可更容易。这种常见的 Python 编码风格假设存在有效的键或属性,并在假设证明错误时捕获异常。这种干净快速的风格的特点是存在许多 try 和 except 语句。该技术与许多其他语言(例如 C)常见的 LBYL 风格形成对比。

来自 Python 术语表

米皮

mypy是什么?

Mypy 是 Python 的可选静态类型检查器。您可以向 Python 程序添加类型提示 ( PEP 484 ),并使用 mypy 对它们进行静态类型检查。甚至无需运行程序即可找到程序中的错误!

您可以在程序中混合动态和静态类型。当静态类型不方便时(例如遗留代码),您始终可以回退到动态类型。

这是有关 EAFP 的精彩 YouTube 视频: https: //youtu.be/x3v9zMX1s4s

我正在尝试使用mypy,但基本上每次我编写一些 EAFP 代码时它都会生气。

例如:

from contextlib import suppress

from typing import Optional


class MyClass:
    def __init__(self):
        self.__name: Optional[str] = None

    @property
    def name(self) -> Optional[str]:
        return self.__name

    @name.setter
    def name(self, name: str):
        self.__name = name

    @property
    def upper_name(self) -> Optional[str]:
        with suppress(AttributeError):
            return self.name.upper()  # Item "None" of "Optional[str]" has no attribute "upper" - mypy(error)
        return None
Run Code Online (Sandbox Code Playgroud)

在此示例中,我希望该upper_name属性尝试转换name为大写,但如果nameNone,它将引发一个AttributeError, 然后被 抑制suppress(AttributeError),因此函数返回None

return None我在底部有明确的说明,因为PEP 8说:

返回语句保持一致。函数中的所有 return 语句要么都应该返回表达式,要么都不应该返回。如果任何 return 语句返回一个表达式,则任何不返回值的 return 语句都应显式地将其声明为 return None,并且显式 return 语句应出现在函数末尾(如果可到达)。

我已将此作为问题 (#9467) 提交给mypyrepo,但它几乎立即被关闭。

我确实认为有可能理解mypyEAFP。

例如,如果它看到一个 type Optional[something],并且它看到一个表达式,SomethingError如果它接收None到 ,则该表达式会引发 a ,但如果它接收到 ,则成功something,并且该表达式包含在可以处理 的tryor块中,那么它可以假设它是一些 EAFP 代码,不要惊慌。suppressSomethingError

但现在的情况是,为了不让上面的例子吓坏,我必须将其更改为:

from typing import Optional


class MyClass:
    def __init__(self):
        self.__name: Optional[str] = None

    @property
    def name(self) -> Optional[str]:
        return self.__name

    @name.setter
    def name(self, name: str):
        self.__name = name

    @property
    def upper_name(self) -> Optional[str]:
        if self.name is not None:
            return self.name.upper()  # No error here this time
        else:
            return None
Run Code Online (Sandbox Code Playgroud)

另一个例子:

假设我可以有一个house可以有一个garage可以有一个car有一个brand,这样的东西可以让我得到它brand

from contextlib import suppress

from my_module import house

car_brand = None
try:
    car = house.garage.car
except AttributeError:
    pass
else:
    car_brand = car.brand
Run Code Online (Sandbox Code Playgroud)

如果houseNone,或者如果是garageNone或者如果是carNone就会car_brand留下来None。但如果有 ahousegaragea car,它会尝试获取它的brand。如果car没有brand,则该错误将不会被抑制,因为 acar必须有brand

为了避免mypy错误,另一种方法是:

from my_module import house

car_brand = None
if house is not None:
    garage = house.garage
    if garage is not None:
        car = garage.car
        if car is not None:
            car_brand = car.brand
Run Code Online (Sandbox Code Playgroud)

我认为这不是非常可读或干净。

我认为有效的另一点是避免竞争条件。如果我首先读取一个值来检查它是否有效,然后再次读取它以使用它(如果它有效),我相信没有什么可以保证我第二次读取它时会收到与第一次相同的值。

使用“EAFP”方式进行操作时,我只读取一次值,因此读取后值是否发生巨大变化并不重要。

我的例子并不完美,但我认为它们在传达想法方面起到了作用。

所以我的问题是,如何使用 EAFP 风格并仍然使用mypy?我是否可以给出一个标志或参数,mypy这样它就不会因为 EAFP 的存在而被吓坏?或者是否有一种“更好”的 EAFP 做事方式,不会让人mypy悲伤?

  • Python 3.8.6
  • 米皮 0.782