<h2>通常,我们如何提示<em>类</em>,而不是类</em>的<em>实例</h2>
<br/>
<p>通常,如果我们想告诉类型检查器某个类的任何<em>实例</em>(或该类的子类</em>的任何<em>实例)应被接受为函数的参数,我们可以这样做:</p>
<pre><code>def accepts_int_instances(x: int) -> None:
pass
class IntSubclass(int):
pass
accepts_int_instances(42) # passes MyPy (an instance of `int`)
accepts_int_instances(IntSubclass(666)) # passes MyPy (an instance of a subclass of `int`)
accepts_int_instances(3.14) # fails MyPy (an instance of `float` — `float` is not a subclass of `int`)
</code></pre>
<blockquote>
<p><em><a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=a841287b8f05dcc6aa3ef679c65febd0&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">Try it on MyPy playground here!</a></em></p>
</blockquote>
<p>另一方面,如果我们有一个类<code>C</code>,并且我们想要提示类<code>C</code><em>本身</em>(或者<code>C</code>的子类)应该作为参数传递给函数,那么我们使用<code>type[C]</code>而不是<code>C</code>。(在Python<;=3.8中,您需要使用<code>typing.Type</code>而不是内置的<code>type</code>函数,但在Python 3.9和<a href="https://www.python.org/dev/peps/pep-0585/" rel="nofollow noreferrer">PEP 585</a>中,我们可以直接参数化<code>type</code>。)</p>
<pre><code>def accepts_int_and_subclasses(x: type[int]) -> None:
pass
class IntSubclass(int):
pass
accepts_int_and_subclasses(int) # passes MyPy
accepts_int_and_subclasses(float) # fails Mypy (not a subclass of `int`)
accepts_int_and_subclasses(IntSubclass) # passes MyPy
</code></pre>
<blockquote>
<ul>
<li><em><a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=7a3e1a9fe35b3431d7978e7cc11b3a85&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">Try it on MyPy playground here!</a></em></li>
<li><em>Read <a href="https://mypy.readthedocs.io/en/stable/kinds_of_types.html#the-type-of-class-objects" rel="nofollow noreferrer">the MyPy docs on "the type of class objects".</a></em></li>
<li><em>See also: <a href="https://stackoverflow.com/questions/46092104/subclass-in-type-hinting">Subclass in type hinting</a></em>.</li>
</ul>
</blockquote>
<hr/>
<p><strong>我们如何注释一个函数来表示某个参数应该接受<em>任何</em>数值类?</strong></p>
<p><code>int</code>、<code>float</code>和所有的<code>numpy</code>数值类型都是<code>numbers.Number</code>的子类,所以如果我们想说所有数值类都是允许的,我们应该可以只使用<code>type[Number]</code></p>
<p>至少,Python<em>说<code>float</code>和<code>int</code>是<code>Number</code>的子类:</p>
<pre><code>>>> from numbers import Number
>>> issubclass(int, Number)
True
>>> issubclass(float, Number)
True
</code></pre>
<p>如果我们使用<em>运行时</em>类型检查库,比如<a href="https://pypi.org/project/typeguard/" rel="nofollow noreferrer">typeguard</a>,那么使用<code>type[Number]</code>似乎可以很好地工作:</p>
<pre><code>>>> from typeguard import typechecked
>>> from fractions import Fraction
>>> from decimal import Decimal
>>> import numpy as np
>>>
>>> @typechecked
... def foo(bar: type[Number]) -> None:
... pass
...
>>> foo(str)
Traceback (most recent call last):
TypeError: argument "bar" must be a subclass of numbers.Number; got str instead
>>> foo(int)
>>> foo(float)
>>> foo(complex)
>>> foo(Decimal)
>>> foo(Fraction)
>>> foo(np.int64)
>>> foo(np.float32)
>>> foo(np.ulonglong)
>>> # etc.
</code></pre>
<p>但是等等!如果我们尝试将<code>type[Number]</code>与<em>静态</em>类型检查器一起使用,它似乎不起作用。如果我们通过MyPy运行以下代码段,它会为每个类(除了<code>fractions.Fraction</code>)引发一个错误:</p>
<pre><code>from numbers import Number
from fractions import Fraction
from decimal import Decimal
NumberType = type[Number]
def foo(bar: NumberType) -> None:
pass
foo(float) # fails
foo(int) # fails
foo(Fraction) # succeeds!
foo(Decimal) # fails
</code></pre>
<blockquote>
<p><em><a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=b21b1372081db2d3362f305e7bc5c364&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">Try it on MyPy playground here!</a></em></p>
</blockquote>
<p>Python肯定不会对我们撒谎说<code>float</code>和<code>int</code>是<code>Number</code>的子类。发生什么事了</p>
<br/>
<hr/>
<br/>
<h2>为什么<code>type[Number]</code>不能作为数值类的静态类型提示</h2>
<br/>
<p>虽然<code>issubclass(float, Number)</code>和<code>issubclass(int, Number)</code>都计算为<code>True</code>,但<code>float</code>和<code>int</code>实际上都不是<code>numbers.Number</code>的“严格”子类<code>numbers.Number</code>是一个抽象基类,<a href="https://github.com/python/cpython/blob/b34dd58fee707b8044beaf878962a6fa12b304dc/Lib/numbers.py#L393" rel="nofollow noreferrer">^{<cd9>}</a>和<a href="https://github.com/python/cpython/blob/b34dd58fee707b8044beaf878962a6fa12b304dc/Lib/numbers.py#L264" rel="nofollow noreferrer">^{<cd10>}</a>都注册为<code>Number</code>的“虚拟子类”。这会导致Python<em>在运行时</em>将<code>float</code>和<code>int</code>识别为<code>Number</code>的“子类”,即使<code>Number</code>不在它们的方法解析顺序中</p>
<blockquote>
<p><em>See <a href="https://stackoverflow.com/questions/2010692/what-does-mro-do">this StackOverflow question</a> for an explanation of what a class's "method resolution order", or "mro", is.</em></p>
</blockquote>
<pre><code>>>> # All classes have `object` in their mro
>>> class Foo: pass
>>> Foo.__mro__
(<class '__main__.Foo'>, <class 'object'>)
>>>
>>> # Subclasses of a class have that class in their mro
>>> class IntSubclass(int): pass
>>> IntSubclass.__mro__
(<class '__main__.IntSubclass'>, <class 'int'>, <class 'object'>)
>>> issubclass(IntSubclass, int)
True
>>>
>>> # But `Number` is not in the mro of `int`...
>>> int.__mro__
(<class 'int'>, <class 'object'>)
>>> # ...Yet `int` still pretends to be a subclass of `Number`!
>>> from numbers import Number
>>> issubclass(int, Number)
True
>>> #?!?!!??
</code></pre>
<blockquote>
<p>What's an Abstract Base Class? Why is <code>numbers.Number</code> an Abstract Base
Class? What's "virtual subclassing"?</p>
<ul>
<li><em><a href="https://docs.python.org/3/library/abc.html" rel="nofollow noreferrer">The docs for Abstract Base Classes ("ABCs") are here.</a></em></li>
<li><em><a href="https://www.python.org/dev/peps/pep-3119/" rel="nofollow noreferrer">PEP 3119, introducing Abstract Base Classes, is here.</a></em></li>
<li><em><a href="https://docs.python.org/3/library/numbers.html#module-numbers" rel="nofollow noreferrer">The docs for the <code>numbers</code> module are here.</a></em></li>
<li><em><a href="https://www.python.org/dev/peps/pep-3141/" rel="nofollow noreferrer">PEP 3141, which introduced <code>numbers.Number</code>, is here.</a></em></li>
<li><em>I can recommend <a href="https://www.youtube.com/watch?v=S_ipdVNSFlo" rel="nofollow noreferrer">this talk by Raymond Hettinger, which has a detailed explanation of ABCs and the purposes of virtual subclassing.</a></em></li>
</ul>
</blockquote>
<p>问题是{a5}(和,{a6})</p>
<p>MyPy确实了解标准库中的一些ABC。例如,<a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=1b7dabd701d6c3db13b5d6489ad13c08&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">MyPy knows that ^{<cd38>} is a subtype of ^{<cd39>}</a>,尽管<code>MutableSequence</code>是ABC,但是<a href="https://github.com/python/cpython/blob/2f205920127bd93eebed044cb1b61834764478ba/Lib/_collections_abc.py#L1162" rel="nofollow noreferrer">^{<cd38>} is only a virtual subclass of ^{<cd40>}.</a>只有</em>知道<code>list</code>是<code>MutableSequence</code><em>的一个亚型,因为我们一直在对MyPy</em>撒谎关于^{<cd38>的方法解析顺序</p>
<p>MyPy与所有其他主要类型检查器一起,使用<a href="https://github.com/python/typeshed" rel="nofollow noreferrer">typeshed repository</a>中的存根对标准库中的类和模块进行静态分析。如果你看一下<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L746" rel="nofollow noreferrer">the stub for ^{<cd38>} in typeshed</a>,你会发现<code>list</code>是<code>collections.abc.MutableSequence</code>的一个直接子类。那根本不是真的-<a href="https://github.com/python/cpython/blob/9626ac8b7421aa5fc04a30359521d24f40105141/Lib/_collections_abc.py#L1047" rel="nofollow noreferrer">^{<cd40>} is written in pure Python,</a>而<a href="https://github.com/python/cpython/blob/main/Objects/listobject.c" rel="nofollow noreferrer">^{<cd38>} is an optimised data structure written in C</a>。但是对于静态分析,MyPy认为这是真的是有用的。标准库中的其他集合类(例如,<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L714" rel="nofollow noreferrer">^{<cd51>}</a>、<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L854" rel="nofollow noreferrer">^{<cd52>}</a>和<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L806" rel="nofollow noreferrer">^{<cd53>}</a>)按typeshed以大致相同的方式进行特殊大小写,但<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L175" rel="nofollow noreferrer">^{<cd9>}</a>和<a href="https://github.com/python/typeshed/blob/32bc2161a107db20c2ebc85aad31c29730db3e38/stdlib/builtins.pyi#L250" rel="nofollow noreferrer">^{<cd10>}</a>等数字类型则不是</p>
<br/>
<p><strong>如果我们在集合类方面对MyPy撒谎,为什么我们不在数值类方面也对MyPy撒谎?</strong></p>
<p>很多人(包括我!)认为我们应该这样做,关于是否应该做出这种改变的讨论已经持续了很长时间(例如,<a href="https://github.com/python/typeshed/pull/3108" rel="nofollow noreferrer">typeshed proposal</a>,<a href="https://github.com/python/mypy/issues/3186" rel="nofollow noreferrer">MyPy issue</a>)。然而,这样做有各种复杂因素</p>
<blockquote>
<p><em>Credit goes to <a href="https://stackoverflow.com/users/1126841/chepner">@chepner</a> in the <a href="https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#comment122628224_69334475">comments for finding the link to the MyPy issue.</a></em></p>
</blockquote>
<br/>
<hr/>
<br/>
<h2>可能的解决方案:使用duck类型</h2>
<br/>
<p>这里一个可能的解决方案(尽管稍微<em>icky</em>)可能是使用<code>typing.SupportsFloat</code></p>
<br/>
<p><code>SupportsFloat</code>是一个运行时可检查的协议,它有一个<code>abstractmethod</code>,<code>__float__</code>。这意味着任何具有<code>__float__</code>方法的类都会被识别为类的子类型,无论是在运行时<em>还是静态类型检查器</em><code>SupportsFloat</code>,即使<code>SupportsFloat</code>不在类的方法解析顺序中</p>
<blockquote>
<p>What's a protocol? What's duck-typing? How do protocols do what they do? Why are some protocols, but not all protocols, checkable at runtime?</p>
<ul>
<li><em><a href="https://www.python.org/dev/peps/pep-0544/" rel="nofollow noreferrer">PEP 544, introducing <code>typing.Protocol</code> and structural-typing/duck-typing</a>, explains in detail how <code>typing.Protocol</code> works. It also explains how static type-checkers are able to recognise classes such as <code>float</code> and <code>int</code> as subtypes of <code>SupportsFloat</code>, even though <code>SupportsFloat</code> does not appear in the method resolution order for <code>int</code> or <code>float</code>.</em></li>
<li><em><a href="https://docs.python.org/3/library/typing.html#nominal-vs-structural-subtyping" rel="nofollow noreferrer">The Python docs for structural subtyping are here.</a></em></li>
<li><em><a href="https://docs.python.org/3/library/typing.html#typing.Protocol" rel="nofollow noreferrer">The Python docs for <code>typing.Protocol</code> are here.</a></em></li>
<li><em><a href="https://mypy.readthedocs.io/en/stable/protocols.html" rel="nofollow noreferrer">The MyPy docs for <code>typing.Protocol</code> are here.</a></em></li>
<li><em><a href="https://docs.python.org/3/library/typing.html#protocols" rel="nofollow noreferrer">The Python docs for <code>typing.SupportsFloat</code> are here.</a></em></li>
<li><em><a href="https://github.com/python/cpython/blob/d441437ee71ae174c008c23308b749b91020ba77/Lib/typing.py#L2163" rel="nofollow noreferrer">The source code for <code>typing.SupportsFloat</code> is here.</a></em></li>
<li><em>By default, protocols cannot be checked at runtime with <code>isinstance</code> and <code>issubclass</code>. <a href="https://github.com/python/cpython/blob/d441437ee71ae174c008c23308b749b91020ba77/Lib/typing.py#L2162" rel="nofollow noreferrer"><code>SupportsFloat</code> <em>is</em> checkable at runtime because it is decorated with the <code>@runtime_checkable</code> decorator</a>. Read <a href="https://docs.python.org/3/library/typing.html#typing.runtime_checkable" rel="nofollow noreferrer">the documentation for that decorator here.</a></em></li>
</ul>
</blockquote>
<p><em>注意:尽管用户定义的协议仅在Python中可用>;=3.8,<code>SupportsFloat</code>模块自从添加到Python 3.5中的标准库之后就一直在<code>typing</code>模块中</em></p>
<hr/>
<p><strong>此解决方案的优点</strong></p>
<ol>
<li><p>所有主要数字类型的综合支持*所有主要数字类型的综合支持*所有主要数字类型的综合支持*所有主要数字类型的综合支持*所有主要数字类型::^{<cd19><cd19>},{<cd19>},{<cd19>},,{<cd19><cd19>},,{<cd19>>cd19>},,{<cd66><cd66>},,{<cd9><cd9><cd9><cd9>}},,,,{<cd9><cd9><cd9><cd9><cd9><cd9><cd9><cd9>}},,,,,,{<cd10<cd9><cd9><cd9><cd9>},{<cd10<cd10><cd10>},},{<cd10<cd10<cd10><cd10>},},<cd10<cd10<cd10<cd10><cd10>>{<cd85>}、{<cd86>}、{<cd87>}、{<cd88>}、{<cd89>}和{<cd90>}它们都有一个<code>__float__</code>方法</p>
</li>
<li><p>如果我们将函数参数注释为<code>type[SupportsFloat]</code>,<a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=5f9378d479cefcf7f422a48108b4406c&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">MyPy correctly accepts* the types that conform to the protocol, and correctly rejects the types that do not conform to the protocol.</a></p>
</li>
<li><p>这是一个相当普遍的解决方案-您不需要显式地枚举所有希望接受的可能类型</p>
</li>
<li><p>使用静态类型检查程序和运行时类型检查库,如<code>typeguard</code></p>
</li>
</ol>
<p><strong>此解决方案的缺点</strong></p>
<ol>
<li><p>这感觉像(而且是)一次黑客攻击。有一个<code>__float__</code>方法并不是任何人在抽象中定义“数字”的合理想法</p>
</li>
<li><p>Mypy不将<code>complex</code>识别为<code>SupportsFloat</code>的亚型<code>complex</code>实际上在Python中有一个<code>__float__</code>方法<;=3.9. 但是,它在<a href="https://github.com/python/typeshed/blob/fe4062af5491bcd85b1098a2092b615226b1f4a0/stdlib/builtins.pyi#L304" rel="nofollow noreferrer">typeshed stub for ^{<cd95>}.</a>中没有<code>__float__</code>方法,因为MyPy(以及所有其他主要类型检查器)使用类型化存根进行静态分析,这意味着它不知道<code>complex</code>有此方法<code>complex.__float__</code>可能从typeshed存根中省略,因为该方法总是引发<code>TypeError</code>;因此,<a href="https://docs.python.org/3.10/whatsnew/3.10.html#removed" rel="nofollow noreferrer">the ^{<cd59>} method has in fact been removed from the ^{<cd95>} class in Python 3.10</a></p>
</li>
<li><p>任何用户定义的类,即使不是数值类,也可能定义<code>__float__</code>。事实上,标准库中甚至有几个非数值类定义了<code>__float__</code>。例如,尽管Python中的<code>str</code>类型(用C编写)没有<code>__float__</code>方法,但是<code>collections.UserString</code>(用纯Python编写)有。(<a href="https://github.com/python/cpython/blob/c96d1546b11b4c282a7e21737cb1f5d16349656d/Objects/unicodeobject.c" rel="nofollow noreferrer">The source code for ^{<cd108>} is here</a>和<a href="https://github.com/python/cpython/blob/0742abdc48886b74ed3b66985a54bb1c32802670/Lib/collections/__init__.py#L1315" rel="nofollow noreferrer">the source code for ^{<cd110>} is here</a>。)</p>
</li>
</ol>
<hr/>
<p><strong>示例用法</strong></p>
<p>除了<code>complex</code>之外,它通过了我测试它的所有数字类型的MyPy:</p>
<pre><code>from typing import SupportsFloat
NumberType = type[SupportsFloat]
def foo(bar: NumberType) -> None:
pass
</code></pre>
<blockquote>
<p><em><a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=5f9378d479cefcf7f422a48108b4406c&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">Try it on MyPy playground here!</a></em></p>
</blockquote>
<p>如果我们希望<code>complex</code>也被接受,那么对这个解决方案的一个简单的调整就是使用以下代码片段,即特殊大小写<code>complex</code>。对于我能想到的每一种数字类型,这都满足MyPy的要求。我还将<code>type[Number]</code>抛出到类型提示中,因为它可以<em>捕获一个假设类,该类<em>直接继承自<code>numbers.Number</code>,<em>没有<code>__float__</code>方法。我不知道为什么有人会编写这样一个类,但是有一些<em>类直接继承自<code>numbers.Number</code>(例如<a href="https://github.com/python/cpython/blob/9626ac8b7421aa5fc04a30359521d24f40105141/Lib/fractions.py#L38" rel="nofollow noreferrer">^{<cd19>}</a>),从理论上讲,创建<code>Number</code>的直接子类而不使用<code>__float__</code>方法肯定是<em>可能的<a href="https://github.com/python/cpython/blob/c379bc5ec9012cf66424ef3d80612cf13ec51006/Lib/numbers.py#L12" rel="nofollow noreferrer">^{<cd16>} itself is an empty class that has no methods</a>-它的存在只是为了为标准库中的其他数值类提供“虚拟基类”</p>
<pre><code>from typing import SupportsFloat, Union
from numbers import Number
NumberType = Union[type[SupportsFloat], type[complex], type[Number]]
# You can also write this more succinctly as:
# NumberType = type[Union[SupportsFloat, complex, Number]]
# The two are equivalent.
# In Python >= 3.10, we can even write it like this:
# NumberType = type[SupportsFloat | complex | Number]
# See PEP 604: https://www.python.org/dev/peps/pep-0604/
def foo(bar: NumberType) -> None:
pass
</code></pre>
<blockquote>
<p><em><a href="https://mypy-play.net/?mypy=latest&python=3.10&gist=565b61c2b131c5449b5eeb28ab046a24&flags=show-error-codes%2Cstrict" rel="nofollow noreferrer">Try it on MyPy playground here!</a></em></p>
</blockquote>
<p>翻译成英语,<code>NumberType</code>这里相当于:</p>
<blockquote>
<p>Any class, if and only if:</p>
<ol>
<li>It has a <code>__float__</code> method;</li>
<li>AND/OR it is <code>complex</code>;</li>
<li>AND/OR it is a subclass of <code>complex</code>;</li>
<li>AND/OR it is <code>numbers.Number</code>;</li>
<li>AND/OR it is a "strict" (non-virtual) subclass of <code>numbers.Number</code>.</li>
</ol>
</blockquote>
<p>我不认为这是<code>complex</code>问题的“解决方案”——它更多的是一种变通方法。与{<cd95>}有关的问题说明了这种方法的危险性。例如,在第三方库中可能存在其他不寻常的数值类型,它们不直接子类<code>numbers.Number</code>或具有<code>__float__</code>方法。要事先知道它们可能是什么样子是非常困难的,特别是在所有的情况下</p>
<br/>
<hr/>
<br/>
<h2>附录</h2>
<br/>
<p><strong>为什么<code>SupportsFloat</code>而不是<a href="https://docs.python.org/3/library/typing.html#typing.SupportsInt" rel="nofollow noreferrer">^{<cd130>}</a>?</strong></p>
<p><code>fractions.Fraction</code>有<code>__float__</code>方法(<a href="https://github.com/python/cpython/blob/9626ac8b7421aa5fc04a30359521d24f40105141/Lib/numbers.py#L282-L283" rel="nofollow noreferrer">inherited from ^{<cd133>}</a>),但没有<code>__int__</code>方法</p>
<br/>
<p><strong>为什么<code>SupportsFloat</code>而不是<a href="https://docs.python.org/3/library/typing.html#typing.SupportsAbs" rel="nofollow noreferrer">^{<cd136>}</a>?</strong></p>
<p>甚至<code>complex</code>也有一个<code>__abs__</code>方法,所以<code>typing.SupportsAbs</code>乍一看似乎是一个很有前途的选择!但是,标准库中还有其他几个类有<code>__abs__</code>方法,而没有<code>__float__</code>方法,如果说它们都是数值类,那就太夸张了。(<a href="https://github.com/python/cpython/blob/0742abdc48886b74ed3b66985a54bb1c32802670/Lib/datetime.py#L644" rel="nofollow noreferrer">^{<cd142>}</a>对我来说不太像<em>数字。)如果你使用<code>SupportsAbs</code>而不是<code>SupportsFloat</code>,你可能会把你的网拉得太宽,并允许各种非数字类</p>
<br/>
<p><strong>为什么<code>SupportsFloat</code>而不是<a href="https://docs.python.org/3/library/typing.html#typing.SupportsRound" rel="nofollow noreferrer">^{<cd146>}</a>?</strong></p>
<P>作为^ {CD57>}的替代,您也可以考虑使用^ {CD148>},它接受所有具有^ {CD149>}方法的类。这与<code>SupportsFloat</code>一样全面(它涵盖了除<code>complex</code>之外的所有主要数字类型)。它的另一个优点是<code>collection.UserString</code>没有<code>__round__</code>方法,而如上所述,它确实有<code>__float__</code>方法。最后,第三方非数值类不太可能包含<code>__round__</code>方法</p>
<p>但是,在我看来,如果您选择<code>SupportsRound</code>而不是<code>SupportsFloat</code>,那么排除有效的第三方数值类的风险会更大,因为任何原因,这些类都没有定义<code>__round__</code></p>
<p>“Having a <code>__float__</code>方法”和“Having a <code>__round__</code>方法”对于类是“数字”意味着什么都是非常糟糕的定义。然而,前者感觉比后者更接近“真实”的定义。因此,指望第三方数值类具有<code>__float__</code>方法比指望它们具有<code>__round__</code>方法更安全</p>
<p>如果您希望在确保函数接受有效的第三方数字类型时“格外安全”,我看不出扩展<code>NumberType</code>有什么特别的<em>危害,甚至<em>进一步</em>扩展<code>SupportsRound</code>:</p>
<pre><code>from typing import SupportsFloat, SupportsRound, Union
from numbers import Number
NumberType = Union[type[SupportsFloat], type[SupportsRound], type[complex], type[Number]]
</code></pre>
<p>然而,考虑到任何具有<code>__round__</code>方法的类型也很可能具有<code>__float__</code>方法,我会质疑是否真的有必要包含<code>SupportsRound</code></p>
<hr/>
<p><sup><sub>*…除了<code>complex</code></sub></sup></p>