当前位置:   article > 正文

Python魔法之旅-魔法方法(18)

Python魔法之旅-魔法方法(18)

目录

一、概述

1、定义

2、作用

二、应用场景

1、构造和析构

2、操作符重载

3、字符串和表示

4、容器管理

5、可调用对象

6、上下文管理

7、属性访问和描述符

8、迭代器和生成器

9、数值类型

10、复制和序列化

11、自定义元类行为

12、自定义类行为

13、类型检查和转换

14、自定义异常

三、学习方法

1、理解基础

2、查阅文档

3、编写示例

4、实践应用

5、阅读他人代码

6、参加社区讨论

7、持续学习

8、练习与总结

9、注意兼容性

10、避免过度使用

四、魔法方法

56、__prepare__方法

56-1、语法

56-2、参数

56-3、功能

56-4、返回值

56-5、说明

56-6、用法

57、__radd__方法

57-1、语法

57-2、参数

57-3、功能

57-4、返回值

57-5、说明

57-6、用法

58、__rdivmod__方法

58-1、语法

58-2、参数

58-3、功能

58-4、返回值

58-5、说明

58-6、用法

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

一、概述

1、定义

        魔法方法(Magic Methods/Special Methods,也称特殊方法或双下划线方法)是Python中一类具有特殊命名规则的方法,它们的名称通常以双下划线(`__`)开头和结尾

        魔法方法用于在特定情况下自动被Python解释器调用,而不需要显式地调用它们,它们提供了一种机制,让你可以定义自定义类时具有与内置类型相似的行为。

2、作用

        魔法方法允许开发者重载Python中的一些内置操作或函数的行为,从而为自定义的类添加特殊的功能

二、应用场景

1、构造和析构

1-1、__init__(self, [args...]):在创建对象时初始化属性。
1-2、__new__(cls, [args...]):在创建对象时控制实例的创建过程(通常与元类一起使用)。
1-3、__del__(self):在对象被销毁前执行清理操作,如关闭文件或释放资源。

2、操作符重载

2-1、__add__(self, other)、__sub__(self, other)、__mul__(self, other)等:自定义对象之间的算术运算。
2-2、__eq__(self, other)、__ne__(self, other)、__lt__(self, other)等:定义对象之间的比较操作。

3、字符串和表示

3-1、__str__(self):定义对象的字符串表示,常用于print()函数。
3-2、__repr__(self):定义对象的官方字符串表示,用于repr()函数和交互式解释器。

4、容器管理

4-1、__getitem__(self, key)、__setitem__(self, key, value)、__delitem__(self, key):用于实现类似列表或字典的索引访问、设置和删除操作。
4-2、__len__(self):返回对象的长度或元素个数。

5、可调用对象

5-1、__call__(self, [args...]):允许对象像函数一样被调用。

6、上下文管理

6-1、__enter__(self)、__exit__(self, exc_type, exc_val, exc_tb):用于实现上下文管理器,如with语句中的对象。

7、属性访问和描述符

7-1、__getattr__, __setattr__, __delattr__:这些方法允许对象在访问或修改不存在的属性时执行自定义操作。
7-2、描述符(Descriptors)是实现了__get__, __set__, 和__delete__方法的对象,它们可以控制对另一个对象属性的访问。

8、迭代器和生成器

8-1、__iter__和__next__:这些方法允许对象支持迭代操作,如使用for循环遍历对象。
8-2、__aiter__, __anext__:这些是异步迭代器的魔法方法,用于支持异步迭代。

9、数值类型

9-1、__int__(self)、__float__(self)、__complex__(self):定义对象到数值类型的转换。
9-2、__index__(self):定义对象用于切片时的整数转换。

10、复制和序列化

10-1、__copy__和__deepcopy__:允许对象支持浅复制和深复制操作。
10-2、__getstate__和__setstate__:用于自定义对象的序列化和反序列化过程。

11、自定义元类行为

11-1、__metaclass__(Python 2)或元类本身(Python 3):允许自定义类的创建过程,如动态创建类、修改类的定义等。

