python
文章目录
一、python中类的__init_subclass__
__init_subclass__
是一个特殊的类方法,它在Python 3.6中被引入。当一个类被定义为另一个类的子类时,__init_subclass__
方法会被自动调用。这个方法可以用来自定义子类的初始化过程。
举个例子:
class MyClass:
"""
__new__方法是一个静态方法,它的第一个参数是cls,表示要实例化的类
这个方法的主要任务是创建新的实例并返回它。
"""
def __new__(cls, *args, **kwargs):
instance = super().__new__(cls)
# 在这里可以添加一些额外的初始化代码
return instance
"""
__init__方法则是一个实例方法,它在__new__方法创建新实例后被调用,用于初始化新创建的实例。
"""
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def __init_subclass__(cls, /, default_name, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
print(f'{cls} is a subclass of BaseClass')
class SubClass(MyClass, default_name="Bruce"):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
a = MyClass(1, 2)
b = SubClass(3, 4)
在这个例子中,当SubClass
被定义为MyClass
的子类时,BaseClass
的__init_subclass__
方法会被自动调用,打印出一条消息。
这段代码中的__init_subclass__
方法用于在子类创建时进行一些初始化操作。在这个例子中,它将default_name
参数设置为子类的类属性,并打印一条消息。
/
在函数参数列表中的作用是指定位置参数。在/
之前的参数必须作为位置参数传入,不能作为关键字参数。在这个例子中,default_name
必须作为位置参数传入。
这样做的好处是:
- 提供默认值:通过在
__init_subclass__
方法中设置类属性,我们可以为所有子类提供默认值。在这个例子中,所有的MyClass
子类都会有一个default_name
属性,其默认值为创建子类时传入的值。 - 强制参数传递方式:通过使用
/
,我们可以强制default_name
必须作为位置参数传入,这可以避免在调用函数时出现歧义。
需要注意的是,__init_subclass__
方法的第一个参数(cls)是子类,而不是实例。这个方法通常用于元编程,即编写可以影响或修改其他代码的代码。在大多数情况下,我们可能不需要使用__init_subclass__
方法。
/
在Python 3.8中被引入,如果我们使用的Python版本低于3.8,这段代码可能会出错。
运行过程
二、__init_subclass__会使用在什么场景
__init_subclass__
主要用于元编程,即编写可以影响或修改其他代码的代码。它在一个类被子类化时被调用,允许我们在子类化时执行一些操作。以下是一些可能的使用场景:
- 注册子类:我们可以使用
__init_subclass__
来自动注册所有的子类。例如,如果我们正在编写一个插件系统,我们可能想要在一个类被子类化时自动注册这个子类。
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
print(PluginBase.subclasses)
# 输出: [<class '__main__.Plugin1'>, <class '__main__.Plugin2'>]
在这个例子中,我们在PluginBase
的__init_subclass__
方法中将每个子类添加到subclasses
列表中。然后,我们可以通过查看subclasses
列表来找出所有的插件。
- 强制子类实现某些方法:我们可以使用
__init_subclass__
来检查子类是否实现了某些方法,如果没有,我们可以抛出一个错误。
class AbstractBase:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if not hasattr(cls, 'foo'):
raise TypeError('Subclasses must define foo method')
class GoodSubclass(AbstractBase):
def foo(self):
pass
class BadSubclass(AbstractBase):
pass
# 抛出错误: TypeError: Subclasses must define foo method
在这个例子中,我们在AbstractBase
的__init_subclass__
方法中检查子类是否定义了foo
方法。如果没有,我们就抛出一个错误。这样,我们就可以确保所有的子类都实现了foo
方法。
__init_subclass__
方法主要用于元编程,即编写可以影响或修改其他代码的代码。它在一个类被子类化时被调用,允许我们在子类化时执行一些操作。以下是一些可能的使用场景:
- 注册子类:我们可以使用
__init_subclass__
来自动注册所有的子类。例如,如果我们正在编写一个插件系统,我们可能想要在一个类被子类化时自动注册这个子类。 - 强制子类实现某些方法:我们可以使用
__init_subclass__
来检查子类是否实现了某些方法,如果没有,我们可以抛出一个错误。 - 自动添加类属性或方法:我们可以使用
__init_subclass__
来自动给子类添加一些属性或方法。 - 修改类的行为:我们可以使用
__init_subclass__
来修改子类的行为,例如修改类的元类。
这些都是__init_subclass__
可能的使用场景,但并不是所有的场景都需要使用这个方法。在大多数情况下,我们可能不需要使用__init_subclass__
方法。
三、什么是元编程,怎么学习元编程
元编程是一种编程技术,它允许开发者在运行时修改或生成代码。在Python中,元编程主要通过反射(introspection)和元类(metaclasses)来实现。
- 反射:Python的反射功能允许我们在运行时获取对象的类型信息,例如我们可以获取一个对象的类名,方法列表等。我们也可以动态地创建和修改对象。Python的
type()
,getattr()
,setattr()
,hasattr()
等函数提供了反射功能。 - 元类:元类是创建类的类。在Python中,
type
是所有类的元类。我们可以通过继承type
来创建自定义的元类,然后使用这个元类来创建类。元类可以用来修改类的行为,例如添加新的方法,修改属性等。
四、什么是 关键字参数?位置参数?
在Python中,函数的参数可以分为两种:位置参数(Positional arguments)和关键字参数(Keyword arguments)。
- 位置参数:位置参数是按照参数在函数定义中的位置来传递的。例如,在函数
def func(a, b):
中,a
和b
都是位置参数。当我们调用func(1, 2)
时,1
会被传递给a
,2
会被传递给b
。 - 关键字参数:关键字参数是通过参数名来传递的。例如,在函数
def func(a, b):
中,我们可以通过func(a=1, b=2)
来调用函数。这样,无论参数在函数定义中的位置如何,1
都会被传递给a
,2
都会被传递给b
。
在函数调用时,位置参数必须在关键字参数之前。例如,func(1, b=2)
是合法的,但func(a=1, 2)
是不合法的。
此外,Python 3.8引入了一个新的语法/
,用于在函数定义中指定位置参数。在/
之前的参数必须作为位置参数传入,不能作为关键字参数。例如,在函数def func(a, /, b):
中,a
必须作为位置参数传入,b
可以作为位置参数或关键字参数传入。
五、官方链接
https://docs.python.org/zh-cn/3/reference/datamodel.html#object.init_subclass