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

为何Callable泛型类型在参数上是逆变的?

摘要

根据PEP 483的说明,为何Callable泛型类型在参数上是逆变的?我的相关分析是否准确?(分析内容见文末)


背景

我目前正在研究用于类型分析的注解相关内容(如MyPy、Pyright等),同时阅读了相关的PEP 483 – 类型提示理论。

该PEP在协变与逆变章节中指出:

用于阐释(看似违反直觉的)逆变行为的最佳示例之一就是可调用类型。它在返回类型上是协变的,但在参数上是逆变的。

我试图理解的核心问题是:为何可调用类型在参数上是逆变的?


PEP 483补充内容

PEP 483给出了以下概念和定义:

子类型关系

若满足以下条件,则类型subtype是类型basetype的子类型:

  • subtype的所有值都属于basetype的值集合;且
  • basetype的所有函数都属于subtype的函数集合。

逆变

若subtype是basetype的子类型,对于所有此类subtype和basetype,若泛型类型构造器GenType满足GenType[basetype]是GenType[subtype]的子类型,则称GenType是“逆变”的。


我的分析尝试

“为何Callable在参数上是逆变的?”这一问题可重新表述为:

当类型subtype是basetype的子类型时,为何Callable[[basetype], None]是Callable[[subtype], None]的子类型?

根据PEP 483的上述内容,这意味着需要满足以下两个条件:

  1. Callable[[basetype], None]的所有值都包含在Callable[[subtype], None]的值集合中。
  2. Callable[[subtype], None]的所有函数都包含在Callable[[basetype], None]的函数集合中。

回答我的核心问题就需要解释为何这两个条件成立。

1. Callable[[basetype], None]的所有值是否都包含在Callable[[subtype], None]的值集合中?

由于subtype是basetype的子类型,subtype的所有值也都是basetype类型的值。

因此,任何接受basetype类型参数的可调用对象也能接受subtype类型的参数。

这意味着Callable[[basetype], None]类型的任何值(可调用对象)同时也是Callable[[subtype], None]类型的值。

因此,答案是肯定的:Callable[[basetype], None]的所有值都属于Callable[[subtype], None]的值集合。

注意: 与之相反的情况(即人们可能直觉认为的协变)并不成立。

我们用PEP 483自身的示例来定义如下代码:

class Employee: ...
class Manager(Employee):
    def manage(self): ...

def my_callable(m: Manager):
    m.manage()

在此例中,尽管my_callable是Callable[[Manager], None]类型,但向其传入Employee对象会违反类型安全性,Manager.manage方法的定义也能明显体现这一点。这意味着my_callable虽然是Callable[[Manager], None]类型,但并非Callable[[Employee], None]类型,这足以反驳协变的普遍性。

2. Callable[[subtype], None]的所有函数是否都包含在Callable[[basetype], None]的函数集合中?

我对这个问题的答案不太确定,也不清楚如何解答。

我们定义以下接受可调用对象作为参数的函数:

def my_func(c: Callable[[subtype], None]): ...

任何具有此类签名(Callable[[Callable[[subtype], None]], None])的函数是否属于Callable[[Callable[[basetype], None]], None]类型?

任何接受Callable[[subtype], None]的函数是否也接受Callable[[basetype], None]?(我认为问题可以归结为此)

我推测任何此类函数都会向其可调用参数传入subtype实例,因此Callable[[basetype], None]会接受该subtype实例,这使得传入Callable[[basetype], None]是可行的。

但这是否足以断言所有类似my_func的函数都属于Callable[[Callable[basetype], None], None]类型的函数集合?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 21:06:08