首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-03 05:09
  • 4415
  • 12974
blog.csdn.net

目录

一、概述

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、避免过度使用

四、魔法方法

65、__round__方法

65-1、语法

65-2、参数

65-3、功能

65-4、返回值

65-5、说明

65-6、用法

66、__set__方法

66-1、语法

66-2、参数

66-3、功能

66-4、返回值

66-5、说明

66-6、用法

67、__set_name__方法

67-1、语法

67-2、参数

67-3、功能

67-4、返回值

67-5、说明

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

四、魔法方法

65、__round__方法

65-1、语法
  1. __round__(self, ndigits=None)
  2. Rounding an Integral returns itself
  3. Rounding with an ndigits argument also returns an integer
65-2、参数

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

65-2-2、 ndigits(可选): 一个整数,表示要保留的小数位数。如果省略或为None,则进行标准舍入到最接近的整数;否则,ndigits必须是一个整数,表示小数点后要保留的位数。

65-3、功能

       允许你自定义一个对象在调用内置的round()函数时的行为。

65-4、返回值

       返回一个与对象相同类型或可以安全地转换为该类型的值,该值表示四舍五入后的结果。

65-5、说明

        如果ndigits省略或为None,则四舍五入到最接近的整数;如果ndigits为负数,则四舍五入到指定的小数点左侧的位数。

65-6、用法
  1. # 065、__round__方法:
  2. # 1、简单数字舍入
  3. class MyNumber:
  4. def __init__(self, value):
  5. self.value = value
  6. def __round__(self, ndigits=None):
  7. return round(self.value, ndigits)
  8. if __name__ == '__main__':
  9. num = MyNumber(3.14159)
  10. print(round(num, 2)) # 输出: 3.14
  11. # 2、向上舍入
  12. class CeilingNumber:
  13. def __init__(self, value):
  14. self.value = value
  15. def __round__(self, ndigits=None):
  16. import math
  17. return math.ceil(self.value) if ndigits is None else math.ceil(self.value * 10 ** ndigits) / 10 ** ndigits
  18. if __name__ == '__main__':
  19. ceil_num = CeilingNumber(3.11)
  20. print(round(ceil_num, 1)) # 输出: 3.2
  21. # 3、银行舍入(向最接近的偶数舍入)
  22. class FinancialAsset:
  23. def __init__(self, symbol, price):
  24. self.symbol = symbol
  25. self.price = price
  26. def __round__(self, ndigits=2):
  27. """
  28. 自定义舍入方法,对于金融资产价格,我们可能希望使用银行家舍入法
  29. 并保留指定的小数位数。
  30. """
  31. if ndigits < 0:
  32. raise ValueError("Number of digits to round to must be non-negative.")
  33. # 使用decimal模块进行更精确的舍入
  34. from decimal import Decimal, ROUND_HALF_EVEN
  35. # 将float转换为Decimal进行更精确的运算
  36. price_decimal = Decimal(str(self.price))
  37. # 使用银行家舍入法进行舍入
  38. rounded_price = price_decimal.quantize(Decimal(f'1.{"0" * ndigits}'), rounding=ROUND_HALF_EVEN)
  39. # 转换回float(如果需要的话,但通常建议使用Decimal进行金融计算)
  40. return float(rounded_price)
  41. def __repr__(self):
  42. return f"FinancialAsset(symbol={self.symbol}, price={self.price})"
  43. if __name__ == '__main__':
  44. asset = FinancialAsset("AAPL", 172.34567)
  45. print(round(asset, 2)) # 输出: 172.35(假设这是使用银行家舍入法得到的结果)
  46. # 注意:在实际金融应用中,通常会避免使用float来表示货币值,
  47. # 因为float的精度问题可能会导致计算错误。相反,应该使用Decimal类型
  48. # 4、物理量舍入(如温度)
  49. class Temperature:
  50. def __init__(self, value, unit):
  51. self.value = value
  52. self.unit = unit
  53. def __round__(self, ndigits=None):
  54. """
  55. 根据温度的单位来定制舍入行为。
  56. 对于摄氏度,我们可能希望保留一位小数;
  57. 对于华氏度,我们可能希望保留整数。
  58. """
  59. if ndigits is None:
  60. ndigits = 1 if self.unit == 'C' else 0
  61. if ndigits < 0:
  62. raise ValueError("Number of digits to round to must be non-negative.")
  63. return round(self.value, ndigits)
  64. def __repr__(self):
  65. return f"Temperature(value={self.value}, unit={self.unit})"
  66. if __name__ == '__main__':
  67. celsius = Temperature(25.456, 'C')
  68. print(round(celsius)) # 输出: 25.5(摄氏度,保留一位小数)
  69. fahrenheit = Temperature(77.8, 'F')
  70. print(round(fahrenheit, 0)) # 输出: 78.0(华氏度,保留一位小数)
  71. # 也可以指定舍入的小数位数
  72. print(round(celsius, 2)) # 输出: 25.46(摄氏度,保留两位小数)

