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

2024-06-04 6570阅读

目录

一、概述

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、避免过度使用

四、魔法方法

26、__getstate__方法

26-1、语法

26-2、参数

26-3、功能

26-4、返回值

26-5、说明

26-6、用法

27、__gt__方法

27-1、语法

27-2、参数

27-3、功能

27-4、返回值

27-5、说明

27-6、用法

28、__setstate__方法

28-1、语法

28-2、参数

28-3、功能

28-4、返回值

28-5、说明

28-6、用法

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页
​​​​​​​

Python魔法之旅-魔法方法(08) 第1张

Python魔法之旅-魔法方法(08) 第2张

Python魔法之旅-魔法方法(08) 第3张

一、概述

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魔法之旅-魔法方法(08) 第4张

三、学习方法

        要学好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的魔法方法需要不断地学习、实践和总结,只有通过不断地练习和积累经验,你才能更好地掌握这些强大的工具,并在实际项目中灵活运用它们。

Python魔法之旅-魔法方法(08) 第5张

四、魔法方法

26、__getstate__方法

26-1、语法
__getstate__(self, /)
    Helper for pickle
26-2、参数

26-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。 

26-2-2、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

26-3、功能

        用于返回对象的状态,这样该对象就可以被pickle模块序列化为字节流,以便之后可以通过pickle.loads()反序列化回原始对象。

26-4、返回值

        返回一个可以被pickle模块序列化的对象,通常是一个字典,其中包含了重构对象所需的所有状态信息。

26-5、说明

        如果你的类有动态生成的属性或者使用了__slots__来限制实例属性,那么你可能需要更仔细地编写__getstate__和__setstate__方法来确保所有必要的信息都被正确地序列化和反序列化。

26-6、用法
# 026、__getstate__方法:
# 1、基本示例
import pickle
class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __getstate__(self):
        return {'x': self.x, 'y': self.y}
    def __setstate__(self, state):
        self.x = state['x']
        self.y = state['y']
if __name__ == '__main__':
    # 序列化和反序列化
    obj = MyClass(10, 24)
    serialized_obj = pickle.dumps(obj)
    deserialized_obj = pickle.loads(serialized_obj)
    print(deserialized_obj.x, deserialized_obj.y)  # 输出: 10 24
# 2、忽略某些属性
class Person:
    def __init__(self, name, age, password):
        self.name = name
        self.age = age
        self._password = password  # 不想在序列化中包含密码
    def __getstate__(self):
        return {'name': self.name, 'age': self.age}
    def __setstate__(self, state):
        self.name = state['name']
        self.age = state['age']
        self._password = None  # 假设在反序列化时重置密码
# 3、自定义序列化格式
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __getstate__(self):
        return (self.x, self.y)  # 使用元组而不是字典
    def __setstate__(self, state):
        self.x, self.y = state
# 4、处理不可序列化的属性
import socket
class Connection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = socket.socket()  # 不可序列化
    def __getstate__(self):
        # 不包括 socket 对象
        return {'host': self.host, 'port': self.port}
    def __setstate__(self, state):
        self.host = state['host']
        self.port = state['port']
        self.socket = socket.socket()  # 在反序列化时重新创建 socket
# 5、序列化和反序列化时执行额外操作
class LoggedClass:
    def __init__(self, data):
        self.data = data
    def __getstate__(self):
        print("Serializing...", self.data)
        return {'data': self.data}
    def __setstate__(self, state):
        print("Deserializing...", state['data'])
        self.data = state['data']
# 6、处理集合类型
class SetExample:
    def __init__(self, items):
        self.items = set(items)
    def __getstate__(self):
        return {'items': list(self.items)}  # 将集合转换为列表
    def __setstate__(self, state):
        self.items = set(state['items'])  # 将列表转换回集合
# 7、嵌套对象
class Inner:
    def __init__(self, value):
        self.value = value
class Outer:
    def __init__(self, inner):
        self.inner = inner
    def __getstate__(self):
        return {'inner': self.inner.__getstate__()}  # 嵌套对象也需要序列化
    def __setstate__(self, state):
        self.inner = Inner.__new__(Inner)  # 创建新实例
        self.inner.__setstate__(state['inner'])  # 反序列化嵌套对象
