【问题标题】:Type-hinting parameters with a sentinel value as the default使用标记值作为默认值的类型提示参数
【发布时间】:2021-09-19 00:16:37
【问题描述】:

当我无法在函数签名中分配默认参数和/或None 已经有意义时,我目前使用this strategy

from typing import Optional

DEFAULT = object()


# `None` already has meaning.
def spam(ham: Optional[list[str]] = DEFAULT):
    if ham is DEFAULT:
        ham = ['prosciutto', 'jamon']
    if ham is None:
        print('Eggs?')
    else:
        print(str(len(ham)) + ' ham(s).')

错误:

Failed (exit code: 1) (2607 ms)

main.py:7: error: Incompatible default for argument "ham" (default has type "object", argument has type "Optional[List[str]]")
Found 1 error in 1 file (checked 1 source file)
  • 如何在 mypy 中输入提示 ham 而不会出现错误?
  • 我应该使用什么策略来代替DEFAULT = object()

【问题讨论】:

  • 我最近一直在做的事情是:DEFAULT = (DefaultType := types.new_class("DefaultType"))(),然后使用Union[List[str], DefaultType] 输入提示。
  • 这是 Python 语言开发中的一个活跃话题,PEP 661 提议将新的哨兵创建者添加到标准库中。该 PEP 尚未被接受,因此它可能会或可能不会成为一件事,但您可能同时借用其中一个建议的实现供您自己使用。我特别热衷于向make sentinels types that are their own metatype (a mind-bending idea that greatly simplifies type annotations) 提出一个不太严肃的建议。
  • 我很欣赏你的两位 cmets。虽然我仍在审查它们,但我认为它们直接解决了这个问题。知道这个话题还没有解决,我感到很欣慰。

标签: python type-hinting mypy python-typing python-3.9


【解决方案1】:

正如我所评论的,这是 Python 中一个活跃的开发领域。 PEP 661 建议添加一个 sentinel 函数来创建一个哨兵对象,但在该 PEP 获得批准之前,您只能靠自己。

不过,您可以从 PEP 中的一些建议(或拒绝)选项中获得灵感。一种非常简单的方法可以很好地与类型提示配合使用,即使您的哨兵值成为一个类:

class DEFAULT: pass

现在您可以将您的函数类型提示为采用包括 type[DEFAULT] 在内的类型的联合:

def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT):

【讨论】:

    【解决方案2】:

    我喜欢做的事情——这只是 @Blckknght's answer 的一个细微变化——是使用一个元类给我的哨兵类一个更好的 repr 并让它永远是假的。


    sentinel.py

    from typing import Literal 
    
    class SentinelMeta(type):
        def __repr__(cls) -> str:
            return f'<{cls.__name__}>'
    
        def __bool__(cls) -> Literal[False]:
            return False
    
    
    class Sentinel(metaclass=SentinelMeta): pass
    

    ma​​in.py

    from sentinel import Sentinel
    
    class DEFAULT(Sentinel): pass
    

    您在类型提示中使用它的方式与@Blckknght 建议的方式完全相同:

    def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT): ...
    

    但您还有一个额外的优势,即您的哨兵值总是虚假的并且具有更好的代表:

    >>> DEFAULT
    <DEFAULT>
    >>> bool(DEFAULT)
    False
    

    【讨论】:

    • 让哨兵总是虚伪的动力是什么? (谢谢。)
    • @zatg98n4qwsb8heo 与 Or Shahaf 在这里给出的基本相同的推理——哨兵通常代表一个“空”值,所以总是虚假的感觉就像在 imo 中是有道理的。 discuss.python.org/t/pep-661-sentinel-values/9126/…
    • 我早些时候看到了这一点,这似乎是一种改进,但我不是对此发表意见的人。
    • 对于只能阅读此答案的任何人:考虑查看此helpful comment
    • @zatg98n4qwsb8heo 意见可能不同!我认为有充分的理由更喜欢 Blckknght 的解决方案(保持简单,愚蠢!)。如果问题有明显的解决方案,那么就不会讨论标准库中可能的新模块。只是想我会把它扔掉,以防万一它有帮助:)
    猜你喜欢
    • 2022-06-30
    • 2012-01-21
    • 2012-08-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    相关资源
    最近更新 更多