66、__set__方法

66-1、语法
  1. __set__(self, instance, value, /)
  2. Set an attribute of instance to value
66-2、参数

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

66-2-2、instance(可选):表示拥有描述符的实例对象。

66-2-3、value(必须):表示你想要设置给描述符的新值。

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

66-3、功能

        用于控制当描述符的值被设置时的行为。

66-4、返回值

        在大多数情况下,__set__方法没有返回值,因为它是通过赋值操作调用的,而赋值操作本身不期望有任何返回值(除了可能的异常)。如果你尝试从__set__方法返回一个值,这个值通常会被忽略。

66-5、说明

        无

66-6、用法
  1. # 066、__set__方法:
  2. # 1、限制属性值的范围
  3. class RangeDescriptor:
  4. def __init__(self, name, min_val, max_val):
  5. self.name = name
  6. self.min_val = min_val
  7. self.max_val = max_val
  8. def __set__(self, instance, value):
  9. if self.min_val <= value <= self.max_val:
  10. instance.__dict__[self.name] = value
  11. else:
  12. raise ValueError(f"Value must be between {self.min_val} and {self.max_val}")
  13. def __get__(self, instance, owner):
  14. if instance is None:
  15. return self # 如果实例为None(例如访问类属性而非实例属性时),返回描述符本身
  16. return instance.__dict__.get(self.name, None) # 如果没有设置值,则返回None或默认值
  17. def range_property(min_val, max_val):
  18. def decorator(cls):
  19. name = None
  20. def getter(instance, cls):
  21. nonlocal name
  22. name = '_' + getter.__name__ # 假设我们总是使用以下划线开头的私有属性名
  23. return RangeDescriptor(name, min_val, max_val)
  24. setattr(cls, getter.__name__, property(getter))
  25. return cls
  26. return decorator
  27. class MyClass:
  28. @range_property(1, 10)
  29. def value(self):
  30. pass # 这里实际上不需要任何实现,因为描述符会处理所有的getter和setter逻辑
  31. if __name__ == '__main__':
  32. obj = MyClass()
  33. obj.value = 5
  34. print(obj.value) # 输出:5
  35. try:
  36. obj.value = 15
  37. except ValueError as e:
  38. print(e) # 输出:ValueError: Value must be between 1 and 10
  39. # 2、字符串长度限制描述符
  40. class StringLengthDescriptor:
  41. def __init__(self, max_length):
  42. self.max_length = max_length
  43. self.name = None
  44. def __set_name__(self, owner, name):
  45. self.name = name
  46. def __get__(self, instance, owner):
  47. if instance is None:
  48. return self
  49. return instance.__dict__.get(self.name, '')
  50. def __set__(self, instance, value):
  51. if isinstance(value, str) and len(value) <= self.max_length:
  52. instance.__dict__[self.name] = value
  53. else:
  54. raise ValueError(f"{self.name} must be a string with length less than or equal to {self.max_length}")
  55. class MyAnotherClass:
  56. short_string = StringLengthDescriptor(10)
  57. if __name__ == '__main__':
  58. obj = MyAnotherClass()
  59. obj.short_string = "short"
  60. print(obj.short_string) # 输出:short
  61. try:
  62. obj.short_string = "too_long_string"
  63. except ValueError as e:
  64. print(e) # 输出:short_string must be a string with length less than or equal to 10
  65. # 3、类型检查描述符
  66. class TypeCheckedDescriptor:
  67. def __init__(self, expected_type):
  68. self.expected_type = expected_type
  69. self.name = None
  70. def __set_name__(self, owner, name):
  71. self.name = name
  72. def __get__(self, instance, owner):
  73. if instance is None:
  74. return self
  75. return instance.__dict__.get(self.name, None)
  76. def __set__(self, instance, value):
  77. if isinstance(value, self.expected_type):
  78. instance.__dict__[self.name] = value
  79. else:
  80. raise TypeError(f"{self.name} must be of type {self.expected_type.__name__}")
  81. class MyTypedClass:
  82. typed_value = TypeCheckedDescriptor(int)
  83. if __name__ == '__main__':
  84. obj = MyTypedClass()
  85. obj.typed_value = 10
  86. print(obj.typed_value) # 输出:10
  87. try:
  88. obj.typed_value = "not_an_int"
  89. except TypeError as e:
  90. print(e) # 输出:typed_value must be of type int
  91. # 4、只读描述符
  92. class ReadOnlyDescriptor:
  93. def __init__(self, initial_value):
  94. self.value = initial_value
  95. def __get__(self, instance, owner):
  96. return self.value
  97. def __set__(self, instance, value):
  98. raise AttributeError("This attribute is read-only")
  99. class MyClassWithReadOnly:
  100. read_only_attr = ReadOnlyDescriptor(10)
  101. if __name__ == '__main__':
  102. obj = MyClassWithReadOnly()
  103. print(obj.read_only_attr) # 输出:10
  104. try:
  105. obj.read_only_attr = 20
  106. except AttributeError as e:
  107. print(e) # 输出:This attribute is read-only
  108. # 5、带有默认值的描述符
  109. class DefaultValueDescriptor:
  110. def __init__(self, default_value):
  111. self.default_value = default_value
  112. self.name = None
  113. def __set_name__(self, owner, name):
  114. self.name = name
  115. def __get__(self, instance, owner):
  116. if instance is None:
  117. return self
  118. return instance.__dict__.get(self.name, self.default_value)
  119. def __set__(self, instance, value):
  120. instance.__dict__[self.name] = value
  121. class MyClassWithDefault:
  122. default_value_attr = DefaultValueDescriptor('default')
  123. if __name__ == '__main__':
  124. obj = MyClassWithDefault()
  125. print(obj.default_value_attr) # 输出:'default'
  126. obj.default_value_attr = 'new_value'
  127. print(obj.default_value_attr) # 输出:'new_value'
  128. # 6、属性验证描述符
  129. class ValidatedDescriptor:
  130. def __init__(self, validator):
  131. self.validator = validator
  132. self.name = None
  133. def __set_name__(self, owner, name):
  134. self.name = name
  135. def __get__(self, instance, owner):
  136. if instance is None:
  137. return self
  138. return instance.__dict__.get(self.name, None)
  139. def __set__(self, instance, value):
  140. if self.validator(value):
  141. instance.__dict__[self.name] = value
  142. else:
  143. raise ValueError(f"{self.name} value is not valid")
  144. def is_even(n):
  145. return n % 2 == 0
  146. class MyClassWithValidation:
  147. even_number = ValidatedDescriptor(is_even)
  148. if __name__ == '__main__':
  149. obj = MyClassWithValidation()
  150. obj.even_number = 2
  151. print(obj.even_number) # 输出:2
  152. try:
  153. obj.even_number = 3
  154. except ValueError as e:
  155. print(e) # 输出:even_number value is not valid

