首页 > 编程语言 >【python】魔术方法大全(四)——类构建篇

【python】魔术方法大全(四)——类构建篇

时间:2023-10-10 18:55:45浏览次数:45  
标签:__ name python 魔术 MyClass 属性 方法 class 大全

这期我们来聊聊和class建立有关的魔术方法。

__init_subclass__魔术方法

__init_subclass__ 是 Python 3.6 新增的一个特殊方法,用于定义一个类(基类)被继承时所执行的逻辑。当一个类被定义为另一个类(基类)的子类时,它会自动调用 __init_subclass__ 方法。

__init_subclass__ 方法定义在父类中,用于自定义子类的创建过程,可以控制子类的行为。在子类定义时,__init_subclass__ 方法会被自动调用,参数为子类本身。可以通过重载 __init_subclass__ 方法来实现自定义子类的行为。

下面是一个简单的例子,演示了如何使用 __init_subclass__ 方法来实现自定义子类的行为:


class Base:
    def __init_subclass__(cls, **kwargs):
        cls.x = {}
        cls.name = kwargs.get("name")


class A(Base, name="Jack"):
    pass


print(A.x)
print(A.name)

输出结果为:

{}
Jack

可以看到以Base类为基类建立衍生类A,可以看到衍生类的xname属性就被赋值了。

__set_name__魔术方法

__set_name__ 是 Python 3.6 新增的一个特殊方法,用于在类定义时自动设置属性的名称。它是在数据描述符的定义中使用的,用于设置描述符属性的名称。

当定义一个数据描述符时,它通常是作为类中的一个属性来定义的,而属性名就是描述符的名称。在类定义中使用描述符时,Python 会自动调用描述符的 __set_name__ 方法,并将属性名作为参数传递进去。这样,描述符就可以保存属性名并在需要的时候使用。

下面是一个简单的例子,演示了如何在一个数据描述符中使用 __set_name__ 方法来保存属性名:

class MyDescriptor:
    def __set_name__(self, owner, name):
        print(owner, name)
        self.name = name

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(self.name)

    def __set__(self, obj, value):
        obj.__dict__[self.name] = value


class MyClass:
    x = MyDescriptor()

输出结果为:

<class '__main__.MyClass'> x

在上面的例子中,我们定义了一个数据描述符 MyDescriptor,其中的 __set_name__ 方法会在类定义时自动调用,并保存属性名。然后我们定义了一个类 MyClass,并将 MyDescriptor 作为它的属性。当 MyDescriptor 被设置为 MyClass 的属性时,__set_name__ 方法会自动保存属性名到 MyDescriptorname 属性中。

总之,__set_name__ 方法可以让开发者在定义数据描述符时自动保存属性名,从而在需要的时候可以方便地使用。它通常用于实现一些高级的数据描述符,例如访问控制、属性计算等。

__class_getitem__魔术方法

__class_getitem__ 是 Python 3.5 新增的一个特殊方法,用于在定义泛型类型时实现类型参数的协变或逆变。它是用于泛型类型中的类方法或静态方法的。

from typing import List


class A:
    def __class_getitem__(cls, item):
        print(item)
        return "abc"


print(A[0])

if __name__ == '__main__':
    int_arr_type = List[int]  # type hint就是基于__class_getitem__实现的
    list1: int_arr_type = [1]
    list2: int_arr_type = []

输出结果为:

0
abc

在 Python 中,泛型类型可以使用类型变量来代替具体的类型,例如 List[T],其中 T 是一个类型变量,表示列表中的元素类型。在泛型类型中,有时需要使用类型参数的子类型或超类型,这时可以使用 __class_getitem__ 方法来实现。

具体地说,__class_getitem__ 方法会在访问泛型类型的类方法或静态方法时自动调用,并传入泛型类型的参数列表。开发者可以在这个方法中根据参数列表来实现类型参数的协变或逆变。

下面是一个简单的例子,演示了如何在泛型类型中使用 __class_getitem__ 方法来实现类型参数的协变:

class MyGeneric:
    @classmethod
    def from_iterable(cls, iterable):
        return cls(*iterable)

    @staticmethod
    def identity(x):
        return x

    def __class_getitem__(cls, params):
        T, = params
        class NewGeneric(cls):
            @classmethod
            def from_iterable(cls, iterable):
                return cls(*(T(x) for x in iterable))

            @staticmethod
            def identity(x):
                return T(x)
        return NewGeneric

在上面的例子中,我们定义了一个泛型类型 MyGeneric,其中包含一个类方法 from_iterable 和一个静态方法 identity。当访问这些方法时,Python 会自动调用 __class_getitem__ 方法,并传入类型参数列表。

