理解 Python 中的 __init_subclass__

在Python的世界里,几乎全部的东西都是可变的。html

起步

类方法 __init_subclass__3.6whatsnew3.6) 引入,做用是能够在不使用元类的状况下改变子类的行为。也就是说它是独立于元类编程的,也能达到编辑其余类的一种手段。那么,如何来理解它呢?python

从示例入手

按照文档中的示例:编程

class Philosopher:
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass
复制代码

示例中,当有子类继承了 Philosopher ,那么 __init_subclass__ 就会调用。内容也很简单,父类 Philosopher 为它的全部子类都设置了 default_name 属性。bash

__init_subclass__ 就像是个钩子函数,当子类定义以后触发。函数

默认实现 object.__init_subclass__ 不执行任何操做。但默认实现不能传递任何参数,不然报错。ui

值得注意的是,示例中的 __init_subclass__ 第一个参数是 cls 而不是常见的 self 。这是由于这个方法隐式地被 @classmethod 装饰(PEP-487)。这个决定实际上是因为多数人忘记给 __prepare__ 加上 @classmethod 装饰了,带来了很差的用户体验;并且在另外一个PEP中 __prepare__ 被记录为普通的方法,因此已经很差改它了。而做为 3.6 新方法的 __init_subclass__ 所以就有理由隐式装饰了(其实我比较喜欢显式,由于 Python 之禅中提到的 "显示优于隐式")。spa

建立类对象后的自定义步骤

尽管 __init_subclass__ 是独立于元类编程的,但类都是由默认元类 type 建立的,那么在 type.__new__() 建立了类以后会有哪些步骤:code

  1. 首先,type.__new__ 收集类命名空间定义的 set_name() 方法的全部描述符;
  2. 其次,这些 __set_name__ 的特定描述符在特定的状况下调用;
  3. 最后,在父类上调用钩子 __init_subclass__()

若类被装饰器装饰,那么就将上述生成的对象传递给类装饰器。htm

总结

总的来讲,__init_subclass__() 是钩子函数,它解决了如何让父类知道被继承的问题。钩子中能改变类的行为,而没必要求助与元类或类装饰器。钩子用起来也更简单且容易理解。对象

虽然本文还提到了 __set_name__ ,但它和 __init_subclass__ 并不相互关联, __set_name__ 主要是解决了如何让描述符知道其属性的名称。

__init_subclass__ 的目标是提供更简单的定制方式,在简单的场景下是元类的替代品。值得试一试。

相关文章
相关标签/搜索