12、自定义类行为

12-1、__init__和__new__:用于初始化对象或控制对象的创建过程。
12-2、__init_subclass__:在子类被创建时调用,允许在子类中执行一些额外的操作。

13、类型检查和转换

13-1、__instancecheck__和__subclasscheck__:用于自定义isinstance()和issubclass()函数的行为。

14、自定义异常

14-1、你可以通过继承内置的Exception类来创建自定义的异常类,并定义其特定的行为。

三、学习方法

        要学好Python的魔法方法,你可以遵循以下方法及步骤:

1、理解基础

        首先确保你对Python的基本语法、数据类型、类和对象等概念有深入的理解,这些是理解魔法方法的基础。

2、查阅文档

        仔细阅读Python官方文档中关于魔法方法的部分,文档会详细解释每个魔法方法的作用、参数和返回值。你可以通过访问Python的官方网站或使用help()函数在Python解释器中查看文档。

3、编写示例

        为每个魔法方法编写简单的示例代码,以便更好地理解其用法和效果,通过实际编写和运行代码,你可以更直观地感受到魔法方法如何改变对象的行为。

4、实践应用

        在实际项目中尝试使用魔法方法。如,你可以创建一个自定义的集合类,使用__getitem__、__setitem__和__delitem__方法来实现索引操作。只有通过实践应用,你才能更深入地理解魔法方法的用途和重要性。

5、阅读他人代码

        阅读开源项目或他人编写的代码,特别是那些使用了魔法方法的代码,这可以帮助你学习如何在实际项目中使用魔法方法。通过分析他人代码中的魔法方法使用方式,你可以学习到一些新的技巧和最佳实践。

6、参加社区讨论

        参与Python社区的讨论,与其他开发者交流关于魔法方法的使用经验和技巧,在社区中提问或回答关于魔法方法的问题,这可以帮助你更深入地理解魔法方法并发现新的应用场景。

7、持续学习

        Python语言和其生态系统不断发展,新的魔法方法和功能可能会不断被引入,保持对Python社区的关注,及时学习新的魔法方法和最佳实践。

8、练习与总结

        多做练习,通过编写各种使用魔法方法的代码来巩固你的理解,定期总结你学到的知识和经验,形成自己的知识体系。

9、注意兼容性

        在使用魔法方法时,要注意不同Python版本之间的兼容性差异,确保你的代码在不同版本的Python中都能正常工作。

10、避免过度使用

        虽然魔法方法非常强大,但过度使用可能会导致代码难以理解和维护,在编写代码时,要权衡使用魔法方法的利弊,避免滥用。

        总之,学好Python的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。

四、魔法方法

56、__prepare__方法

56-1、语法
  1. __prepare__(metacls, name, bases, **kwds)
  2. __prepare__() -> dict
56-2、参数

56-2-1、metacls(必须)一个对元类对象本身的引用。

56-2-2、name(必须)一个字符串,表示新创建的类的名称。

56-2-3、bases(必须)一个元组,包含新类的基类。

56-2-4、 **kwds(可选)一个关键字参数字典,它包含了传递给type()或其他用于创建类的构造函数的任何关键字参数。

56-3、功能

        在类创建之前被调用,用于准备类的命名空间。

56-4、返回值

       返回一个映射对象(如字典),这个对象将被用作新类的命名空间。

56-5、说明

        在类定义体中定义的所有属性、方法等都将被添加到这个命名空间中,通常,你会返回一个普通的字典,但你也可以返回一个具有特殊行为的自定义映射对象。

