深入理解 Python 中的 __init_subclass__
2021/11/28 17:12:19
本文主要是介绍深入理解 Python 中的 __init_subclass__,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
起源
在研究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"""
这篇关于深入理解 Python 中的 __init_subclass__的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-21Python编程基础教程
- 2024-11-20Python编程基础与实践
- 2024-11-20Python编程基础与高级应用
- 2024-11-19Python 基础编程教程
- 2024-11-19Python基础入门教程
- 2024-11-17在FastAPI项目中添加一个生产级别的数据库——本地环境搭建指南
- 2024-11-16`PyMuPDF4LLM`:提取PDF数据的神器
- 2024-11-16四种数据科学Web界面框架快速对比:Rio、Reflex、Streamlit和Plotly Dash
- 2024-11-14获取参数学习:Python编程入门教程
- 2024-11-14Python编程基础入门