如何使用__init_subclass__代替ABCMeta执行一个子类实现父类的抽象方法?

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

我有以下代码来比较基类的电流(空)执行所需的功能,以它的子类,其中必须实现他们在某些不同的方式,以被认为是在运行时是可接受的。如果不使用metaclass=ABCMeta和实施@abstractmethod装饰这些基类的方法,我怎么会去这样做?现在,我正在写在我的项目中多个地方我的ad-hoc,元类,那么抽象基类以下__init_subclass__挂钩,但感觉不对。

import inspect

class AbstractThing:
    def __init__(self, topic: str, thing: Thing):
        thing.subscriptions[topic] = self.on_message
        thing.on_connected.append(self.on_connected)
        thing.on_disconnected.append(self.on_disconnected)

    def __init_subclass__(cls):
        required_methods = ['on_connected', 'on_disconnected', 'on_message']
        for f in required_methods:
            func_source = inspect.getsourcelines(getattr(cls, f))
            # if this class no longer inherits from `Object`, the method resolution order will have updated
            parent_func_source = inspect.getsourcelines(getattr(cls.__mro__[-2], f))
            if func_source == parent_func_source:
                raise NotImplementedError(f"You need to override method '{f}' in your class {cls.__name__}")

    def on_connected(self, config: dict):
        pass

    def on_disconnected(self):
        pass

    def on_message(self, msg: str):
        pass

有一个更好的方法吗?奖励积分,如果我能在我的编辑得到的类型检查的错误,同时定义这个AbstractThing的子类。

python abstract-class inspect
1个回答
2
投票

事实上,你不应该依赖inspect.getsourcelines为应在严重的环境中使用的任何代码(即外实验领域或工具来处理源代码本身)

在简单明了的操作者is足够,以检查是否在一个给定的类中的方法是相同的基类。 (在Python 3. Python 2个的用户必须要小心,被检索的作为unbound methods代替原始功能的方法)

除此之外,你正在服用一些不必要的轮流去的基类本身 - 的little documented and little used special variable __class__可以帮你:这是一个自动的引用,里面写到(类主体不self.__class__的错误,是一个参考到子类,而不是)。

从文档:

这个类对象是将由super(). __class__的零参数形式被引用的一个是由编译器创建如果在类身体的任何方法指任__class__super隐式闭合参考。这允许super()的零参数形式正确地识别基于词法作用域的类被定义,而用于使当前呼叫类或实例是基于传递给该方法的第一个参数鉴定。

所以,同时保持你的主要做法,整个事情可以说是相当简单:

def __init_subclass__(cls):
    required_methods = ['on_connected', 'on_disconnected', 'on_message']
    for f in required_methods:
         if getattr(cls, f) is getattr(__class__, f):
              raise NotImplementedError(...)

如果你有一个复杂的层次结构,并与其他强制方法,父类,这些类的子类必须实现 - 因此,不能硬编码在required_methods所需要的方法,你仍然可以使用从abstractmethodabc装饰,不使用ABCMeta元类。所有装饰确实是建立在其上的元类检查方法的属性。只是要在__init_subclass__方法相同的检查:

from abc import abstractmethod

class Base:
   def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        for attr_name in dir(cls):
            method = getattr(cls, attr_name)
            if (getattr(method, '__isabstractmethod__', False) and
                    not attr_name in cls.__dict__):
                # The second condition above allows 
                # abstractmethods to exist in the class where 
                # they are defined, but not on further subclasses
                raise NotImplementedError(...)

class NetworkMixin(Base):
    @abstractmethod
    def on_connect(self):
         pass

class FileMixin(Base):
    @abstractmethod
    def on_close(self):
         pass

class MyFileNetworkThing(NetworkMixin, FileMixin):
    # if any of the two abstract methods is not
    # implemented, Base.__init_subclass__ will fail

请记住,这只是检查,在一类dir显示方法。但是,定制__dir__用于塞尔顿足以使它成为可靠的 - 只是要小心文件证明。

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