python中__init_subclass__方法用法详解

源码内容:

    def __init_subclass__(self, *args, **kwargs): # real signature unknown
        """
        This method is called when a class is subclassed.
        
        The default implementation does nothing. It may be
        overridden to extend subclasses.
        """
        pass

先看下面代码执行的效果:

示例代码1:

class Test(object):

    def __init_subclass__(cls, **kwargs):
        print("__init_subclass__", cls, kwargs)


class A(Test, name="张三", age=16):
    pass


"""
代码运行直接输出下面的结果:
    __init_subclass__ <class '__main__.A'> {'name': '张三', 'age': 16}
"""

运行结果:

        上面代码中定义了一个Test类,然后让A这个类继承它。发现还没有实例化,而是在创建类的时候就有输出结果了。

        对于一个类,如果这个类被作为父类继承,那么会触发其内部的__init_subclass__方法,这里的Test被A继承,那么Test中的__init_subclass__就会被触发。而且看到,里面的cls,就是定义的字类A,也就是继承它的类,**kwargs,就是额外传递的参数。但是发现,第一个参数不是self,而是cls,而且这个cls还不是Test,而是继承它的类。其实这个方法是隐式的被classmethod装饰了。

当多个类同时继承Test父类时,示例代码如下:

class Test(object):

    def __init_subclass__(cls, **kwargs):
        print("__init_subclass__", cls, kwargs)


class A(Test, name="张三", age=16):
    pass


class B(Test, name="李四", age=26):
    pass

"""
代码运行直接输出下面的结果:
    __init_subclass__ <class '__main__.A'> {'name': '张三', 'age': 16}
    __init_subclass__ <class '__main__.B'> {'name': '李四', 'age': 26}
"""

运行结果:

        有时候想控制类的生成过程,怎么办呢?显然可以通过元类的的方式,但是如果场景比较简单,也没必要使用元类。直接使用__init_subclass__即可

示例代码2:

class Test(object):

    def __init_subclass__(cls, **kwargs):
        for k, v in kwargs.items():
            type.__setattr__(cls, k, v)


class A(Test, name="张三", age=16):
    pass


print(A.name)  # 张三
print(A.age)  # 16

运行结果:

        可以看到,在不使用元类的情况下,通过__init_subclass__实现了类的自定义过程。当然这比较简单,也可以实现更复杂的逻辑,在某些场景下,可以替代元类。

示例代码3:

class TestBase(object):
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.default_name = default_name
        print(cls.default_name)


class Test(TestBase, default_name="张三"):
    pass


class NewTest(Test, default_name="李四"):
    pass

运行结果:

         在上述代码3中,当有子类继承了 TestBase类时,那么 __init_subclass__ 就会调用。内容也很简单,父类 TestBase为它的所有子类都设置了default_name 属性。

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

        默认实现 object.__init_subclass__ 不执行任何操作。但默认实现不能传递任何参数,否则报错。
        值得注意的是,示例中的 __init_subclass__ 第一个参数是 cls 而不是常见的 self 。这是因为这个方法隐式地被 @classmethod 装饰。

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

示例代码4:

# defining a SuperClass
class SuperClass(object):

    # 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)

运行结果:

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

        这个__init_subclass__方法看起来很像 Decorator类。但是,如果类装饰器仅影响它们所应用的特定类,则__init_subclass__仅适用于定义该方法的类的未来子类。这意味着可以更改/定义从超类继承的任何新类的行为。

示例代码5:

# defining a SuperClass
class SuperClass(object):

    # 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)
    default_name2 = "SubClass2"
    print(default_name2)


subclass = SubClass()
print(subclass.default_name)
print(subclass.default_name2)

运行结果:

示例代码6:

# defining a SuperClass
class SuperClass(object):
    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)

运行结果:

 有示例代码6中可以得出结论,__init_subclass__ 方法用于改变将来可能创建的子类的行为。

示例代码7:

class Base(object):
    _sub_class = {}
    _base_class_name = 'base_class'

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__()
        cls._sub_class[cls._base_class_name] = cls
        # cls._sub_class[cls._base_class_name] = cls()
        print(cls._sub_class)
        print(cls._base_class_name)
        print("*" * 100)


class SubClass(Base):
    pass


obj = SubClass()
print(obj)
print(obj._sub_class)
print(obj._base_class_name)

运行结果:

示例代码8:

class Base(object):
    _sub_class = {}
    _base_class_name = 'base_class'

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__()
        cls._sub_class[cls._base_class_name] = cls
        # cls._sub_class[cls._base_class_name] = cls()
        print(cls._sub_class)
        print(cls._base_class_name)
        cls._base_class_name = 'I am base!'
        print(cls._base_class_name)
        print("*" * 100)


class SubClass(Base):
    pass


obj = SubClass()
print(obj)
print(obj._sub_class)
print(obj._base_class_name)

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值