gpt4 book ai didi

python - 如何强制子类使用 __init_subclass__ 而不是 ABCMeta 来实现父类的抽象方法?

转载 作者:太空宇宙 更新时间:2023-11-04 02:07:04 28 4
gpt4 key购买 nike

我有以下代码来比较基类的当前(空)所需功能的实现与其子类,子类必须以某种不同的方式实现它们,以便在运行时被认为是可接受的。如果不使用 metaclass=ABCMeta 并在这些基类方法上实现 @abstractmethod 装饰器,我该怎么做呢?现在,我正在项目的多个位置编写以下 __init_subclass__ Hook 我的临时、无元类的抽象基类,但感觉不对。

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 的子类时在我的编辑器中遇到类型检查错误,则可加分。

最佳答案

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

简单明了的 is 运算符足以检查给定类中的方法是否与基类中的方法相同。 (在 Python 3 中。Python 2 用户必须注意方法是作为未绑定(bind)方法而不是原始函数检索的)

除此之外,您还进行了几次不必要的转弯以到达基类本身 - little documented and little used special variable __class__可以帮助你:它是对编写它的类主体的自动引用(不要误认为 self.__class__ 而是对子类的引用)。

来自文档:

This class object is the one that will be referenced by the zero-argument form of super(). __class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

因此,在保持主要方法的同时,整个事情可以变得非常简单:

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 中硬编码所需的方法,您仍然可以使用 abc 中的 abstractmethod 装饰器,而无需使用 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__ 很少被使用以使其可靠 - 只需注意记录它。

关于python - 如何强制子类使用 __init_subclass__ 而不是 ABCMeta 来实现父类的抽象方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54439861/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com