67、__set_name__方法

67-1、语法
  1. __set_name__(self, owner, name)
  2. Method to set name of a property
67-2、参数

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

67-2-2、owner(必须):表示将描述符分配给的类对象。

67-2-3、name(必须):表示描述符在owner类中的属性名。

67-3、功能

        为描述符实例提供有关其被分配到的类和属性名的信息。

67-4、返回值

        没有特定的返回值要求,它通常被设计为一个“副作用”方法,即它的主要目的是修改描述符实例的内部状态或执行一些操作,而不是返回任何值。

67-5、说明

        如果你尝试从__set_name__方法返回一个值,那么这个返回值通常会被忽略,因为Python解释器不会对这个返回值做任何特殊处理。

67-6、用法
  1. # 067、__set_name__方法:
  2. # 1、字符串长度限制描述符
  3. class StringLengthDescriptor:
  4. def __init__(self, max_length):
  5. self.max_length = max_length
  6. self.name = None
  7. def __set_name__(self, owner, name):
  8. self.name = name
  9. def __get__(self, instance, owner):
  10. if instance is None:
  11. return self
  12. return instance.__dict__.get(self.name, '')
  13. def __set__(self, instance, value):
  14. if isinstance(value, str) and len(value) <= self.max_length:
  15. instance.__dict__[self.name] = value
  16. else:
  17. raise ValueError(f"{self.name} must be a string with length less than or equal to {self.max_length}")
  18. class MyAnotherClass:
  19. short_string = StringLengthDescriptor(10)
  20. if __name__ == '__main__':
  21. obj = MyAnotherClass()
  22. obj.short_string = "short"
  23. print(obj.short_string) # 输出:short
  24. try:
  25. obj.short_string = "too_long_string"
  26. except ValueError as e:
  27. print(e) # 输出:short_string must be a string with length less than or equal to 10
  28. # 2、类型检查描述符
  29. class TypeCheckedDescriptor:
  30. def __init__(self, expected_type):
  31. self.expected_type = expected_type
  32. self.name = None
  33. def __set_name__(self, owner, name):
  34. self.name = name
  35. def __get__(self, instance, owner):
  36. if instance is None:
  37. return self
  38. return instance.__dict__.get(self.name, None)
  39. def __set__(self, instance, value):
  40. if isinstance(value, self.expected_type):
  41. instance.__dict__[self.name] = value
  42. else:
  43. raise TypeError(f"{self.name} must be of type {self.expected_type.__name__}")
  44. class MyTypedClass:
  45. typed_value = TypeCheckedDescriptor(int)
  46. if __name__ == '__main__':
  47. obj = MyTypedClass()
  48. obj.typed_value = 10
  49. print(obj.typed_value) # 输出:10
  50. try:
  51. obj.typed_value = "not_an_int"
  52. except TypeError as e:
  53. print(e) # 输出:typed_value must be of type int
  54. # 3、带有默认值的描述符
  55. class DefaultValueDescriptor:
  56. def __init__(self, default_value):
  57. self.default_value = default_value
  58. self.name = None
  59. def __set_name__(self, owner, name):
  60. self.name = name
  61. def __get__(self, instance, owner):
  62. if instance is None:
  63. return self
  64. return instance.__dict__.get(self.name, self.default_value)
  65. def __set__(self, instance, value):
  66. instance.__dict__[self.name] = value
  67. class MyClassWithDefault:
  68. default_value_attr = DefaultValueDescriptor('default')
  69. if __name__ == '__main__':
  70. obj = MyClassWithDefault()
  71. print(obj.default_value_attr) # 输出:'default'
  72. obj.default_value_attr = 'new_value'
  73. print(obj.default_value_attr) # 输出:'new_value'
  74. # 4、属性验证描述符
  75. class ValidatedDescriptor:
  76. def __init__(self, validator):
  77. self.validator = validator
  78. self.name = None
  79. def __set_name__(self, owner, name):
  80. self.name = name
  81. def __get__(self, instance, owner):
  82. if instance is None:
  83. return self
  84. return instance.__dict__.get(self.name, None)
  85. def __set__(self, instance, value):
  86. if self.validator(value):
  87. instance.__dict__[self.name] = value
  88. else:
  89. raise ValueError(f"{self.name} value is not valid")
  90. def is_even(n):
  91. return n % 2 == 0
  92. class MyClassWithValidation:
  93. even_number = ValidatedDescriptor(is_even)
  94. if __name__ == '__main__':
  95. obj = MyClassWithValidation()
  96. obj.even_number = 2
  97. print(obj.even_number) # 输出:2
  98. try:
  99. obj.even_number = 3
  100. except ValueError as e:
  101. print(e) # 输出:even_number value is not valid

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

文章知识点与官方知识档案匹配,可进一步学习相关知识
Python入门技能树首页概览425874 人正在系统学习中
遨游码海,我心飞扬
微信名片
注:本文转载自blog.csdn.net的神奇夜光杯的文章"https://myelsa1024.blog.csdn.net/article/details/139509079"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top