简体   繁体   English

可调用类的 Python 类型

[英]Python Typing for a Callable Class

I have something like我有类似的东西

class Class1(Class2):
    def __init__(
        self,
        use_class = Class3
        ...
    ):
         do something

So Class1 inherits from Class2 and needs a parameter use_class which is set to Class3 by default.所以Class1继承自Class2并且需要一个默认设置为Class3的参数use_class Class3 accepts floats and/or strings as input. Class3接受floats和/或strings作为输入。 But there can be other classes set to use instead of Class3 .但是可以使用其他类来代替Class3 They also accept floats and/or strings as input.它们还接受floats和/或strings作为输入。 However, they all have to inherit from the same Class4 .但是,它们都必须从同一个Class4继承。 Furthermore, each of those classes that are valid inputs for use_class doesn't return anything.此外,作为use_class有效输入的每个类都不会返回任何内容。 So my guess for typing would be所以我对打字的猜测是

class Class1(Class2):
    def __init__(
        self,
        use_class: Callable[[Union[float, str]], None] = Class3
        ...
    ):
         do something

But this doesn't incorporate the fact that the class given as use_class has to inherit from Class4 in order for the code to run.但这并没有包含这样一个事实,即作为use_class给出的类必须从Class4继承才能运行代码。 Am I missing something here?我在这里错过了什么吗? I hope this question makes sense, any help or pointers are greatly appreciated.我希望这个问题有意义,非常感谢任何帮助或指示。

We can do either of these, but not both.我们可以做其中任何一个,但不能同时做。

To require that it be a subclass of Class4, we'd use Type for the standard nominal typing:为了要求它是 Class4 的子类,我们将Type用于标准的名义类型:

IsClass4 = Type[Class4]

And requiring that the value itself is a callable accepting certain inputs can be handled by structural typing via the Callable you suggested, or simply Protocol :并且要求值本身是一个可调用的接受某些输入,可以通过您建议的Callable或简单的Protocol结构类型来处理:

class MostlyWorks(Protocol):
    def __call__(self, value: float | str) -> Class4:
        ...

Ideally, we'd like to do both via an intersection type :理想情况下,我们希望通过交集类型来实现这两者:

WhatWeWant = IsClass4 & AcceptsFloatsStrings

They're working on a PEP, but it's going to be some time.他们正在研究 PEP,但这需要一些时间。

Two thoughts on this: Python type checkers are often quite loose or even buggy, and there's not an absolutely canonical meaning for annotations.对此有两个想法:Python 类型检查器通常非常松散甚至有问题,并且注释没有绝对规范的含义。 They were designed to catch dumb mistakes in existing codebases, and to make code a bit more self-documenting.它们旨在捕捉现有代码库中的愚蠢错误,并使代码更具自我记录性。

This is why I'd advise against going too deep down the annotations rabbit hole.这就是为什么我建议不要在注释兔子洞中走得太深。

Second thought: imagine the type checker is a rather dim colleague such that if you can explain it to the type checker without using advanced techniques, your code will be clear and readable.第二个想法:想象类型检查器是一个相当昏暗的同事,如果你可以在不使用高级技术的情况下向类型检查器解释它,你的代码将是清晰易读的。

Here you're mixing nominal typing, structural typing, and you're requiring a Type[...] instance.在这里,您混合了名义类型、结构类型,并且您需要一个Type[...]实例。 That's tricky stuff.这是棘手的事情。

For instance, suppose you want use_class to be a subclass of Class4 so you can call a class method on it.例如,假设您希望use_classClass4的子类,因此您可以在其上调用类方法。

What if we don't need this to be a class ?如果我们不需要这是一个class怎么办? What if it's just a Plain Old Python Object that just looks like one:如果它只是一个看起来像一个普通的旧 Python 对象怎么办:

class FakeClass(Protocol):
    def __call__(self, value: float | str) -> Class4:
        ...

    def works_like_classmethod(self, woah: Class77) -> str:
        ...

This gets us back to a fairly standard pattern, and one that explicitly lays out what your code will do with this thing.这让我们回到了一个相当标准的模式,并且明确地列出了你的代码将如何处理这件事。

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

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