一文掌握python面向对象魔术方法(二)

本文深入探讨了Python面向对象编程中的魔术方法,涵盖了迭代、序列化、上下文管理、类型转换、反射与描述符、元类和类方法、特殊属性访问以及复制与深浅拷贝等方面。通过实例解释了如何使用这些魔术方法来实现自定义行为,如定义迭代器、上下文管理协议、类型转换和复制操作等。
摘要由CSDN通过智能技术生成

接上篇:一文掌握python面向对象魔术方法(一)-CSDN博客

目录

六、迭代和序列化:

1、__iter__(self): 定义迭代器,使得类可以被 for 循环迭代。

2、__getitem__(self, key): 定义索引操作,如 obj[key]。

3、__setitem__(self, key, value): 定义赋值操作,如 obj[key] = value。

4、__delitem__(self, key): 定义删除操作,如 del obj[key]。

5、__len__(self): 定义长度获取,如 len(obj)。

6、__reversed__(self): 定义反向迭代。

七、上下文管理:

1、__enter__(self):  实现上下文管理协议(context management protocol)。

2、__exit__(self, exc_type, exc_value, traceback): 退出上下文管理器时调用。与 __enter__ 方法配对使用。

八、类型转换:

1、__bool__(self): 定义布尔测试,即 if obj 是否为真。

2、__int__(self): 定义对象转换为整数。

3、__float__(self): 定义对象转换为浮点数。

4、__complex__(self): 定义对象转换为复数。

5、__hash__(self): 定义对象的哈希值,以便放入集合和作为字典键。

九、反射与描述符:

1、__class__(self): 提供对象所属类的引用。

2、__dir__(self): 列出对象公开的属性名。

3、__get__(self, instance, owner): 在描述符协议中,定义当访问属性时的行为。

4、__set__(self, instance, value): 在描述符协议中,定义当设置属性时的行为。

5、__delete__(self, instance): 在描述符协议中,定义当删除属性时的行为。

6、__call__:使得一个对象可以像函数那样被调用

十、元类和类方法:

1、__metaclass__: (Python 2 中)定义类的元类。

2、__prepare__(metacls, name, bases, **kwds):  Python 3 引入的元类方法,控制类字典的初始准备阶段。

3、__mro__: 返回方法解析顺序 (Method Resolution Order),即类继承层次结构中方法查找的顺序。

4、__subclasses__(cls): 返回当前类的所有直接子类列表。

十一、特殊属性访问:

1、__slots__: 在类中定义后,限制类实例可用的属性,节省内存。

2、__module__: 类所在的模块名称,通常是一个字符串形式的模块路径。

十二、复制与深浅拷贝:

1、__copy__(self): 用于实现浅复制功能。

2、__deepcopy__(self, memo): 用于实现深复制功能。


六、迭代和序列化

1、__iter__(self): 定义迭代器,使得类可以被 for 循环迭代。

当调用迭代器的__iter__()方法时,它会返回一个迭代器对象,该对象能够通过next()函数或者for循环等来逐个访问容器中的元素。

这个方法的主要目的是使对象变成可迭代的。也就是说,如果一个类定义了__iter__()方法,那么这个类的对象就可以在for循环中直接使用,或者通过其他需要迭代器的上下文使用。

class MyIterable:
    def __init__(self, items):
        self.items = items

    def __iter__(self):
        return iter(self.items)

my_obj = MyIterable([1, 2, 3])

for item in my_obj:
    print(item)

在这个例子中,MyIterable类通过定义__iter__方法使其变得可迭代,当在for循环中使用my_obj时,会调用其__iter__方法返回一个迭代器,然后依次获取并打印列表中的每个元素。

2、__getitem__(self, key): 定义索引操作,如 obj[key]

当尝试从对象中通过下标(索引)或切片获取值时,Python解释器会自动调用此方法。

例如,如果有一个类实例,并尝试像操作列表或字典那样通过索引获取元素,如 instance[0] 或 instance[1:3],那么实际上就是在调用该类的__getitem__(self, key)方法。