56-6、用法
  1. # 056、__prepare__方法:
  2. # 示例1
  3. class CustomPrepareMeta(type):
  4. def __prepare__(name, bases, **kwds):
  5. print(f"Preparing namespace for {name}")
  6. # 返回一个自定义的字典子类,用于跟踪属性的添加
  7. class NamespaceDict(dict):
  8. def __setitem__(self, key, value):
  9. print(f"Adding attribute {key} with value {value}")
  10. super().__setitem__(key, value)
  11. return NamespaceDict()
  12. def __new__(metacls, name, bases, namespace, **kwds):
  13. print(f"Creating class {name}")
  14. # 在这里,namespace 已经是 CustomPrepareMeta.__prepare__ 返回的 NamespaceDict 实例
  15. # 所以我们可以直接调用 type 的 __new__ 方法来创建类
  16. return super(CustomPrepareMeta, metacls).__new__(metacls, name, bases, dict(namespace))
  17. # 使用元类 CustomPrepareMeta 来定义 MyClass
  18. class MyClass(metaclass=CustomPrepareMeta):
  19. # 当这些属性被定义时,它们将通过 NamespaceDict 的 __setitem__ 方法被添加
  20. class_attr1 = 'Attribute 1'
  21. class_attr2 = 'Attribute 2'
  22. # 打印类属性
  23. print(MyClass.class_attr1) # 输出: Attribute 1
  24. print(MyClass.class_attr2) # 输出: Attribute 2
  25. # 示例2
  26. class AutoAttrMeta(type):
  27. def __prepare__(name, bases, **kwds):
  28. print(f"Preparing namespace for {name}")
  29. # 返回一个自定义的字典,用于存储类属性
  30. class NamespaceDict(dict):
  31. def __setitem__(self, key, value):
  32. if key == 'base_value':
  33. # 当设置base_value时,同时计算double_value
  34. super().__setitem__(key, value)
  35. self['double_value'] = value * 2
  36. else:
  37. super().__setitem__(key, value)
  38. return NamespaceDict()
  39. def __new__(metacls, name, bases, namespace, **kwds):
  40. # 检查是否已经有double_value,如果没有则根据base_value计算
  41. if 'base_value' in namespace and 'double_value' not in namespace:
  42. namespace['double_value'] = namespace['base_value'] * 2
  43. print(f"Creating class {name}")
  44. return super(AutoAttrMeta, metacls).__new__(metacls, name, bases, namespace)
  45. # 使用元类AutoAttrMeta来定义MyClass
  46. class MyClass(metaclass=AutoAttrMeta):
  47. base_value = 10 # 设置base_value时会自动计算double_value
  48. # 也可以在其他地方手动设置double_value,但这将覆盖自动计算的值
  49. # double_value = 20 # 如果取消注释这行代码,将不会执行自动计算
  50. # 打印类属性
  51. print(MyClass.base_value) # 输出: 10
  52. print(MyClass.double_value) # 输出: 20,因为它是base_value的两倍

57、__radd__方法

57-1、语法
  1. __radd__(self, other, /)
  2. Return other + self # 注意方向
57-2、参数

57-2-1、self(必须)一个对实例对象本身的引用,在类的所有方法中都会自动传递。

57-2-2、 other(必须)表示与self相加的另一个对象。

57-2-3、/(可选)这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

57-3、功能

        用于处理与左侧操作数的加法操作。

57-4、返回值

        返回一个对象,这个对象表示了加法操作的结果。

57-5、说明

        如果__add__和__radd__都没有定义,或者都返回NotImplemented,则加法操作将失败,并可能引发TypeError。

