我有以下代码将基本类的当前(空)所需函数的实现与其子类进行比较,子类必须以某种不同的方式实现它们,以便在运行时被接受。在这些基类方法上不使用metaclass=ABCMeta
并实现@abstractmethod
装饰器,我该怎么做呢?现在,我正在项目中多个位置的临时,无元类的抽象基类上编写以下__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
的子类时在编辑器中遇到类型检查错误。
答案 0 :(得分:2)
实际上,您不应依赖inspect.getsourcelines
来处理应在严重上下文中使用的任何代码(例如,在实验领域之外或用于处理源代码本身的工具)
简单而简单的is
运算符就足以检查给定类中的方法是否与基类中的方法相同。 (在Python 3中。Python2用户必须注意,方法检索为unbound methods
而不是原始函数)
除此之外,您需要进行一些不必要的操作才能进入基类本身-little documented and little used special variable __class__
可以帮助您:它是对编写它的类主体的自动引用(不要self.__class__
错误,而该错误是对子类的引用。
从文档中:
该类对象是将由{_1}的零参数形式引用的对象,如果类主体中的任何方法引用
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
中的abstractmethod
装饰器,而不使用abc
元类。装饰者所做的只是在对元类检查的方法上创建一个属性。只需在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
的使用足以使它变得可靠-务必要对其进行记录。