__class_getitem__ 方法中,我们根据类型参数 T 来定义一个新的泛型类型 NewGeneric,它的 from_iterable 方法将输入的可迭代对象转换成 T 类型的元素,而 identity 方法将输入的对象转换成 T 类型的对象。这样,MyGeneric[int] 就可以使用 from_iterableidentity 方法,并将所有输入转换成整数类型。

总之,__class_getitem__ 方法可以让开发者在泛型类型中实现类型参数的协变或逆变,从而更加灵活地处理类型。它通常用于实现一些高级的泛型类型,例如函数式编程中的 Functor、Monad 等。

__mro_entries__魔术方法

__mro_entries__ 不是一个标准的魔术方法,它是 Python 3.7 新增的一个类属性。

在 Python 中,类的继承关系是通过 Method Resolution Order (MRO) 来决定的。__mro_entries__ 类属性允许开发者指定一个自定义的 MRO 列表,以覆盖默认的 MRO 计算规则。

__mro_entries__ 类属性应该是一个元组,每个元素都是一个类。这些类将按照元组的顺序排列,并添加到当前类的 MRO 中。具体来说,Python 会按照以下顺序计算 MRO:

  1. 查找当前类的 __mro_entries__ 类属性,并将其中的类添加到 MRO 中。
  2. 根据 C3 算法(https://en.wikipedia.org/wiki/C3_linearization)计算当前类的基类 MRO,将其添加到 MRO 中。

举个例子,如果一个类定义了 __mro_entries__ = (MyMixin,),那么 MyMixin 将会在该类的所有基类之前被加入到 MRO 中。这个特性对于需要在类继承关系中插入额外的基类时非常有用。

下面是一个使用 __mro_entries__ 属性的例子:

class MyMeta:
    def __mro_entries__(self, bases):
        return (dict,)


class MyClass(MyMeta()):
    pass


print(issubclass(MyClass, MyMeta))
print(MyClass.__mro__)  # 打印 (<class '__main__.MyClass'>, <class 'object'>)

输出结果为:

False
(<class '__main__.MyClass'>, <class 'dict'>, <class 'object'>)

在上面的例子中,定义了一个元类 MyMeta,并在其中定义了 __mro_entries__ 方法,将 MyClass 的 MRO 序列扩展为 (dict,)。因此,MyClass 继承了 dict 类,成为了一个字典对象。
假如我们把上述代码 __mro_entries__返回值修改为以下,则会得到不同的结果。

class MyMeta:
    def __mro_entries__(self, bases):
        return (MyMeta,)


class MyClass(MyMeta()):
    pass


print(issubclass(MyClass, MyMeta))
print(MyClass.__mro__)  

输出结果为:

True
(<class '__main__.MyClass'>, <class '__main__.MyMeta'>, <class 'object'>)

更多详情参考:https://peps.python.org/pep-0560/

__prepare__魔术方法

__prepare__ 方法是 Python 3 中新引入的一个元类方法,它在类定义之前被调用,用于创建用于存储类属性的字典。该方法必须返回一个字典对象,用于存储类属性。

具体来说,当定义一个新的类时,Python 首先会寻找元类并调用它的 __prepare__ 方法。该方法返回的字典会被用来存储类属性。然后 Python 会解析类定义,将属性添加到字典中。

下面是一个简单的示例,演示如何使用 __prepare__ 方法:

import collections


class MyMeta(type):
    @classmethod
    def __prepare__(mcs, name, bases, **kwargs):
        # 创建一个 OrderedDict 对象作为字典,并返回它
        return collections.OrderedDict()


class MyClass(metaclass=MyMeta):
    a = 1
    b = 2
    c = 3


print(MyClass.__dict__)

输出结果为:

{'__module__': '__main__', 'a': 1, 'b': 2, 'c': 3, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}

在这个例子中,我们定义了一个元类 MyMeta,并重载了它的 __prepare__ 方法,用于创建一个 OrderedDict 对象作为类属性的字典。然后,我们定义一个 MyClass 类,并使用 MyMeta 作为它的元类。在 MyClass 中,我们定义了三个类属性 abc。由于我们使用了 OrderedDict 对象作为 MyClass 的字典,因此这些属性将按照定义的顺序存储在字典中。最后,我们打印了 MyClass.__dict__,可以看到这个字典按照我们的期望存储了类属性,并且保持了它们的顺序。

__isinstancecheck____subclasscheck__魔术方法

这两个方法一般也是定义在元类里的。

__isinstancecheck__ 是一个特殊方法,用于自定义对象是否为某个类或其子类的实例。如果定义了这个方法,那么在使用 isinstance() 函数检查对象是否为某个类或其子类的实例时,会调用这个方法,返回值为 TrueFalse

__subclasscheck__ 是一个特殊方法,用于自定义对象是否为某个类的子类。如果定义了这个方法,那么在使用 issubclass() 函数检查对象是否为某个类的子类时,会调用这个方法,返回值为 TrueFalse

下面是一个简单的示例:

class Meta(type):
    def __instancecheck__(self, instance):
        print("Instance Check")
        return True

    def __subclasscheck__(self, subclass):
        print("Subclass Check")
        if subclass is int:
            return True
        return False


class A(metaclass=Meta):
    pass

o = A()

print(isinstance(123, A))

print()

print(issubclass(int, A))

输出结果为:

Instance Check
True

Subclass Check
True

今天介绍的魔术方法都比较偏,我们在日常开发的时候用到的机会也比较少。

本文由mdnice多平台发布

标签:__,name,python,魔术,MyClass,属性,方法,class,大全
From: https://www.cnblogs.com/lianhaifeng/p/17755467.html

相关文章

  • Python并发及网络编程进阶
    案例引入假如你们一家已上市的电商公司,在元旦来临前夕,领导需要你模拟用户,通过接口生成10万笔新订单。你该如何处理?方案探索串行:多个任务逐个执行的过程,上个任务执行完成前,阻塞下一个任务执行。并发:多个任务交替执行的过程,这些任务可能在同一时间段内执行,但是它们的执行时......
  • cpsc 103 python基本前提
    python基本知识基本运算符1.(**)是次方ex:2**3evaluatesto(2tothepowerof3)82.(%)是求余数ex:15%4evaluatestobecausetheremainderwhendividing15by4is33基本运算规则1.str乘以数字ex:'hello'*2evalutesto'hellohello'2.[a......
  • python31days
    组合反射(通过字符串来操作属性)getattrsetattrhasattrdelattr魔术方法(内置方法,双下划线开头的方法)异常———————————————————————————————————......
  • docker搭建pypi服务,实现python包版本永久保存
    1.宿主机上安装htpasswdpipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simplehtpasswd2.使用htpasswd生成账户和密码htpasswd-schtpasswd.txttestuser3.拉取pypiserver镜像dockerpullpypiserver/pypiserver4.使用docker安装pypi容器4.1.创建数据卷docker......
  • 【PyQt6】Python窗口拖拽文件并响应的实现
    情景大概应用情景为:将一个文件拖入到窗口内,地址栏显示文件路径,文本框显示文件内容。说明实现拖拽必须在exec前设置QMineDate,否则不会开始拖拽操作。实现fromPyQt6.QtCoreimport*calssMainWindow(QMainWindow,Ui_MainWindow): def__init__(self): super().__init__......
  • Python 反射
    反射通过字符串映射或修改程序运行时的状态、属性、方法,有以下4个方法。hasattr(obj,name_str)#判断一个对象里是否有对应的name_str字符串的方法映射getattr(obj,name_str)#根据字符串去获取obj对象里的对应的方法的内存地址setattr(obj,"y",z),isequivalent(相当于)(to)......
  • 图解项目延期的原因及解决应对方案大全
    项目管理中经常出现项目延期的情况,面对项目延期的风险如何应对是考验每个项目经理管理水平的时候,很多人纵然是工作多年也没有对延期的种种情况进行总结思考。咱们社区的@小竹对大家常见的项目延期情况及处理方案进行了详细全面的总结,供大家参考。一、监控过程未做好1、明确......
  • 利用Python爬虫打造SEO智能关键词聚合
    在当今互联网的竞争激烈时代,网站的SEO优化至关重要。而关键词是SEO优化的核心,选择恰当的关键词能够带来更多的流量和用户。本文将为您揭秘一项SEO黑科技:如何利用Python爬虫打造智能关键词聚合工具。通过这个工具,您可以快速地扫描和聚合与您网站相关的关键词,为您的SEO优化提供更准确......
  • 使用python 读写excel表格
     1、先导入openpyxl库,如没有先用pip安装(pipinstallopenpyxl)importopenpyxl2.打开表格进行操作excel_file="文件名.xlsx"open_work_book=openpyxl.load_workbook(excel_file)sheet=open_work_book.active3、对表格操作后进行保存sheet.cell(row=row[0].row,col......
  • 【Python深度学习】目标检测和语义分割的区别
    ​        在计算机视觉领域,语义分割和目标检测是两个关键的任务,它们都是对图像和视频进行分析,但它们之间存在着明显的区别。本文将通过图像示例,详细阐述语义分割和目标检测之间的差异。一、基本概念        1.1语义分割(SemanticSegmentation)      ......