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

news2024/11/15 21:27:40

目录

 一、概述

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)

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1717427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kotlin 2.0 重磅发布! 性能提升!新功能上线!开发者必看!

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

2.7HDR与LDR

一、基本概念 1.基本概念 动态范围&#xff08;Dynamic Range&#xff09; 最高亮度 / 最低亮度 HDR High Dynamic RangeLDR Low Dynamic Range HDR与LDR和Tonemapping的对应关系&#xff1a; 我们常用的各种显示器屏幕&#xff0c;由于不同的厂家不同的工艺导致它们的…

【GlobalMapper精品教程】083:基于DEM整体抬升或下降地形高程的两种方式

在Globalmapper24中文版中,可以很方便地对DEM高程数据进行整体抬升或下降。 文章目录 一、准备DEM数据二、高程整体修改1. 修改偏移2. 数据导出三、栅格计算器一、准备DEM数据 订阅专栏后,从私信查收实验数据及Globalmapper24中文版,加载实验数据包data083.rar中的dem数据,…

Unity【入门】脚本基础

Unity脚本基础 文章目录 1、脚本基本规则1、创建规则2、MonoBehavior基类3、不继承MonoBehavior的类4、执行的先后顺序5、默认脚本内容 2、生命周期函数1、概念2、生命周期函数有哪些3、生命周期函数支持继承多态 3、Inspector窗口可编辑的变量4、Mono中的重要内容1、重要成员2…

一个良好的嵌入式系统框架(基于FreeRTOS)

目录 Unix操作系统里的优先级嵌入式系统里的优先级 Unix操作系统里的优先级 在《Unix传奇》中有这样一句话&#xff0c;用户态的进程/线程是三等公民、root线程是二等公民、硬件中断是一等公民。 在操作系统中&#xff0c;"用户态"和"内核态"是两种不同的…

深入Kafka消息分区机制:从原理到实践

深入Kafka消息分区机制&#xff1a;从原理到实践 在现代分布式系统中&#xff0c;如何高效地处理海量数据是一个至关重要的问题。Apache Kafka作为一种高吞吐量的分布式消息系统&#xff0c;广泛应用于日志收集、实时分析等场景。为了保证数据的高效处理和系统的高可扩展性&am…

全新/二手KEITHLEY 2400 数字万用表

吉时利Keithley 2400数字源表&#xff0c;200V&#xff0c;1A&#xff0c;20W Keithley 2400 源表是一款 20W 仪器&#xff0c;可输出和测量 5V&#xff08;输出&#xff09;和 1V&#xff08;测量&#xff09;至 200V DC 的电压以及 10pA 至 1A 的电流。该万用表功能包括高重复…

2024 RCTF WebMisc部分 WP

Misc gogogo 考点:内存取证 得到 gogogo.raw 内存取证的题用volatility和AXIOM结合分析 AXIOM 分析存在云服务 但是百度网盘要密码 https://pan.baidu.com/share/init?surlZllFd8IK-oHvTCYl61_7Kw 发现访问过sqlite数据库 可以尝试提取数据库文件出来 结合 volatility 第…

Flutter基础 -- Dart 语言 -- 列表集合枚举

目录 1. 列表 List 1.1 初始 1.2 声明 1.2.1 自动 1.2.2 定长 1.2.3 生成数据 1.3 属性 1.4 方法 1.4.1 添加 1.4.2 查询 1.4.3 删除 1.4.4 Range 1.4.5 洗牌 1.4.6 排序 1.4.7 复制子列表 1.4.8 操作符 2. 集合 Map 2.1 初始 2.2 声明 2.2.1 松散 2.2.2 …

python-求点积

【问题描述】&#xff1a;给出两个数组&#xff0c;并求它们的点积。 【问题描述】&#xff1a;输入A[1,1,1],B[2,2,2]&#xff0c;输出6,即1*21*21*26。输入A[3,2],B[2,3,3],输出-1&#xff0c;没有点积。 完整代码如下&#xff1a; alist(map(int,input().split())) blist(…

vue路由跳转之【编程式导航与传参】

vue路由有两种跳转方式 ----> 编程式与声明式&#xff0c;本文重点讲解vue路由的【编程式导航 】【编程式导航传参 ( 查询参数传参 & 动态路由传参 ) 】等内容&#xff0c;并结合具体案例让小伙伴们深入理解 &#xff0c;彻底掌握&#xff01;创作不易&#xff0c;需要的…

Maven项目通过maven central 发布到中央仓库 https://repo.maven.apache.org/ 手把手教学 最新教学

一、注册maven central账号 ​ https://central.sonatype.com/publishing/namespaces 我这里直接使用github账号登录 &#xff0c;可以自己注册或者直接使用google账号或者github账号登录 这里github账号登录之后 应该只出现io.github 下面的io.gitee我也验证过 所以这里出…

AltiumDesigner/AD添加数据库连接

1.首先确保本机电脑有无对应的数据库驱动&#xff0c;例如我这边要添加MySQL的数据&#xff0c;则需要首先下载MySQL数据驱动&#xff1a;MySQL :: Download MySQL Connector/ODBC (Archived Versions) 2.运行“odbcad32.exe”&#xff0c;如下图添加对应的数据库配置&#xf…

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

目录 一、概述 1、定义 2、作用 二、应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类型检…

openresty(Nginx) 配置 特殊URL 密码访问 使用htpasswd 配置 Basic_Auth登录认证

1 使用htpasswd 生成密码文件.htpasswd是Apache附带的工具。如果没有可以安装。 #centos 8.5 系统 yum install httpd-tools #Ubuntu 24.04 系统 sudo apt update sudo apt-get install apache2-utils #生成密码文件,用户test sudo htpasswd -c /usr/local/openresty/nginx/…

独家首发 | 基于 KAN、KAN卷积的轴承故障诊断模型

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Python轴承故障诊断入门教学-CSDN博客 Python轴承故障诊断 (13)基于故障信号特征提取的超强机器学习识别模型-CSDN博客 Python轴承故障诊断 (14)高创新故障识别模型-CSDN…

Git使用规范及命令

文章目录 一、Git工作流二、分支管理三、Git命令操作规范1. 切到develop分支&#xff0c;更新develop最新代码2. 新建feature分支&#xff0c;开发新功能3. 完成feature分支&#xff0c;合并到develop分支4. 当某个版本所有的 feature 分支均合并到 develop 分支&#xff0c;就…

【漏洞复现】大华 DSS 数字监控系统 user_edit.action 信息泄露漏洞

0x01 产品简介 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统&#xff0c;拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。 0x02 漏洞概述 大华 DSS 数字监控系统 user_edit.action 接囗处存在信息泄露漏洞。未经身份验证的远程攻击者可利用此漏洞…

无意间看到男主眼神,这也太有感觉了吧❗❗

2025即将首播《藏海传》中国大陆剧情/奇幻/古装共40集。 原本&#xff0c;稚奴身为大雍国钦天监监正蒯铎之子&#xff0c;背负着家族血仇。 历经十年沉默与磨砺&#xff0c;他化名为藏海&#xff08;肖战 饰&#xff09;&#xff0c;重返京城。 他凭借卓越的营造技艺和深谙纵…

gitbook安装 报错处理 windows系统

首先需要有nodejs。若没有&#xff0c;则去nodejs官网下载nodejs安装。 然后安装gitbook。命令如下&#xff1a;这是在linux系统的命令。 $ npm config set registry http://registry.npm.taobao.org #设置一下淘宝镜像&#xff08;非必选&#xff09; $ npm install gitbo…