赞
踩
目录
魔法方法(Magic Methods/Special Methods,也称特殊方法或双下划线方法)是Python中一类具有特殊命名规则的方法,它们的名称通常以双下划线(`__`)开头和结尾。
魔法方法用于在特定情况下自动被Python解释器调用,而不需要显式地调用它们,它们提供了一种机制,让你可以定义自定义类时具有与内置类型相似的行为。
魔法方法允许开发者重载Python中的一些内置操作或函数的行为,从而为自定义的类添加特殊的功能。
1-1、__init__(self, [args...]):在创建对象时初始化属性。
1-2、__new__(cls, [args...]):在创建对象时控制实例的创建过程(通常与元类一起使用)。
1-3、__del__(self):在对象被销毁前执行清理操作,如关闭文件或释放资源。
2-1、__add__(self, other)、__sub__(self, other)、__mul__(self, other)等:自定义对象之间的算术运算。
2-2、__eq__(self, other)、__ne__(self, other)、__lt__(self, other)等:定义对象之间的比较操作。
3-1、__str__(self):定义对象的字符串表示,常用于print()函数。
3-2、__repr__(self):定义对象的官方字符串表示,用于repr()函数和交互式解释器。
4-1、__getitem__(self, key)、__setitem__(self, key, value)、__delitem__(self, key):用于实现类似列表或字典的索引访问、设置和删除操作。
4-2、__len__(self):返回对象的长度或元素个数。
5-1、__call__(self, [args...]):允许对象像函数一样被调用。
6-1、__enter__(self)、__exit__(self, exc_type, exc_val, exc_tb):用于实现上下文管理器,如with语句中的对象。
7-1、__getattr__, __setattr__, __delattr__:这些方法允许对象在访问或修改不存在的属性时执行自定义操作。
7-2、描述符(Descriptors)是实现了__get__, __set__, 和__delete__方法的对象,它们可以控制对另一个对象属性的访问。
8-1、__iter__和__next__:这些方法允许对象支持迭代操作,如使用for循环遍历对象。
8-2、__aiter__, __anext__:这些是异步迭代器的魔法方法,用于支持异步迭代。
9-1、__int__(self)、__float__(self)、__complex__(self):定义对象到数值类型的转换。
9-2、__index__(self):定义对象用于切片时的整数转换。
10-1、__copy__和__deepcopy__:允许对象支持浅复制和深复制操作。
10-2、__getstate__和__setstate__:用于自定义对象的序列化和反序列化过程。
11-1、__metaclass__(Python 2)或元类本身(Python 3):允许自定义类的创建过程,如动态创建类、修改类的定义等。
12-1、__init__和__new__:用于初始化对象或控制对象的创建过程。
12-2、__init_subclass__:在子类被创建时调用,允许在子类中执行一些额外的操作。
13-1、__instancecheck__和__subclasscheck__:用于自定义isinstance()和issubclass()函数的行为。
14-1、你可以通过继承内置的Exception类来创建自定义的异常类,并定义其特定的行为。
要学好Python的魔法方法,你可以遵循以下方法及步骤:
首先确保你对Python的基本语法、数据类型、类和对象等概念有深入的理解,这些是理解魔法方法的基础。
仔细阅读Python官方文档中关于魔法方法的部分,文档会详细解释每个魔法方法的作用、参数和返回值。你可以通过访问Python的官方网站或使用help()函数在Python解释器中查看文档。
为每个魔法方法编写简单的示例代码,以便更好地理解其用法和效果,通过实际编写和运行代码,你可以更直观地感受到魔法方法如何改变对象的行为。
在实际项目中尝试使用魔法方法。如,你可以创建一个自定义的集合类,使用__getitem__、__setitem__和__delitem__方法来实现索引操作。只有通过实践应用,你才能更深入地理解魔法方法的用途和重要性。
阅读开源项目或他人编写的代码,特别是那些使用了魔法方法的代码,这可以帮助你学习如何在实际项目中使用魔法方法。通过分析他人代码中的魔法方法使用方式,你可以学习到一些新的技巧和最佳实践。
参与Python社区的讨论,与其他开发者交流关于魔法方法的使用经验和技巧,在社区中提问或回答关于魔法方法的问题,这可以帮助你更深入地理解魔法方法并发现新的应用场景。
Python语言和其生态系统不断发展,新的魔法方法和功能可能会不断被引入,保持对Python社区的关注,及时学习新的魔法方法和最佳实践。
多做练习,通过编写各种使用魔法方法的代码来巩固你的理解,定期总结你学到的知识和经验,形成自己的知识体系。
在使用魔法方法时,要注意不同Python版本之间的兼容性差异,确保你的代码在不同版本的Python中都能正常工作。
虽然魔法方法非常强大,但过度使用可能会导致代码难以理解和维护,在编写代码时,要权衡使用魔法方法的利弊,避免滥用。
总之,学好Python的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。
- __prepare__(metacls, name, bases, **kwds)
- __prepare__() -> dict
56-2-1、metacls(必须):一个对元类对象本身的引用。
56-2-2、name(必须):一个字符串,表示新创建的类的名称。
56-2-3、bases(必须):一个元组,包含新类的基类。
56-2-4、 **kwds(可选):一个关键字参数字典,它包含了传递给type()或其他用于创建类的构造函数的任何关键字参数。
在类创建之前被调用,用于准备类的命名空间。
返回一个映射对象(如字典),这个对象将被用作新类的命名空间。
在类定义体中定义的所有属性、方法等都将被添加到这个命名空间中,通常,你会返回一个普通的字典,但你也可以返回一个具有特殊行为的自定义映射对象。
- # 056、__prepare__方法:
- # 示例1
- class CustomPrepareMeta(type):
- def __prepare__(name, bases, **kwds):
- print(f"Preparing namespace for {name}")
- # 返回一个自定义的字典子类,用于跟踪属性的添加
- class NamespaceDict(dict):
- def __setitem__(self, key, value):
- print(f"Adding attribute {key} with value {value}")
- super().__setitem__(key, value)
- return NamespaceDict()
- def __new__(metacls, name, bases, namespace, **kwds):
- print(f"Creating class {name}")
- # 在这里,namespace 已经是 CustomPrepareMeta.__prepare__ 返回的 NamespaceDict 实例
- # 所以我们可以直接调用 type 的 __new__ 方法来创建类
- return super(CustomPrepareMeta, metacls).__new__(metacls, name, bases, dict(namespace))
- # 使用元类 CustomPrepareMeta 来定义 MyClass
- class MyClass(metaclass=CustomPrepareMeta):
- # 当这些属性被定义时,它们将通过 NamespaceDict 的 __setitem__ 方法被添加
- class_attr1 = 'Attribute 1'
- class_attr2 = 'Attribute 2'
- # 打印类属性
- print(MyClass.class_attr1) # 输出: Attribute 1
- print(MyClass.class_attr2) # 输出: Attribute 2
-
- # 示例2
- class AutoAttrMeta(type):
- def __prepare__(name, bases, **kwds):
- print(f"Preparing namespace for {name}")
- # 返回一个自定义的字典,用于存储类属性
- class NamespaceDict(dict):
- def __setitem__(self, key, value):
- if key == 'base_value':
- # 当设置base_value时,同时计算double_value
- super().__setitem__(key, value)
- self['double_value'] = value * 2
- else:
- super().__setitem__(key, value)
- return NamespaceDict()
- def __new__(metacls, name, bases, namespace, **kwds):
- # 检查是否已经有double_value,如果没有则根据base_value计算
- if 'base_value' in namespace and 'double_value' not in namespace:
- namespace['double_value'] = namespace['base_value'] * 2
- print(f"Creating class {name}")
- return super(AutoAttrMeta, metacls).__new__(metacls, name, bases, namespace)
- # 使用元类AutoAttrMeta来定义MyClass
- class MyClass(metaclass=AutoAttrMeta):
- base_value = 10 # 设置base_value时会自动计算double_value
- # 也可以在其他地方手动设置double_value,但这将覆盖自动计算的值
- # double_value = 20 # 如果取消注释这行代码,将不会执行自动计算
- # 打印类属性
- print(MyClass.base_value) # 输出: 10
- print(MyClass.double_value) # 输出: 20,因为它是base_value的两倍