# 8、带有文件和文件描述符的对象
import os
class FileHandler:
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'rb')
    def __getstate__(self):
        # 关闭文件描述符,只保存文件名
        self.file.close()
        return {'filename': self.filename}
    def __setstate__(self, state):
        self.filename = state['filename']
        self.file = open(self.filename, 'rb')
# 9、带有循环引用的对象
class Node:
    def __init__(self, value, next_node=None):
        self.value = value
        self.next_node = next_node
    def __getstate__(self):
        # 处理循环引用,通常使用弱引用或特殊标记
        # 这里简化处理,只保存值和下一个节点的ID(如果可用)
        if self.next_node is not None:
            next_id = id(self.next_node)
        else:
            next_id = None
        return {'value': self.value, 'next_id': next_id}
    def __setstate__(self, state):
        self.value = state['value']
        self.next_node = None  # 初始为空
        # 在这里,你可能需要一个节点ID到节点的映射来恢复循环引用
# 10、带有线程或进程的对象
import threading
class ThreadedClass:
    def __init__(self):
        self.thread = threading.Thread(target=self.run)
        self.thread.start()
    def run(self):
        # 模拟线程运行
        pass
    def __getstate__(self):
        # 线程不能序列化,所以只保存其他数据(如果有的话)
        # 这里假设没有其他数据可保存,所以返回空字典
        return {}
    def __setstate__(self, state):
        # 在反序列化时,重新创建线程(如果需要的话)
        # 但通常不推荐这样做,因为线程状态难以恢复
        pass
        # 注意:线程和进程状态通常不能序列化,因为它们与特定执行环境紧密相关

27、__gt__方法

27-1、语法
__gt__(self, other, /)
    Return self > other
27-2、参数

27-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。 

27-2-2、other(必须):表示与self进行比较的另一个对象。

27-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

27-3、功能

        用于定义对象之间的“大于”(greater than)比较操作。

27-4、返回值

        返回一个布尔值(True或False),如果self大于other,则返回True;反之,则返回False。

27-5、说明

        如果self.value大于other.value,则方法返回True;否则,你可以根据需要选择返回False或者抛出一个异常。

27-6、用法
# 027、__gt__方法:
# 1、比较两个数字类
class Number:
    def __init__(self, value):
        self.value = value
    def __gt__(self, other):
        if isinstance(other, Number):
            return self.value > other.value
        return NotImplemented
if __name__ == '__main__':
    a = Number(5)
    b = Number(3)
    print(a > b)  # True
# 2、比较两个字符串长度
class StringLength:
    def __init__(self, string):
        self.string = string
    def __gt__(self, other):
        if isinstance(other, StringLength):
            return len(self.string) > len(other.string)
        return NotImplemented
if __name__ == '__main__':
    s1 = StringLength("hello")
    s2 = StringLength("world")
    print(s1 > s2)  # False
# 3、比较两个日期对象
from datetime import date
class DateWrapper:
    def __init__(self, year, month, day):
        self.date = date(year, month, day)
    def __gt__(self, other):
        if isinstance(other, DateWrapper):
            return self.date > other.date
        return NotImplemented
if __name__ == '__main__':
    d1 = DateWrapper(2024, 3, 13)
    d2 = DateWrapper(2024, 6, 1)
    print(d1 > d2)  # False
# 4、比较两个自定义分数类
from fractions import Fraction
class MyFraction:
    def __init__(self, numerator, denominator):
        self.fraction = Fraction(numerator, denominator)
    def __gt__(self, other):
        if isinstance(other, MyFraction):
            return self.fraction > other.fraction
        return NotImplemented
if __name__ == '__main__':
    f1 = MyFraction(3, 2)
    f2 = MyFraction(1, 1)
    print(f1 > f2)  # True
# 5、比较两个点的坐标(二维空间)
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __gt__(self, other):
        if isinstance(other, Point):
            # 比较x坐标,如果相等则比较y坐标
            if self.x == other.x:
                return self.y > other.y
            return self.x > other.x
        return NotImplemented
