简体   繁体   English

是否可以仅键入 class object 提示但排除子类对象?

[英]Is it possible to type hint exclusively a class object but exclude subclass objects?

I would like to exclusively type hint an argument to a specific class but exclude any subclasses.我想专门键入提示特定 class 的参数,但排除任何子类。

class A:
    pass
class B(A):
    pass

def foo(obj: A):
    pass

foo(B()) # I'd like the type checker to warn me here that it expects A, not B

Is this possible?这可能吗? and if so, how?如果是这样,如何?

(bonus points if you can tell me what I would call this. Googling wasn't helpful, but I'm afraid I'm using the wrong terminology to describe this) (如果你能告诉我我会怎么称呼它,那么加分。谷歌搜索没有帮助,但我担心我使用了错误的术语来描述这个)

No, this is not possible to do.不,这是不可能的。

Fundamentally, the Python typing ecosystem assumes that you are following the Liskov substitution principle -- assumes that it is always safe to substitute a subclass in places designed to handle the parent.从根本上说,Python 类型生态系统假定您遵循Liskov 替换原则——假定在设计用于处理父类的地方替换子类总是安全的。

The fact that it permits you to pass in instances of B in addition to instances of A in your code snippet is just one example of this principle in play.除了代码片段中的 A 实例之外,它还允许您传递 B 的实例这一事实只是这一原则的一个例子。

So if your subclass B is designed not to follow the Liskov substitution principle, that probably it wasn't ever really a "kind of" A to begin with and shouldn't be subclassing it.因此,如果您的子类 B 被设计为遵循 Liskov 替换原则,那么它可能从一开始就不是真正的“某种” A,也不应该对其进行子类化。

You could fix this by either adjusting your code so B does properly follow Liskov or by making B stop subclassing A and instead use composition instead of inheritance as a mechanism for code reuse.您可以通过调整代码使 B 正确遵循 Liskov 或通过使 B 停止继承 A 并使用组合而不是 inheritance 作为代码重用机制来解决此问题。 That is, make B keep an instance of A as a field and use it as appropriate.也就是说,让 B 将 A 的一个实例保留为字段并酌情使用它。

And if you run into a rare case where it's legitimately not possible to ever subclass A without breaking Liskov, something you could do to prevent people from accidentally subclassing it would be to explicitly mark A as being final :而且,如果您遇到一种罕见的情况,即在不破坏 Liskov 的情况下合法地不可能对 A 进行子类化,那么您可以采取一些措施来防止人们意外地对其进行子类化,那就是明确地将 A 标记为final

from typing import final
# If you want to support Python 3.7 or earlier, pip-install 'typing_extensions'
# and do 'from typing_extensions import final' instead

@final
class A: pass

class B(A): pass

This should make your type checker report a "B cannot subclass A" error on the definition of B. And if you fix that error by changing/deleting B , the call to foo(B()) should also naturally fail to type-check.这应该使您的类型检查器在 B 的定义上报告“B 不能继承 A”错误。如果您通过更改/删除B来修复该错误,则对foo(B())的调用自然也应该无法进行类型检查.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM