类型提示动态创建子类

问题描述 投票:0回答:1

简化示例

class A:
    def __init__(self): ...

class BBuilder:
    # To make this simpler, we won't have any arguments

    def build(self): # -> WhatTypeHintDoIPutHere

        class B(A):
            def __init__(self):
                super().__init__()

        return B

my_b_builder: BBuilder = BBuilder()
BBuilt = my_b_builder.build() # BBuilt: WhatTypeHintDoIPutHere
my_b = BBuilt() # my_b: WhatTypeHintDoIPutHere

问题

如何正确输入提示

my_b
BBuilt
?无需删除 BBuilder,也无需用静态子类定义替换 BBuilder。

我尝试了什么

尝试输入提示

my_b
为:

  • Type[A] - 不正确,因为 my_b 是一个实例

  • 类型[B] - 不正确,因为 my_b 是一个实例

  • BBuilt - mypy 抱怨 - 你不能在类型提示中使用变量

  • 使用 TypeAliases

    BBuiltAlias: TypeAlias = BBuilt; my_b: BBuiltAlias
    - 您不能在类型提示中使用变量

  • 使用

    type
    关键字
    type BBuiltAlias = BBuilt; my_b: BBuiltAlias
    - 您不能在类型提示中使用变量

将 BBuilder 编辑为不是数据类不会改变任何东西
还尝试了 TypeVars - 没有成功。尝试: 1 2 3
还尝试询问 ChatGPT - 没有成功

后记

如果您真的需要它,这里是原始代码以及这些类的一些示例用法。作为一个附带问题:这样做是一种不好的编码习惯吗?原始代码易于编辑,易于使用。

题外话:我尝试解决这个问题失败了这里

python class type-hinting builder typing
1个回答
0
投票

即使这是可能的,动态创建类(根据很多人的说法)可能会带来一些令人讨厌的问题。可能还有其他方法可以在没有动态类定义的情况下解决您的问题!

答案其实非常简单。

示例

from typing import Type

# To make this a little simpler, we will not define any arguments for the init functions.
# It would not be that hard if you wanted to introduce arguments


class ABase:  # We want to subclass ABase and overwrite its init
    def __init__(self) -> None:  # We want to call this init in our dynamically created class!
        print("ABase Init")
        ...  # Imagine lots of code here

    def uncool(self) -> None:  # This method is uncool. We want our final subclass to overwrite it and make it cooler
        print("Uncool (we should not see this message because its uncool and this method will be overwritten)")

    def a_cool(self) -> None:  # This method is cool! We want our final subclass to support it!
        print("A_Cool Is there really no other way to solve your problem than creating classes dynamically?")


class BReal(ABase):
    def __init__(self) -> None:
        super().__init__()
        print("BReal Init")
        ...  # Imagine lots of code here

    def b_cool(self) -> None:  # This method is also cool! we want our final subclass to support it!
        print("B_Cool")

    def uncool(self) -> None:
        print("Uncool is now cool because it has been overwritten! So we can now see its message!")

    # Define all the methods and functionality you want the final dynamically-created Subclass to support
    # Basically treat BReal almost as if it WAS the final dynamically-created Subclass


class BClassBuilder:

    def build(self) -> Type[BReal]:  # Since the B class is 100% compatible with BReal. There is no problem with using the Type[BReal] typehint
        class B(BReal): ...

        return B  # Yay! B is now dynamically created

        # KEEP IN MIND THIS IS JUST SOME BASIC PROOF-OF-CONCEPT CODE
        # I KEPT THIS AS SIMPLE AS POSSIBLE
        # But don't be fooled, this simple design took me a day to figure out


our_builder: BClassBuilder = BClassBuilder()
BBuilt: Type[BReal] = our_builder.build()  # Since the B class is 100% compatible with BReal. There is no problem with using the `Type[BReal]` typehint

print("\nTesting Inits")
our_b: BReal = BBuilt()  # Instance of our dynamically created B class!

print("\nTesting Methods")
our_b.a_cool()  # InteliSense WORKED
our_b.b_cool()  # InteliSense WORKED
our_b.uncool()  # InteliSense WORKED

"""OUTPUT

Testing Inits
ABase Init
BReal Init

Testing Methods
A_Cool Is there really no other way to solve your problem than creating classes dynamically?
B_Cool
Uncool is now cool because it has been overwritten! So we can now see its message!
"""  # EVERYTHING WORKS AND THERE ARE 0 COMPLAINTS FROM PYLANCE AND MYPY

说明

由于示例中的注释数量较多,我认为没有必要进行太多解释。我将简要解释正在发生的事情:

  1. 首先我们将要子类化的类定义为

    ABase

  2. 然后我们静态创建

    ABase
    的子类,名为
    BReal
    (“真实”,因为这实际上是您编写代码的部分)。我们动态创建的类将简单地子类化 BReal,因此继承它的所有方法和其他内容。

  3. 在现在的示例中,我们添加了一些动态定义类的方法。我选择了 Class Builder(这可能不是一个真正的术语,也不是一个专业术语)。

  4. 定义 A 的动态创建子类非常简单。我们将其命名为 B。

    class B(BReal): ...
    我们故意使用 ...,因为我们没有应该在这里编写真正的代码。

对于那些希望 B 在其 init 中支持参数的 Python 新手请注意:
由于我们定义 B 的方式使其成为 BReal 的 100% 子类,没有任何差异,因此它也继承了 BReal 的

init
!因此,如果您希望 B 支持参数,请编辑 BReal 的
init
。保留 B 的
init
未定义!

  1. 现在只需使用您编写的代码来动态定义一个类并将其保存到变量中即可。
    如果您想了解如何输入该变量和 B 的实例,请阅读示例的代码。

即使这是可能的,动态创建类(根据很多人的说法)可能会带来一些令人讨厌的问题。可能还有其他方法可以在没有动态类定义的情况下解决您的问题!

作为旁注。我觉得有点不满意,问题的解决方案是首先在全局范围内静态定义类,然后基本上“镜像”它

© www.soinside.com 2019 - 2024. All rights reserved.