子类实例在需要类实例的地方是否有效?

时间:2018-08-16 21:13:44

标签: python python-3.x

说我们有以下内容:

class MyClass:
    pass

def my_function(class_instance_argument: MyClass):
    pass

如果我正在编写一段代码,并且想要指示应该传递的对象类型,那么我可以做到这一点。但是,如果我将代码进一步扩展到另一个文件中:

class MyClass2(MyClass):
    pass

我不确定子类实例是否应为期望类实例的有效数据类型。我知道注释不具有约束力;实际上,如果我想断言所传递的参数是我的意愿,我会这样做:

assert(issubclass(type(class_instance_argument), MyClass)))

这将适用于任何子类,甚至适用于具有自身的类:

>>> issubclass(type, type)
True

1 个答案:

答案 0 :(得分:4)

最简单的答案是:不。您不必做任何事情。

子类型化是指可替代性。换句话说,如果MyClass2MyClass的子类型,则任何MyClass2的行为都像MyClass一样。或者,换句话说,您可以在任何需要MyClass2的代码中使用MyClass值,即使该代码从未听说过MyClass2,它也可以工作。如果您想深入了解“行为正常”的真正含义,请参阅Wikipedia上有关Liskov substitution principle的更详细的文章。 1

因此,当您注释该功能需要MyClass时,这意味着MyClass2是可以接受的,因为MyClass2实际上是MyClass


值得一读PEP 484,该提案添加了类型提示语法,并且其他PEP从顶部开始链接。实际上,这个问题就在Type Declaration Syntax下的顶部附近:

  

其类型是特定参数类型的子类型的表达式也被该参数接受。


现在,Python实际上并没有强制您的子类成为子类型。强烈建议这样做,但是如果您想编写一个子类来覆盖具有不兼容签名的方法(甚至将它们隐藏在__getattribute__后面),则可以,并且您将愚弄任何合理的静态类型检查器。

但是,只要您没有真正地欺骗类型检查器,一切都会按预期进行。 (而且,如果您去做,请走开……好吧,想必您有这样做的理由,并且在您明确要求他们去工作时,事情将会起作用。)


Python本身是基于鸭子类型的概念构建的-对象实际上是否是某种类型的实例并不重要,只是它提供了具有正确行为的正确属性-非正式地,它“就像鸭子一样MyClass”,或更正式地说,它“符合MyClass协议”。

您可能会认为这不适用于静态类型检查。但是,事实证明,它运行良好。一方面,类型提示总是可选的。 2 但是,当您想显式检查鸭子类型的函数时,通常有一种非常简单的方法来编写结构化协议检查类型,如果没有的话。 typing模块中已经有一个。例如,您不需要list,而是需要Sequence,它将与您的某些用户匹配的tuplerange或某些自定义序列类型匹配只要符合Sequence的协议,库就会发明您从未听说过的东西。 (虽然我们正在这样做,但您也可以要求一个Sequence[int],它将接受listtupleMySillySequence,但前提是类型检查器可以证明它只包含int个值。)


1。 Python的AFAIK文档(包括一组PEP)特意避免引用LSP。 LSP是描述子类型的某种宽松方法,而PEP 484依靠一种描述子类型的方法,该子类型在某种程度上略有不同。使用诸如“子类型树的上限”之类的正式术语而不定义“子类型”已经有点奇怪了,但是它几乎可以工作。暗示子类型树实际上是由LSP定义的,这可能意味着它不够准确。

2。这真的很重要。有些协议易于使用,但很难定义,而且即使在没有完全注释的程序中,您也可以撑开并说出Any,这意味着您可以完全注释该程序,并在其上运行类型检查器(甚至可以看到松散的输入将“泄漏”到其余的代码中,而其他地方则不会)。