ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

深入理解 Python 中的 __init_subclass__

2021-11-28 15:34:55  阅读:236  来源: 互联网

标签:__ name Python subclass init meta cls


起源

在研究graphql-python源码的时候被__init_subclass_with_meta__这个类方法吸引,进而发现除元类外改变子类行为的另一种方式:__init_subclass__

类方法 __init_subclass__ 从 3.6 引入,作用是可以在不使用元类的情况下改变子类的行为。也就是说它是独立于元类编程的,也能达到编辑其他类的一种手段。

示例1

# defining a SuperClass
class SuperClass:
  
     # defining __init_subclass__ method
    def __init_subclass__(cls, **kwargs):
        cls.default_name ="Inherited Class"
  
# defining a SubClass
class SubClass(SuperClass):
  
     # an attribute of SubClass
    default_name ="SubClass" 
    print(default_name)
  
subclass = SubClass()
print(subclass.default_name)

输出

SubClass
Inherited Class

了解代码

  • 在上面的示例中,有 2 个类(即超类和子类),子类继承自超类。default_name是子类的一个属性。
  • 属性default_name的值由 SuperClass 使用__init_subclass__方法更改。
  • cls是指继承的子类。提供给新类的关键字参数 (**kwargs) 将传递给父类的类__init_subclass__。
  • 为了与使用__init_subclass__的其他子类兼容,应该取出所需的关键字参数,并将其他子类传递给基类(Super Class)。

这个__init_subclass__ 子类与Decorator类非常相似。但是,如果类装饰符只影响它们应用于的特定类,但是__init_subclass__只应用于定义该方法的类的未来子类。这意味着我们可以改变/定义从超类继承的任何新类的行为。

示例2

# defining a SuperClass
class SuperClass:
	def __init_subclass__(cls, default_name, **kwargs):
		cls.default_name = default_name

# defining a subclass
class SubClass1(SuperClass, default_name ="SubClass1"):
	pass

# defining another subclass
class SubClass2(SuperClass, default_name ="SubClass2"):
	default_name = "InheritedClass"


# references for subclasses
subClass1 = SubClass1()
subClass2 = SubClass2()

print(subClass1.default_name)
print(subClass2.default_name)

输出

SubClass1
SubClass2

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

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

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

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

总结

总的来说,__init_subclass__() 是钩子函数,它解决了如何让父类知道被继承的问题。钩子中能改变类的行为,而不必求助与元类或类装饰器。钩子用起来也更简单且容易理解。
虽然本文还提到了 __set_name__ ,但它和 __init_subclass__ 并不相互关联, __set_name__ 主要是解决了如何让描述符知道其属性的名称。

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

下面是graphql-python对该方法的使用,值得学习

点击查看代码
from inspect import isclass

from .props import props


class SubclassWithMeta_Meta(type):
    _meta = None

    def __str__(cls):
        if cls._meta:
            return cls._meta.name
        return cls.__name__

    def __repr__(cls):
        return f"<{cls.__name__} meta={repr(cls._meta)}>"


class SubclassWithMeta(metaclass=SubclassWithMeta_Meta):
    """This class improves __init_subclass__ to receive automatically the options from meta"""

    def __init_subclass__(cls, **meta_options):
        """This method just terminates the super() chain"""
        _Meta = getattr(cls, "Meta", None)
        _meta_props = {}
        if _Meta:
            if isinstance(_Meta, dict):
                _meta_props = _Meta
            elif isclass(_Meta):
                _meta_props = props(_Meta)
            else:
                raise Exception(
                    f"Meta have to be either a class or a dict. Received {_Meta}"
                )
            delattr(cls, "Meta")
        options = dict(meta_options, **_meta_props)

        abstract = options.pop("abstract", False)
        if abstract:
            assert not options, (
                "Abstract types can only contain the abstract attribute. "
                f"Received: abstract, {', '.join(options)}"
            )
        else:
            super_class = super(cls, cls)
            if hasattr(super_class, "__init_subclass_with_meta__"):
                super_class.__init_subclass_with_meta__(**options)

    @classmethod
    def __init_subclass_with_meta__(cls, **meta_options):
        """This method just terminates the super() chain"""

标签:__,name,Python,subclass,init,meta,cls
来源: https://www.cnblogs.com/zhanhaixiang/p/15615286.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有