- __radd__(self, other, /)
- Return other + self # 注意方向
57-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
57-2-2、 other(必须):表示与self相加的另一个对象。
57-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
用于处理与左侧操作数的加法操作。
返回一个对象,这个对象表示了加法操作的结果。
如果__add__和__radd__都没有定义,或者都返回NotImplemented,则加法操作将失败,并可能引发TypeError。
- # 057、__radd__方法:
- # 1、自定义数字类型
- class MyNumber:
- def __init__(self, value):
- self.value = value
- def __add__(self, other):
- if isinstance(other, MyNumber):
- return MyNumber(self.value + other.value)
- else:
- return self.value + other
- def __radd__(self, other):
- return self + other # 在这里直接使用 __add__
- if __name__ == '__main__':
- n = MyNumber(5)
- print(3 + n) # 输出:8
-
- # 2、字符串和自定义对象的拼接
- class CustomString:
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return self.value
- def __radd__(self, other):
- if isinstance(other, str):
- return other + self.value
- else:
- return NotImplemented # 告诉 Python 无法进行该操作
- if __name__ == '__main__':
- # 使用示例
- cs = CustomString("world")
- print("Hello " + cs) # 输出:Hello world
-
- # 3、列表和自定义集合的合并
- class CustomSet:
- def __init__(self, items):
- self.items = set(items)
- def __add__(self, other):
- if isinstance(other, CustomSet):
- return CustomSet(self.items.union(other.items))
- else:
- raise TypeError("unsupported operand type(s) for +: 'CustomSet' and '{}'".format(type(other).__name__))
- def __radd__(self, other):
- if isinstance(other, list):
- return CustomSet(set(other).union(self.items))
- else:
- return NotImplemented
- if __name__ == '__main__':
- cs = CustomSet([1, 2, 3])
- print([4, 5] + cs) # 输出:类似于<__main__.CustomSet object at 0x0000021608A0E090>