if __name__ == '__main__':
    p1 = Point(3, 6)
    p2 = Point(5, 11)
    print(p1 > p2)  # False
# 6、比较两个矩形的面积
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    @property
    def area(self):
        return self.width * self.height
    def __gt__(self, other):
        if isinstance(other, Rectangle):
            return self.area > other.area
        return NotImplemented
if __name__ == '__main__':
    r1 = Rectangle(3, 6)
    r2 = Rectangle(5, 11)
    print(r1 > r2)  # False
# 7、 比较两个列表的长度
class ListWrapper:
    def __init__(self, lst):
        self.lst = lst
    def __gt__(self, other):
        if isinstance(other, ListWrapper):
            return len(self.lst) > len(other.lst)
        return NotImplemented
if __name__ == '__main__':
    lw1 = ListWrapper([1, 2, 3])
    lw2 = ListWrapper([1, 2])
    print(lw1 > lw2)  # True
# 8、比较两个字典的键的数量
class DictWrapper:
    def __init__(self, dct):
        self.dct = dct
    def __gt__(self, other):
        if isinstance(other, DictWrapper):
            return len(self.dct.keys()) > len(other.dct.keys())
        return NotImplemented
if __name__ == '__main__':
    dw1 = DictWrapper({'a': 1, 'b': 2})
    dw2 = DictWrapper({'a': 1})
    print(dw1 > dw2) # True
# 9、比较两个自定义员工类的薪资
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    def __gt__(self, other):
        if isinstance(other, Employee):
            return self.salary > other.salary
        return NotImplemented
if __name__ == '__main__':
    emp1 = Employee('Myelsa', 50000)
    emp2 = Employee('Jimmy', 48000)
    print(emp1 > emp2)  # True
# 10、比较两个自定义时间段的时长
from datetime import timedelta
class TimePeriod:
    def __init__(self, start, end):
        self.start = start
        self.end = end
    @property
    def duration(self):
        return self.end - self.start
    def __gt__(self, other):
        if isinstance(other, TimePeriod):
            return self.duration > other.duration
        return NotImplemented
if __name__ == '__main__':
    tp1 = TimePeriod(timedelta(hours=2), timedelta(hours=3))
    tp2 = TimePeriod(timedelta(hours=1), timedelta(hours=2))
    print(tp1 > tp2)  # False
    # 注意:这里的timedelta对象不能直接相减,这里的示例仅用于展示结构
# 11、比较两个自定义成绩类的分数
class Grade:
    def __init__(self, score):
        self.score = score
    def __gt__(self, other):
        if isinstance(other, Grade):
            return self.score > other.score
        return NotImplemented
if __name__ == '__main__':
    g1 = Grade(90)
    g2 = Grade(85)
    print(g1 > g2)  # True
# 12、比较两个自定义商品的价格
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    def __gt__(self, other):
        if isinstance(other, Product):
            return self.price > other.price
        return NotImplemented
if __name__ == '__main__':
    p1 = Product('Laptop', 1000)
    p2 = Product('Phone', 800)
    print(p1 > p2)  # True

28、__setstate__方法

28-1、语法
__setstate__(self, state, /)
    Set state information for unpickling
28-2、参数

28-2-1、self(必须):一个对实例对象本身的引用,在类的所有方法中都会自动传递。 

28-2-2、state(必须):一个对象(通常是一个字典),包含了在序列化时由 __getstate__ 方法返回的状态信息。

28-2-3、/(可选):这是从Python 3.8开始引入的参数注解语法,它表示这个方法不接受任何位置参数(positional-only parameters)之后的关键字参数(keyword arguments)。

28-3、功能

        使用传入的state参数(通常是一个字典或其他数据结构)来重新构造对象的内部状态。

28-4、返回值

        没有返回值,即返回None。

28-5、说明

        在__setstate__方法内部,你可以根据state中的数据来设置对象的属性、调用其他方法或执行其他必要的操作,以确保对象在反序列化后具有与序列化时相同的状态。

