__子类或注册表通过初始化子类?

2024-10-03 21:27:46 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我想创建一个特定类的子类的注册表。现在有两种方法我可以想到,虽然我知道(一些)他们的差异,我想了解更多关于这个话题。你知道吗

class Base:
    pass

class DerivedA(Base):
    pass

class DerivedB(Base):
    pass

__subclasses__()

如果有上述情况,我可以简单地得到Base的子类列表,如下所示:

>>> [cmd.__name__ for cmd in Base.__subclasses__()]
['DerivedA', 'DerivedB']

现在我知道,如果我添加第三个类,它不是直接子类化Base,如下所示:

class DerivedC(DerivedA):
    pass

我不会在列表中看到这个:

>>> [cmd.__name__ for cmd in Base.__subclasses__()]
['DerivedA', 'DerivedB']

我也不能过滤子类,例如忽略一个特定的子类的任何原因。你知道吗

__init_subclass__()

由于Python3.6提供了一个很好的类创建过程的钩子,不需要编写自己的元类就可以完成更高级的事情。所以我也可以这样做。。。你知道吗

_registry = []

class Base:

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        _registry.append(cls.__name__)

class DerivedA(Base):
    pass

class DerivedB(Base):
    pass

class DerivedC(DerivedA):
    pass

然后只需访问_registry

>>> _registry
['DerivedA', 'DerivedB', 'DerivedC']

如果需要,我还可以修改Base以忽略某些子类:

_registry = []

class Base:

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if cls.__name__ != 'DerivedB':
            _registry.append(cls.__name__)

class DerivedA(Base):
    pass

class DerivedB(Base):
    pass

class DerivedC(DerivedA):
    pass
>>> _registry
['DerivedA', 'DerivedC']

为什么要用后者?你知道吗

现在假设我不想过滤子类,我只对直接子类感兴趣。前一种方法似乎更简单(我知道是主观的)。其他的区别是什么?后一种方法的优点是什么?你知道吗

谢谢!你知道吗


Tags: 方法namecmdbaseinitpass子类kwargs
1条回答
网友
1楼 · 发布于 2024-10-03 21:27:46

在本例中,在基类中编写__init_subclass__的明显好处是,您可以自动获得不直接从基类继承的子类。你知道吗

如果您只需要直接从基继承的类,那么它就可以在__subclasses__方法中使用,主要的优点是您不需要编写一行代码,甚至不需要保留单独的注册表,因为__subclasses__会为您这样做。你知道吗

但是,除非您正在编写一个相对较小的应用程序,或者正在处理一个只需要查找少量固定数量的子类的功能,否则依赖__subclasses__是不够的—如果您只是需要或想要层次结构中的另一个级别的类,它将停止工作,而且您必须求助于真正的注册表。你知道吗

在使用__init_subclass__钩子之前,您必须编写一个适当的元类来保留此注册表,并将其输入元类__init__方法,或者执行一个复杂的递归查询,如:

def check_subclass(base, candidate):
   for cls in base.__subclasses__():
       if cls is candidate:
          return True
       if check_subclass(cls, candidate):
          return True
   return False

而且,尽管应该不言而喻,__init_subclass__方法可以做的远不止简单地保留一个注册表,因为它可以运行任何代码。它可以根据DB层检查映射到该子类的字段是否是最新的,并警告需要进行的迁移,甚至可以自己执行DB迁移,或者初始化创建类实例时需要找到的任何资源,例如记录器处理程序、线程池、DB连接池(如您所说)。你知道吗

TL;DR:如果您只需要类的直接子类,请使用__subclasses__。关键在于它只是注释了直接子类。你知道吗

相关问题 更多 >