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

CSDN 2024-07-19 09:35:03 阅读 78

目录

 一、概述

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、语法

<code>__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)

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。