28-6、用法
# 028、__setstate__方法:
# 1、基本示例
import pickle
class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __getstate__(self):
        return {'x': self.x, 'y': self.y}
    def __setstate__(self, state):
        self.x = state['x']
        self.y = state['y']
if __name__ == '__main__':
    # 序列化和反序列化
    obj = MyClass(10, 24)
    serialized_obj = pickle.dumps(obj)
    deserialized_obj = pickle.loads(serialized_obj)
    print(deserialized_obj.x, deserialized_obj.y)  # 输出: 10 24
# 2、忽略某些属性
class Person:
    def __init__(self, name, age, password):
        self.name = name
        self.age = age
        self._password = password  # 不想在序列化中包含密码
    def __getstate__(self):
        return {'name': self.name, 'age': self.age}
    def __setstate__(self, state):
        self.name = state['name']
        self.age = state['age']
        self._password = None  # 假设在反序列化时重置密码
# 3、自定义序列化格式
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __getstate__(self):
        return (self.x, self.y)  # 使用元组而不是字典
    def __setstate__(self, state):
        self.x, self.y = state
# 4、处理不可序列化的属性
import socket
class Connection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = socket.socket()  # 不可序列化
    def __getstate__(self):
        # 不包括 socket 对象
        return {'host': self.host, 'port': self.port}
    def __setstate__(self, state):
        self.host = state['host']
        self.port = state['port']
        self.socket = socket.socket()  # 在反序列化时重新创建 socket
# 5、序列化和反序列化时执行额外操作
class LoggedClass:
    def __init__(self, data):
        self.data = data
    def __getstate__(self):
        print("Serializing...", self.data)
        return {'data': self.data}
    def __setstate__(self, state):
        print("Deserializing...", state['data'])
        self.data = state['data']
# 6、处理集合类型
class SetExample:
    def __init__(self, items):
        self.items = set(items)
    def __getstate__(self):
        return {'items': list(self.items)}  # 将集合转换为列表
    def __setstate__(self, state):
        self.items = set(state['items'])  # 将列表转换回集合
# 7、嵌套对象
class Inner:
    def __init__(self, value):
        self.value = value
class Outer:
    def __init__(self, inner):
        self.inner = inner
    def __getstate__(self):
        return {'inner': self.inner.__getstate__()}  # 嵌套对象也需要序列化
    def __setstate__(self, state):
        self.inner = Inner.__new__(Inner)  # 创建新实例
        self.inner.__setstate__(state['inner'])  # 反序列化嵌套对象
# 8、带有文件和文件描述符的对象
import os
class FileHandler:
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'rb')
    def __getstate__(self):
        # 关闭文件描述符,只保存文件名
        self.file.close()
        return {'filename': self.filename}
    def __setstate__(self, state):
        self.filename = state['filename']
        self.file = open(self.filename, 'rb')
# 9、带有循环引用的对象
class Node:
    def __init__(self, value, next_node=None):
        self.value = value
        self.next_node = next_node
    def __getstate__(self):
        # 处理循环引用,通常使用弱引用或特殊标记
        # 这里简化处理,只保存值和下一个节点的ID(如果可用)
        if self.next_node is not None:
            next_id = id(self.next_node)
        else:
            next_id = None
        return {'value': self.value, 'next_id': next_id}
    def __setstate__(self, state):
        self.value = state['value']
        self.next_node = None  # 初始为空
        # 在这里,你可能需要一个节点ID到节点的映射来恢复循环引用
# 10、带有线程或进程的对象
import threading
class ThreadedClass:
    def __init__(self):
        self.thread = threading.Thread(target=self.run)
        self.thread.start()
    def run(self):
        # 模拟线程运行
        pass
    def __getstate__(self):
        # 线程不能序列化,所以只保存其他数据(如果有的话)
        # 这里假设没有其他数据可保存,所以返回空字典
        return {}
    def __setstate__(self, state):
        # 在反序列化时,重新创建线程(如果需要的话)
        # 但通常不推荐这样做,因为线程状态难以恢复
        pass
        # 注意:线程和进程状态通常不能序列化,因为它们与特定执行环境紧密相关

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]