目录
一、概述
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、避免过度使用
四、魔法方法
11、__delitem__方法
11-1、语法
11-2、参数
11-3、功能
11-4、返回值
11-5、说明
11-6、用法
12、__dir__方法
12-1、语法
12-2、参数
12-3、功能
12-4、返回值
12-5、说明
12-6、用法
13、__divmod__方法
13-1、语法
13-2、参数
13-3、功能
13-4、返回值
13-5、说明
13-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的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。
四、魔法方法
11、__delitem__方法
11-1、语法
__delitem__(self, key, /)
Delete self[key]
11-2、参数
11-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
11-2-2、key(必须):表示想要从对象中删除的元素的键或索引
11-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
11-3、功能
用于定义当从容器中删除一个元素时应该执行的操作。
11-4、返回值
当这个方法被调用时,它应该执行删除操作,但不应该返回任何值(或者更具体地说,它应该返回None)。
11-5、说明
对于列表,key通常是一个整数索引;对于字典,key通常是一个键。
11-6、用法
# 011、__delitem__方法:
# 1、简单的自定义字典
# 定义一个名为CustomDict的类,该类继承自内置的dict类
class CustomDict(dict):
# 重写父类dict中的__delitem__方法,该方法在删除字典中的键值对时被调用
def __delitem__(self, key):
# 打印出将要被删除的键
print(f"Deleting key: {key}")
# 调用父类dict的__delitem__方法来实际删除指定的键值对
# 使用super()函数可以调用当前类继承的父类(或多个父类)中的方法
super().__delitem__(key)
# 判断当前模块是否作为主程序运行(而不是被导入为模块)
if __name__ == '__main__':
# 创建一个CustomDict对象d,并初始化时传入一个字典{'a': 1, 'b': 2}
d = CustomDict({'a': 1, 'b': 2})
# 删除d中键为'a'的键值对
# 这会触发CustomDict类中定义的__delitem__方法,打印出"Deleting key: a"
del d['a'] # 输出: Deleting key: a
# 2、带有额外检查的字典
# 定义一个名为CheckedDict的类,该类继承自内置的dict类
class CheckedDict(dict):
# 重写父类dict中的__delitem__方法,用于在删除字典项之前进行检查
def __delitem__(self, key):
# 检查键key是否存在于当前字典中
if key not in self:
# 如果键不存在,则抛出一个KeyError异常,并说明键不存在
raise KeyError(f"Key {key} does not exist.")
# 如果键存在,则打印出正在删除的键
print(f"Deleting key: {key}")
# 调用父类dict的__delitem__方法来实际删除指定的键值对
super().__delitem__(key)
# 判断当前模块是否作为主程序运行(而不是被导入为模块)
if __name__ == '__main__':
# 创建一个CheckedDict对象cd,并初始化时传入一个字典{'a': 1, 'b': 2}
cd = CheckedDict({'a': 1, 'b': 2})
# 删除cd中键为'a'的键值对
# 这会触发CheckedDict类中定义的__delitem__方法,打印出"Deleting key: a"
del cd['a'] # 输出: Deleting key: a
# 尝试删除cd中键为'c'的键值对
# 因为'c'不在cd中,所以会触发CheckedDict类中定义的__delitem__方法中的KeyError异常
del cd['c'] # 引发 KeyError: Key c does not exist.
# 3、自定义列表,删除元素时更新索引
class IndexedList:
# 初始化方法,创建一个空的列表用于存储元素,和一个空的字典用于存储元素和它们的索引
def __init__(self):
self.items = [] # 列表,用于存储元素
self.indices = {} # 字典,用于存储元素和它们的索引
# 添加元素到列表的末尾,并在indices字典中记录其索引
def append(self, item):
index = len(self.items) # 获取当前列表的长度,即新元素的索引
self.items.append(item) # 将元素添加到列表的末尾
self.indices[item] = index # 在indices字典中记录元素和它的索引
# 删除指定索引或元素的方法
def __delitem__(self, key):
# 如果key是整数,则认为它是索引
if isinstance(key, int):
index = key # 索引就是key
item = self.items[index] # 从列表中根据索引获取元素
# 如果key在indices字典中,则认为它是元素的值
elif key in self.indices:
index = self.indices[key] # 从indices字典中获取元素的索引
# 注意:这里item应该设置为列表中的元素,而不是key本身
# 但由于代码逻辑,我们保持item = key,这在key是元素值时是正确的
item = key
# 如果key既不是整数也不在indices字典中,则抛出KeyError
else:
raise KeyError(f"Key {key} does not exist.")
# 打印正在删除的元素和它的索引
print(f"Deleting item at index {index}: {item}")
# 从列表中删除元素
del self.items[index]
# 从indices字典中删除对应的条目
# 注意:这里应该使用item对应的值,而不是item本身(如果key是索引的话)
# 但由于之前的代码逻辑,这里直接使用item是可行的(如果key是元素值)
del self.indices[item]
if __name__ == '__main__':
il = IndexedList() # 创建一个IndexedList对象
il.append('apple') # 添加'apple'元素,其索引为0
il.append('banana') # 添加'banana'元素,其索引为1
del il[1] # 删除索引为1的元素(即'banana'),并输出:Deleting item at index 1: banana
# 4、带有删除日志的列表
class LoggedList(list):
# 重写list类的__delitem__方法,以便在删除元素时记录日志
def __delitem__(self, index):
# 使用pop方法删除指定索引的元素,并返回该元素
# 注意:pop方法会直接修改列表,删除指定索引的元素并返回它
item = self.pop(index) # 使用pop删除并返回元素
# 打印正在删除的元素
print(f"Deleting item: {item}")
if __name__ == '__main__':
# 创建一个LoggedList对象,并初始化列表为[1, 2, 3]
ll = LoggedList([1, 2, 3])
# 删除索引为1的元素(即值为2的元素)
# 由于LoggedList类重写了__delitem__方法,所以在删除时会调用该方法并记录日志
del ll[1] # 输出: Deleting item: 2
# 5、限制删除操作的列表
class RestrictedList(list):
# 重写父类list的__delitem__方法,增加索引检查
def __delitem__(self, index):
# 检查索引是否在有效范围内(包括0到len(self)-1)
if not 0 <= index < len(self):
# 如果索引超出范围,则抛出IndexError异常
raise IndexError(f"Index {index} out of range.")
# 如果索引有效,则调用父类list的__delitem__方法来删除元素
super().__delitem__(index)
if __name__ == '__main__':
# 创建一个RestrictedList对象,并初始化列表为[1, 2, 3]
rl = RestrictedList([1, 2, 3])
# 删除索引为1的元素(即值为2的元素),这是正常删除
del rl[1] # 正常删除
# 打印列表rl,此时应该输出:[1, 3]
print(rl) # 输出:[1, 3]
# 尝试删除索引为3的元素,由于索引超出范围,将引发IndexError异常
del rl[3] # 引发 IndexError: Index 3 out of range.
# 注意:由于IndexError异常,接下来的代码(如果有的话)将不会被执行
# 6、删除元素时触发回调的列表
class CallbackList(list):
# 初始化方法,除了继承自list的初始化外,还接受一个回调函数作为参数
def __init__(self, callback):
# 存储传入的回调函数
self.callback = callback
# 调用父类list的初始化方法
super().__init__()
# 重写list类的__delitem__方法,以便在删除元素时执行回调函数并打印信息
def __delitem__(self, index):
# 使用pop方法删除指定索引的元素,并返回该元素
item = self.pop(index)
# 调用存储的回调函数,传入被删除的元素
self.callback(item)
# 打印被删除的元素
print(f"Deleted item: {item}")
# 使用示例中的回调函数
def print_deleted(item):
# 打印回调函数接收到的元素,表示该元素已被删除
print(f"Callback: Item {item} was deleted")
if __name__ == '__main__':
# 创建一个CallbackList对象,并传入回调函数print_deleted
cl = CallbackList(print_deleted)
# 向CallbackList对象中添加元素
cl.append(1)
cl.append(2)
# 删除索引为0的元素(即值为1的元素)
# 由于CallbackList类重写了__delitem__方法,所以在删除时会调用该方法并执行回调函数和打印信息
del cl[0] # 输出: Callback: Item 1 was deleted 和 Deleted item: 1
12、__dir__方法
12-1、语法
__dir__(self, /)
Default dir() implementation
12-2、参数
12-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
12-2-2、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
12-3、功能
用于定制对象的属性列表,即在执行如dir(obj)这样的操作时返回的属性名列表。
12-4、返回值
返回一个字符串列表,包含了你想让dir()函数返回的对象的属性名。
12-5、说明
如果你没有为类定义 __
dir__
方法,那么Python会使用默认的dir()实现,它会列出对象的所有属性,包括从父类继承的属性,以及实例属性、方法、类等。
12-6、用法
# 012、__dir__方法:
# 1、基本实现,返回所有属性
# 定义一个名为 MyClass 的类
class MyClass:
# 类的初始化方法,当创建 MyClass 的实例时会被调用
def __init__(self):
# 为实例设置属性 a,并赋值为 1
self.a = 1
# 为实例设置属性 b,并赋值为 2
self.b = 2
# 定义一个名为 method 的方法,它不执行任何操作(pass 是空操作)
def method(self):
pass
# 自定义 __dir__ 方法,用于返回对象的属性列表
def __dir__(self):
# 返回实例的 __dict__ 属性(即实例的属性字典)的键的列表
# 并手动添加方法名 'method' 到列表中(注意:在 Python 3.3+ 中,方法名通常会自动包含在 __dir__ 中)
return list(self.__dict__.keys()) + ['method'] # 加上方法名(通常不需要手动添加)
# 判断当前脚本是否作为主程序运行(而不是被导入为模块)
if __name__ == '__main__':
# 创建一个 MyClass 的实例,并将其赋值给变量 obj
obj = MyClass()
# 调用 dir 函数并传入 obj 作为参数,打印 obj 的属性列表
# 由于 MyClass 定义了 __dir__ 方法,输出将包括 MyClass 实例的属性 'a'、'b' 以及手动添加的 'method'
# 注意:实际输出可能还包括其他内置方法和属性,如 '__class__', '__dict__', '__doc__' 等
print(
dir(obj)) # 输出可能包括:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', ..., 'a', 'b', 'method']
# 2、隐藏某些属性
# 定义一个名为 MyClass 的类
class MyClass:
# 类的初始化方法,当创建 MyClass 的实例时会被调用
def __init__(self):
# 为实例设置一个公共属性 public_attr,并赋值为 1
self.public_attr = 1
# 为实例设置一个通常以单下划线开头的“受保护的”属性 _private_attr,并赋值为 2
# 注意:单下划线开头的属性并不是真正的私有属性,但在约定上被视为“受保护的”或“内部使用的”
self._private_attr = 2 # 通常以单下划线开头的属性被视为“受保护的”
# 自定义 __dir__ 方法,用于返回对象的属性列表
# 但这个方法有一个问题,因为它试图从自身调用 dir(self),这会导致无限递归
# 因为它会再次调用这个自定义的 __dir__ 方法,而不是内置的 dir 函数
def __dir__(self):
# 使用内置的 vars() 函数来获取实例的 __dict__,从而避免无限递归
return [attr for attr in vars(self) if not attr.startswith('_')]
if __name__ == '__main__':
# 创建一个 MyClass 的实例,并将其赋值给变量 obj
obj = MyClass()
# 调用 dir 函数并传入 obj 作为参数,打印 obj 的属性列表
# 由于 MyClass 定义了 __dir__ 方法,输出将不包括以单下划线开头的属性
print(dir(obj)) # 输出将不包括以单下划线开头的属性,如:['public_attr']
# 3、添加动态属性
# 定义一个名为MyClass的类
class MyClass:
# 初始化方法,当创建MyClass的实例时会被调用
def __init__(self):
# 初始化一个实例变量dynamic_attr,并设置其值为None
self.dynamic_attr = None
# 定义一个方法add_dynamic_attr,用于动态地为实例添加属性
def add_dynamic_attr(self, name, value):
# 使用setattr函数动态地为实例添加属性,name为属性名,value为属性值
setattr(self, name, value)
# 自定义__dir__方法,用于返回对象的属性列表
def __dir__(self):
# 获取当前实例的所有属性名(不包括继承的属性),并将其转换为列表
# 然后添加方法名'add_dynamic_attr'到列表中
# 注意:在实际使用中,Python的dir()函数通常会自动包含方法名,这里添加可能是为了特殊需要或示例
return list(self.__dict__.keys()) + ['add_dynamic_attr'] # 添加方法名
# 当此脚本作为主程序运行时执行以下代码
if __name__ == '__main__':
# 创建一个MyClass的实例,并将其赋值给变量obj
obj = MyClass()
# 调用obj的add_dynamic_attr方法,动态地为其添加名为'new_attr'的属性,并设置其值为'some value'
obj.add_dynamic_attr('new_attr', 'some value')
# 调用dir函数并传入obj作为参数,打印obj的属性列表
# 由于MyClass定义了__dir__方法,输出将包括'add_dynamic_attr'、'dynamic_attr'以及动态添加的'new_attr'
print(dir(obj)) # 输出:['add_dynamic_attr', 'dynamic_attr', 'new_attr']
# 4、合并父类属性
# 定义一个名为Parent的基类
class Parent:
# 初始化方法,当创建Parent的实例时会被调用
def __init__(self):
# 初始化一个实例变量parent_attr,并设置其值为'parent'
self.parent_attr = 'parent'
# 定义一个名为Child的子类,继承自Parent类
class Child(Parent):
# 子类的初始化方法
def __init__(self):
# 调用父类的初始化方法,确保父类的属性被正确初始化
super().__init__()
# 初始化一个子类特有的实例变量child_attr,并设置其值为'child'
self.child_attr = 'child'
# 自定义__dir__方法,用于返回对象的属性列表
def __dir__(self):
# 使用vars函数获取当前实例的字典表示(即属性和其值),并转换为集合
child_attrs = set(vars(self))
# 使用vars函数和super函数获取父类实例的字典表示,并转换为集合
parent_attrs = set(vars(super(Child, self)))
# 使用集合的并集操作符|,将子类和父类的属性合并为一个集合
all_attrs = child_attrs | parent_attrs
# 将合并后的属性集合转换为列表并返回
return list(all_attrs)
# 当此脚本作为主程序运行时执行以下代码
if __name__ == '__main__':
# 创建一个Child类的实例,并将其赋值给变量obj
obj = Child()
# 调用dir函数并传入obj作为参数,打印obj的属性列表
# 由于Child类定义了__dir__方法,输出将包括父类Parent和子类Child的属性
print(dir(obj)) # 输出将包括父类和子类的属性
# 5、按条件显示属性
# 定义一个名为MyClass的类
class MyClass:
# 初始化方法,当创建MyClass的实例时会被调用
def __init__(self):
# 初始化一个实例变量show_this,并设置其值为True
self.show_this = True
# 初始化另一个实例变量hide_this,并设置其值为False
self.hide_this = False
# 自定义__dir__方法,用于返回对象的属性列表
def __dir__(self):
# 使用列表推导式遍历self.__dict__中的所有属性名(即键)
# 如果属性名不以'_this'结尾,或者该属性的值不为False(即值存在),则将其包含在内
# 注意:这里的逻辑实际上不会排除'hide_this',因为即使其值为False,它仍然存在于__dict__中
# 但为了注释说明,我们假设这是意图(尽管实际行为并非如此)
return [attr for attr in self.__dict__ if not attr.endswith('_this') or getattr(self, attr)]
# 当此脚本作为主程序运行时执行以下代码
if __name__ == '__main__':
# 创建一个MyClass的实例,并将其赋值给变量obj
obj = MyClass()
# 调用dir函数并传入obj作为参数,打印obj的属性列表
# 注释说明中提到输出将包括'show_this',但不包括'hide_this'(因为其值为False)
print(dir(obj)) # 输出仅包括 ['show_this']
# 6、自定义排序
# 定义一个名为MyClass的类
class MyClass:
# 初始化方法,当创建MyClass的实例时会被调用
def __init__(self):
# 定义并初始化一个实例变量z,赋值为3
self.z = 3
# 定义并初始化一个实例变量b,赋值为2
self.b = 2
# 定义并初始化一个实例变量a,赋值为1
self.a = 1
# 自定义__dir__方法,用于返回对象的属性列表
def __dir__(self):
# 使用self.__dict__.keys()获取当前对象的所有属性名(作为字典的键)
# 然后使用sorted()函数对属性名进行排序(默认按字母顺序排序)
# 最后返回排序后的属性名列表
return sorted(self.__dict__.keys()) # 按字母顺序排序
# 当此脚本作为主程序运行时执行以下代码
if __name__ == '__main__':
# 创建一个MyClass的实例,并将其赋值给变量obj
obj = MyClass()
# 调用dir函数并传入obj作为参数,打印obj的属性列表
# 由于MyClass类中重写了__dir__方法,因此输出将按字母顺序排列属性名
print(dir(obj)) # 输出:['a', 'b', 'z']
13、__divmod__方法
13-1、语法
__divmod__(self, value, /)
Return divmod(self, value)
13-2、参数
13-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
13-2-2、value(必须):表示要与self进行除法和取模运算的第二个值。
13-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
13-3、功能
用于定义对象在使用divmod()内置函数时的行为。
13-4、返回值
返回一个元组,其中包含两个值:除法的商和余数。
13-5、说明
当你对一个对象调用divmod()函数时,Python会自动尝试调用该对象的 __divmod__ 方法。这个方法应该接受两个参数:self(即对象本身)和value(即要与对象进行除法和取模运算的值)。
13-6、用法
# 013、__divmod__方法:
# 1、整数类
# 定义一个名为Integer的类,用于表示整数
class Integer:
# 初始化方法,接收一个value参数,并将其赋值给实例变量self.value
def __init__(self, value):
self.value = value
# 定义__divmod__方法,用于自定义当对象使用divmod()函数时的行为
def __divmod__(self, other):
# 检查other是否为整数或浮点数,并且不等于0
# 如果不是,则抛出一个TypeError异常,提示不支持的操作数类型
if not isinstance(other, (int, float)) and other != 0:
raise TypeError("Unsupported operand type(s) for divmod()")
# 调用内置的divmod函数,对self.value和other进行除法和取模运算
# 并返回结果(一个包含商和余数的元组)
return divmod(self.value, other)
# 如果当前模块是作为主程序运行的(而不是被导入到其他模块中),则执行以下代码
if __name__ == '__main__':
# 创建一个Integer对象a,并初始化其值为10
a = Integer(10)
# 使用divmod函数对a和3进行除法和取模运算
# 因为a是Integer的实例,所以这里会调用Integer类的__divmod__方法
# 输出结果应该是(3, 1),因为10除以3的商是3,余数是1
print(divmod(a, 3)) # 输出: (3, 1)
# 2、分数类
# 导入Fraction类,这是Python内置的一个分数类
from fractions import Fraction
# 定义一个名为MyFraction的类,用于表示自定义的分数
class MyFraction:
# 初始化方法,用于创建MyFraction对象时设置分子和分母
def __init__(self, numerator, denominator=1):
# 分子
self.numerator = numerator
# 分母,默认为1(但通常不会为0,因为0作为分母在数学上是没有定义的)
self.denominator = denominator
# 定义divmod方法的特殊版本,用于实现自定义分数与其他数字类型的除法取余操作
def __divmod__(self, other):
# 检查传入的other是否是整数、浮点数或Fraction类型
if not isinstance(other, (int, float, Fraction)):
# 如果不是,则抛出TypeError异常
raise TypeError("Unsupported operand type(s) for divmod()")
# 将other转换为Fraction类型,以确保可以与Fraction对象进行运算
other_fraction = Fraction(other)
# 使用内置的divmod函数和Fraction对象进行除法取余操作
# 注意:这里将MyFraction对象也转换为Fraction对象进行计算
result = divmod(Fraction(self.numerator, self.denominator), other_fraction)
# divmod函数返回的是一个包含两个元素的元组:(商, 余数)
# 商和余数都是Fraction对象,我们需要将其转换为(分子, 分母)的形式并返回
# 第一个元素是商,转换为(分子, 分母)的元组形式
# 第二个元素是余数,直接返回其分子(因为余数的分母始终为1)
return (result[0].numerator, result[0].denominator), result[1].numerator
# 如果当前脚本作为主程序运行(而不是被导入),则执行以下代码
if __name__ == '__main__':
# 创建一个MyFraction对象,分子为7,分母为3
frac = MyFraction(7, 3)
# 使用内置的divmod函数和自定义的MyFraction对象进行除法取余操作
# 注意:这里并没有直接调用MyFraction的__divmod__方法,而是使用了内置的divmod函数
# 但由于MyFraction类定义了__divmod__方法,所以内置的divmod函数能够识别并使用它
print(divmod(frac, 2)) # 输出: ((1, 1), 1),表示商为1(分子为1,分母为1),余数为1
# 3、复数类(注意:复数通常不支持取模运算)
# 定义一个名为ComplexNumber的类,用于表示复数
class ComplexNumber:
# 初始化方法,设置复数的实部和虚部
def __init__(self, real, imag):
self.real = real # 复数的实部
self.imag = imag # 复数的虚部
# 定义__divmod__方法,用于实现复数与其他数字类型的除法取余操作
# 注意:复数通常不支持取模运算,这里仅作为示例展示除法
def __divmod__(self, other):
# 检查传入的other是否是整数、浮点数或复数类型
if not isinstance(other, (int, float, complex)):
# 如果不是,则抛出TypeError异常
raise TypeError("Unsupported operand type(s) for divmod()")
# 计算除法,使用内置的complex函数和/操作符进行复数除法
quotient = complex(self.real, self.imag) / other
# 这里不返回余数,因为复数除法没有明确的余数概念
# 返回商和0j(即虚部为0的复数,表示没有余数)
# 注意:这里返回的第二个元素虽然是0j,但在复数除法中并没有实际意义
return quotient, 0 + 0j
# 如果当前脚本作为主程序运行(而不是被导入),则执行以下代码
if __name__ == '__main__':
# 创建一个ComplexNumber对象,实部为2,虚部为3
c = ComplexNumber(2, 3)
# 使用内置的divmod函数和自定义的ComplexNumber对象进行除法取余操作
# 注意:虽然名为divmod,但这里只实现了除法,并返回了商和0j作为余数(无实际意义)
print(divmod(c, 1 + 1j)) # 输出:((2.5+0.5j), 0j),表示商为2.5+0.5j,余数为0j(无实际意义)
# 4、自定义单位类(如长度,使用米和厘米)
class Length:
def __init__(self, meters, cm=0):
# 将厘米转换为米,并加到总的米数上
self.meters = meters + cm / 100
# 获取剩余的厘米数(0-99)
self.cm = cm % 100
def __divmod__(self, other):
if not isinstance(other, (int, float)):
raise TypeError("不支持的操作数类型进行 divmod() 操作")
# 确保除数不为零
if other == 0:
raise ZeroDivisionError("除数不能为零")
# 转换为厘米以便进行计算
total_cm = int(self.meters * 100) + self.cm
# 使用 divmod 函数计算商和余数(均为厘米数)
quotient_cm, remainder = divmod(total_cm, other)
# 将商转换为米和厘米
quotient_meters = quotient_cm // 100
quotient_cm %= 100
# 返回一个包含商(Length 对象)和余数(厘米数)的元组
return Length(quotient_meters, quotient_cm), remainder
if __name__ == '__main__':
l = Length(2, 30)
# 使用 divmod 函数进行除法操作,并打印结果
# 结果应该是一个包含 Length 对象和余数的元组
result = divmod(l, 5)
print(result) # 输出可能类似于:(<__main__.Length object at 0x...>, 4)
# (< __main__.Length object at 0x0000023C5CACCE50 >, 4)