class CustomList:
    def __init__(self, data):
        self.data = data

    def __getitem__(self, key):
        return self.data[key]

custom_list = CustomList([1, 2, 3, 4, 5])
print(custom_list[0])  # 输出:1
print(custom_list[1:3])  # 输出:[2, 3]

在这个例子中,CustomList类重载了__getitem__方法,使得我们可以像操作普通列表一样通过索引来获取数据。当执行custom_list[0]custom_list[1:3]时,实际上是调用了custom_list.__getitem__(0)custom_list.__getitem__(slice(1, 3))

3、__setitem__(self, key, value): 定义赋值操作,如 obj[key] = value

它在尝试通过索引赋值操作来修改对象内容时被调用。当你对一个对象执行类似 instance[key] = value 的操作时,Python解释器会自动调用这个方法。

例如,在自定义类中实现__setitem__方法可以允许用户像操作列表或字典那样设置元素的值:

class CustomList:
    def __init__(self, data=None):
        if data is None:
            self.data = []
        else:
            self.data = data

    def __setitem__(self, key, value):
        self.data[key] = value

custom_list = CustomList([1, 2, 3, 4, 5])
custom_list[0] = 10  # 这里会调用 __setitem__
print(custom_list.data)  # 输出:[10, 2, 3, 4, 5]

# 对于可变长度的键,如切片,也可以支持:
custom_list[1:3] = [11, 12]  # 这也需要 __setitem__ 方法正确处理切片赋值
print(custom_list.data)  # 输出:[10, 11, 12, 4, 5]

在上述示例中,CustomList 类通过覆盖 __setitem__ 方法实现了索引赋值功能,使得可以直接使用下标修改类实例内部的数据成员。对于更复杂的操作,如切片赋值,可能需要进一步扩展该方法以处理不同类型的键。

实现切片赋值:

class CustomList:
    def __init__(self, data=None):
        self.data = [] if data is None else list(data)

    def __setitem__(self, key, value):
        if isinstance(key, slice):
            start, stop, step = key.start, key.stop, key.step
            if start is None:
                start = 0
            if stop is None:
                stop = len(self.data)
            if step is None:
                step = 1
            indices = range(start, stop, step)
            for i, v in zip(indices, value):
                if i < 0:
                    i += len(self.data)
                if i < 0 or i >= len(self.data):
                    raise IndexError("Index out of range")
                self.data[i] = v
        else:
            if key < 0:
                key += len(self.data)
            if key < 0 or key >= len(self.data):
                raise IndexError("Index out of range")
            self.data[key] = value

# 使用示例
cl = CustomList([0, 1, 2, 3, 4, 5])
cl[1:4] = [10, 20, 30]
print(cl.data)  # 输出: [0, 10, 20, 30, 4, 5]

在这个例子中,当我们在自定义列表类CustomList上调用切片赋值时,__setitem__方法首先检查key是否为slice类型。如果是,它将解析切片的开始、结束和步长,并根据这些值遍历索引,然后逐个赋值。如果不是切片,它就按照常规索引进行赋值。

4、__delitem__(self, key): 定义删除操作,如 del obj[key]

主要用于在用户尝试删除类实例中的某个项目时执行相应的操作。当你对类实例使用 del 关键字或通过索引/切片表达式删除元素时,Python 解释器会调用这个方法。

例如,在一个自定义列表类中,__delitem__ 方法可以这样实现以支持索引或切片删除:

class CustomList:
    def __init__(self, data=None):
        self.data = [] if data is None else list(data)

    def __delitem__(self, key):
        if isinstance(key, slice):
            start, stop, step = key.indices(len(self.data))
            # self.data.__delslice__(start, stop, step)  # 旧版 Python
            self.data[start:stop:step] = self.data[:start] + self.data[stop:]  # 新版本 Python
        else:
            if key < 0:
                key += len(self.data)
            if key < 0 or key >= len(self.data):
                raise IndexError("Index out of range")
            del self.data[key]

# 使用示例
cl = CustomList([0, 1, 2, 3, 4, 5])
del cl[1:4]
print(cl.data)  # 输出: [0, 4, 5]

