43、Python之面向对象:元类繁琐且吃力,不妨尝试__init_subclass__

引文

前面我们通过两篇文章重点介绍了Python中元类的定义及使用场景,不可否认的是,元类确实比较强大。但是,对于一些Python初学者来说,元类概念的理解,以及真正使用元类,可能还是稍显繁琐、吃力的。

好在Python在3.6之后提供了__init_subclass__的特性,让我们通过这个特殊的方法,在继承关系中也能够实现元类的作用,比如类的检查、动态增强,以及类的注册等功能。

__init_subclass__概述

__init_subclass__()方法是在Python 3.6中引入的一个特殊方法,这个方法在基类中进行定义,当创建该基类的子类时会自动调用。

该方法的主要用途是在父类中定义一些特殊逻辑,当子类被创建时,这些逻辑会自动执行。这样使得父类可以对子类进行一些初始化或者配置操作,而不需要子类显式地定义某个方法实现相同的逻辑。

如同元类的使用场景一样,通过__init_subclass__()方法,我们也能实现注册表、子类定义检查、子类的动态增强等功能。

在这些场景下,优先考虑通过__init_subclass__实现,而不要用标准的元类机制来实现,因为__init_subclass__更清晰,更便于初学者理解。

使用实例

1、子类定义的检查

class PluginBase:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, 'execute') or not callable(getattr(cls, 'execute')):
            raise TypeError(f'{cls.__name__}类未定义execute()方法')


class PluginA(PluginBase):
    def execute(self):
        print(f"插件{self.__class__()}在运行")


a = PluginA()
a.execute()


class PluginB(PluginBase):
    pass
    # def execute(self):
    #     print(f"插件{self.__class__()}在运行")


print("before instance")
b = PluginB()
b.execute()

执行结果:

6d841a46d5f1e143f7ca813aabf18dc4.jpeg

可以看到,__init_subclass__()方法触发的时机也是在类的定义,而非实例化环节。

通过这种方式,我们省去了重新定义一个全新的元类,而且基于继承关系,相关的逻辑显得更加清晰。

2、子类的动态增强

如同通过元类,对类对象动态添加属性、方法,进行类的统一动态增强。通过__init_subclass__()方法,也可以做到这一点。

直接看代码:

class PluginBase:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, 'active'):
            cls.active = False
        if not hasattr(cls, 'execute') or not callable(getattr(cls, 'execute')):
            cls.execute = execute


def execute(self):
    if self.active:
        print(f"插件{self.__class__()}在运行")
    else:
        print(f"插件{self.__class__()}未激活")


class PluginA(PluginBase):
    pass


class PluginB(PluginBase):
    pass


if __name__ == '__main__':
    a = PluginA()
    a.execute()
    a.active = True
    a.execute()
    b = PluginB()
    b.active = True
    b.execute()

执行结果:

6af820bb951d02e753b3dc533d55aee8.jpeg

需要说明的是,上面的代码只是为了演示__init_subclass__()方法可以动态增强子类。其实,在当前场景中,更加合适的做法,是将相关的属性、方法在父类中定义,子类直接继承即可。

3、子类的注册

在一个继承关系中,我们通过子类的__bases__属性,可以找到一个类的所有基类。但是,反过来,如果我们想要从一个基类,找出所有的子类,则有些困难。之前,我们可以通过元类的类似插件注册的功能来实现,现在,有了__init_subclass__()方法,我们也可以实现同样的功能。

直接看代码示例:

class PluginBase:
    plugins = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        PluginBase.plugins[cls.__name__] = cls


class PluginA(PluginBase):
    pass


class PluginB(PluginBase):
    pass


if __name__ == '__main__':
    # 从子类找到父类
    print(PluginA.__bases__)
    # 从父类找到所有的子类
    print(PluginBase.plugins)

执行结果:

f2fda5eae8ff058edcb6b085bf3eeff5.jpeg

总结

本文介绍了Python3.6中引入的新特性,__init_subclass__()方法。通过该方法,我们可以更加轻便地实现之前必须通过元类来实现的类定义检查、类动态增强、类注册等功能。此外,相较于元类概念的不易理解,以及使用相对繁琐,通过__init_subclass__()方法,我们能够写出可读性更佳的代码。

感谢您的拨冗阅读。如果对您学习Python有所帮助,欢迎点赞、关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南宫理的日知录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值