57-6、用法
  1. # 057、__radd__方法:
  2. # 1、自定义数字类型
  3. class MyNumber:
  4. def __init__(self, value):
  5. self.value = value
  6. def __add__(self, other):
  7. if isinstance(other, MyNumber):
  8. return MyNumber(self.value + other.value)
  9. else:
  10. return self.value + other
  11. def __radd__(self, other):
  12. return self + other # 在这里直接使用 __add__
  13. if __name__ == '__main__':
  14. n = MyNumber(5)
  15. print(3 + n) # 输出:8
  16. # 2、字符串和自定义对象的拼接
  17. class CustomString:
  18. def __init__(self, value):
  19. self.value = value
  20. def __str__(self):
  21. return self.value
  22. def __radd__(self, other):
  23. if isinstance(other, str):
  24. return other + self.value
  25. else:
  26. return NotImplemented # 告诉 Python 无法进行该操作
  27. if __name__ == '__main__':
  28. # 使用示例
  29. cs = CustomString("world")
  30. print("Hello " + cs) # 输出:Hello world
  31. # 3、列表和自定义集合的合并
  32. class CustomSet:
  33. def __init__(self, items):
  34. self.items = set(items)
  35. def __add__(self, other):
  36. if isinstance(other, CustomSet):
  37. return CustomSet(self.items.union(other.items))
  38. else:
  39. raise TypeError("unsupported operand type(s) for +: 'CustomSet' and '{}'".format(type(other).__name__))
  40. def __radd__(self, other):
  41. if isinstance(other, list):
  42. return CustomSet(set(other).union(self.items))
  43. else:
  44. return NotImplemented
  45. if __name__ == '__main__':
  46. cs = CustomSet([1, 2, 3])
  47. print([4, 5] + cs) # 输出:类似于<__main__.CustomSet object at 0x0000021608A0E090>

58、__rdivmod__方法

58-1、语法
  1. __rdivmod__(self, other, /)
  2. Return divmod(other, self)
58-2、参数

58-2-1、self(必须)一个对实例对象本身的引用,在类的所有方法中都会自动传递。

58-2-2、 other(必须)表示与self进行除法操作的右侧操作数。

58-2-3、/(可选)这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

58-3、功能

        用于定义当对象作为除法操作的右侧操作数时,同时返回商和余数的行为。

58-4、返回值

        返回一个包含两个元素的元组(tuple),第一个元素是商(整数除法的结果),第二个元素是余数。

58-5、说明

        无

58-6、用法
  1. # 058、__rdivmod__方法:
  2. # 1、自定义分数类
  3. import math
  4. class Fraction:
  5. def __init__(self, numerator, denominator=1):
  6. self.numerator = numerator
  7. self.denominator = denominator
  8. self.reduce()
  9. def reduce(self):
  10. # 简化分数
  11. gcd = math.gcd(self.numerator, self.denominator)
  12. self.numerator //= gcd
  13. self.denominator //= gcd
  14. def __rdivmod__(self, other):
  15. if isinstance(other, int):
  16. # 假设 other 是一个整数,执行逆除法并返回商和余数
  17. # 计算分数的倒数
  18. inverse_fraction = Fraction(self.denominator, self.numerator)
  19. # 计算商
  20. whole_part = (other * inverse_fraction.numerator) // inverse_fraction.denominator
  21. # 计算余数
  22. remainder = other - whole_part * (self.denominator // self.numerator)
  23. # 将余数转化为分数
  24. remainder = Fraction(remainder, self.denominator // self.numerator)
  25. return Fraction(whole_part, 1), remainder
  26. else:
  27. return NotImplemented
  28. if __name__ == '__main__':
  29. f = Fraction(2, 3)
  30. q, r = divmod(45, f) # 这里会调用 f 的 __rdivmod__ 方法
  31. print(q, r) # 输出应该是 67 1/2,因为 45 / (2/3) = 67.5 = 67 + 1/2
  32. # 2、自定义时间单位类
  33. from datetime import timedelta
  34. class TimeUnit:
  35. def __init__(self, seconds):
  36. self.seconds = seconds
  37. def __rdivmod__(self, other):
  38. if isinstance(other, timedelta):
  39. # 假设 other 是一个 timedelta 对象,我们将其转换为秒,然后执行逆除法
  40. total_seconds = other.total_seconds()
  41. quotient = total_seconds // self.seconds
  42. remainder = timedelta(seconds=total_seconds % self.seconds)
  43. return quotient, remainder
  44. else:
  45. return NotImplemented
  46. if __name__ == '__main__':
  47. tu = TimeUnit(60) # 表示1分钟
  48. td = timedelta(hours=1, minutes=30) # 表示1小时30分钟
  49. q, r = divmod(td, tu) # 这里会调用 tu 的 __rdivmod__ 方法
  50. print(q, r) # 输出: 90 0:00:00(90个1分钟的时间单位,余数为0)

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号