- __rdivmod__(self, other, /)
- Return divmod(other, self)
58-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
58-2-2、 other(必须):表示与self进行除法操作的右侧操作数。
58-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
用于定义当对象作为除法操作的右侧操作数时,同时返回商和余数的行为。
返回一个包含两个元素的元组(tuple),第一个元素是商(整数除法的结果),第二个元素是余数。
无
- # 058、__rdivmod__方法:
- # 1、自定义分数类
- import math
- class Fraction:
- def __init__(self, numerator, denominator=1):
- self.numerator = numerator
- self.denominator = denominator
- self.reduce()
- def reduce(self):
- # 简化分数
- gcd = math.gcd(self.numerator, self.denominator)
- self.numerator //= gcd
- self.denominator //= gcd
- def __rdivmod__(self, other):
- if isinstance(other, int):
- # 假设 other 是一个整数,执行逆除法并返回商和余数
- # 计算分数的倒数
- inverse_fraction = Fraction(self.denominator, self.numerator)
- # 计算商
- whole_part = (other * inverse_fraction.numerator) // inverse_fraction.denominator
- # 计算余数
- remainder = other - whole_part * (self.denominator // self.numerator)
- # 将余数转化为分数
- remainder = Fraction(remainder, self.denominator // self.numerator)
- return Fraction(whole_part, 1), remainder
- else:
- return NotImplemented
- if __name__ == '__main__':
- f = Fraction(2, 3)
- q, r = divmod(45, f) # 这里会调用 f 的 __rdivmod__ 方法
- print(q, r) # 输出应该是 67 1/2,因为 45 / (2/3) = 67.5 = 67 + 1/2
-
- # 2、自定义时间单位类
- from datetime import timedelta
- class TimeUnit:
- def __init__(self, seconds):
- self.seconds = seconds
- def __rdivmod__(self, other):
- if isinstance(other, timedelta):
- # 假设 other 是一个 timedelta 对象,我们将其转换为秒,然后执行逆除法
- total_seconds = other.total_seconds()
- quotient = total_seconds // self.seconds
- remainder = timedelta(seconds=total_seconds % self.seconds)
- return quotient, remainder
- else:
- return NotImplemented
- if __name__ == '__main__':
- tu = TimeUnit(60) # 表示1分钟
- td = timedelta(hours=1, minutes=30) # 表示1小时30分钟
- q, r = divmod(td, tu) # 这里会调用 tu 的 __rdivmod__ 方法
- print(q, r) # 输出: 90 0:00:00(90个1分钟的时间单位,余数为0)

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。