# 注意:对于现代Python(3.x),无需手动处理切片删除,可以直接使用 del cl[1:4],因为Python内置列表会自动处理这种情况。

对于 Python 3.x,由于 __delslice__ 已经被废弃,直接使用 del cl[1:4] 即可,因为在 Python 内置的列表对象中,已经内置了对切片删除的支持,不需要额外处理。上述代码仅适用于需要在自定义类中模拟列表行为的情况,而在现代 Python 版本中,您可以直接使用内置列表的功能,无需覆盖 __delitem__ 方法处理切片删除。

5、__len__(self): 定义长度获取,如 len(obj)

当对象的 len() 函数被调用时,Python 会自动调用这个方法。在自定义类中,重写 __len__ 方法可以让用户轻松获取对象所包含元素的数量。

例如,如果想创建一个自定义列表类,并希望用户能够使用 len() 函数获取列表中元素的数量,你可以在类中添加 __len__ 方法:

class CustomList:
    def __init__(self, data=None):
        self.data = [] if data is None else list(data)

    def __len__(self):
        return len(self.data)

# 使用示例
cl = CustomList([0, 1, 2, 3, 4, 5])
print(len(cl))  # 输出: 6

在这个例子中,当调用 len(cl) 时,Python 将自动调用 cl 实例的 __len__ 方法,返回自定义列表 data 属性中的元素数量。

6、__reversed__(self): 定义反向迭代。

在类中定义时,允许该类的对象能够被内置的 reversed() 函数调用以实现反向迭代。

这个方法应该返回一个迭代器,该迭代器生成对象元素的反向序列。例如,如果有一个自定义列表类,可以这样实现 __reversed__ 方法:

class MyList:
    def __init__(self, elements):
        self.elements = elements

    def __reversed__(self):
        return reversed(self.elements)

# 使用示例
my_list = MyList([1, 2, 3, 4, 5])
for item in reversed(my_list):
    print(item)  # 5, 4, 3, 2, 1

七、上下文管理

1、__enter__(self):  实现上下文管理协议(context management protocol)。

当对象作为 with 语句的目标表达式时,该方法会被自动调用。它的主要作用是初始化进入某个特定上下文所需的资源,并返回一个对象供 with 语句块内部使用。

例如,在一个实现了上下文管理协议的类中,__enter__ 方法可能会打开一个文件、创建一个数据库连接或其他需要在使用完毕后清理的资源:

class ManagedResource:
    def __enter__(self):
        # 打开或者初始化资源
        self.resource = open("example.txt", "r")
        return self.resource  # 返回资源对象,可以在这个资源上执行操作

    def __exit__(self, exc_type, exc_value, traceback):
        # 使用完资源后关闭或清理
        self.resource.close()

# 使用with语句来自动管理资源
with ManagedResource() as resource:
    content = resource.read()
    # 在此处对resource进行操作,完成后无需手动关闭,__exit__会确保资源正确关闭

在这个例子中,当 ManagedResource 类的对象进入 with 语句的作用域时,__enter__ 方法会被调用,返回打开的文件对象;当 with 语句块结束时,无论是否发生异常,__exit__ 方法都会被调用来关闭文件并可能处理任何异常情况。

2、__exit__(self, exc_type, exc_value, traceback): 退出上下文管理器时调用。与 __enter__ 方法配对使用。

当离开 with 语句块时,不论是因为正常执行完成还是抛出了异常,__exit__ 方法都会被调用。

这个方法接收三个参数:

  • exc_type: 如果在 with 语句块内发生了异常,那么这是异常的类型(如 TypeErrorValueError 等)。如果没有异常,则为 None
  • exc_value: 如果有异常,这是异常实例本身。若无异常,则为 None
  • traceback: 如果有异常,这是跟踪回溯对象。若无异常,则为 None

__exit__ 方法的主要职责是清理在 __enter__ 中获取或创建的资源,包括但不限于关闭文件、释放锁、删除临时变量等,并且可以根据需要处理在 with 语句块中发生的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值