赞
踩
目录
魔法方法(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的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。
- __delitem__(self, key, /)
- Delete self[key]
11-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
11-2-2、key(必须):表示想要从对象中删除的元素的键或索引
11-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
用于定义当从容器中删除一个元素时应该执行的操作。
当这个方法被调用时,它应该执行删除操作,但不应该返回任何值(或者更具体地说,它应该返回None)。
对于列表,key通常是一个整数索引;对于字典,key通常是一个键。
- # 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
- __dir__(self, /)
- Default dir() implementation
12-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
12-2-2、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
用于定制对象的属性列表,即在执行如dir(obj)这样的操作时返回的属性名列表。
返回一个字符串列表,包含了你想让dir()函数返回的对象的属性名。
如果你没有为类定义 __
dir__
方法,那么Python会使用默认的dir()实现,它会列出对象的所有属性,包括从父类继承的属性,以及实例属性、方法、类等。
- # 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']
- __divmod__(self, value, /)
- Return divmod(self, value)
13-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。
13-2-2、value(必须):表示要与self进行除法和取模运算的第二个值。
13-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。
用于定义对象在使用divmod()内置函数时的行为。
返回一个元组,其中包含两个值:除法的商和余数。
当你对一个对象调用divmod()函数时,Python会自动尝试调用该对象的 __divmod__ 方法。这个方法应该接受两个参数:self(即对象本身)和value(即要与对象进行除法和取模运算的值)。
- # 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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。