首页 > Python资料 博客日记

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

2024-06-28 07:00:05Python资料围观78

Python资料网推荐Python魔法之旅-魔法方法(22)这篇文章给大家,欢迎收藏Python资料网享受知识的乐趣

目录

一、概述

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

四、魔法方法

68、__setattr__方法

68-1、语法

68-2、参数

68-3、功能

68-4、返回值

68-5、说明

68-6、用法

69、__setitem__方法

69-1、语法

69-2、参数

69-3、功能

69-4、返回值

69-5、说明

69-6、用法

70、__sizeof__方法

70-1、语法

70-2、参数

70-3、功能

70-4、返回值

70-5、说明

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

四、魔法方法

68、__setattr__方法

68-1、语法
__setattr__(self, name, value, /)
    Implement setattr(self, name, value)
  
68-2、参数

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

68-2-2、name(必须)一个字符串,表示要设置的属性的名称。

68-2-3、value(必须)要设置给属性的新值。

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

68-3、功能

        用于在尝试为一个对象的属性赋值时被调用。

68-4、返回值

       没有特定的返回值要求,其主要目的是执行某些操作,而不是返回任何值。

68-5、说明

        由于__setattr__是在设置属性时被调用的,因此它本身并不“返回”被设置的属性值;相反,它执行必要的操作来确保属性值被正确地设置。

68-6、用法
# 068、__setattr__方法:
# 1、属性值范围限制
class RangeLimited:
    def __init__(self, min_value=0, max_value=100):
        self._min_value = min_value
        self._max_value = max_value
        self._data = {}
    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        else:
            if self._min_value <= value <= self._max_value:
                self._data[name] = value
            else:
                raise ValueError(
                    f"Value {value} for attribute {name} is out of range ({self._min_value} to {self._max_value})")
    def __getattr__(self, name):
        return self._data.get(name)
if __name__ == '__main__':
    obj = RangeLimited(min_value=10, max_value=50)
    obj.value = 25  # 正常赋值
    print(obj.value)  # 输出:25
    # obj.value = 75  # 抛出异常:ValueError: Value 75 for attribute value is out of range (10 to 50)

# 2、属性依赖检查
class DependencyChecker:
    def __init__(self):
        self._data = {}
    def __setattr__(self, name, value):
        # 检查是否是_data字典的赋值,或者是否是_data字典之外的属性赋值
        if name == '_data':
            # 如果是_data字典的赋值,直接使用基类的__setattr__
            super().__setattr__(name, value)
        elif name == 'b' and 'a' not in self._data:
            # 如果尝试设置b属性但a属性尚未设置,则抛出异常
            raise AttributeError("Cannot set attribute 'b' before 'a' is set.")
        else:
            # 对于_data字典之外的其他属性,存储到_data字典中
            self._data[name] = value
if __name__ == '__main__':
    obj = DependencyChecker()
    obj.a = 10  # 正常赋值
    obj.b = 20  # 如果在设置obj.b之前没有设置obj.a,则会抛出AttributeError(但在这个示例中不会)
    # 尝试在没有设置a的情况下设置b,会抛出AttributeError
    # obj = DependencyChecker()
    # obj.b = 20  # 这会抛出AttributeError: Cannot set attribute 'b' before 'a' is set.

69、__setitem__方法

69-1、语法
__setitem__(self, key, value, /)
    Set self[key] to value
69-2、参数

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

69-2-2、key(必须)表示要设置的键,通常是一个可哈希(hashable)的对象,如整数、浮点数、字符串、元组等,在my_dict[key]=value这样的语句中,key就是你要设置的键。

69-2-3、value(必须)表示要设置给键的新值,在my_dict[key]=value这样的语句中,value就是你要设置给键的值。

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

69-3、功能

        用于自定义字典(或类似字典的容器)在通过键(key)设置值(value)时的行为。

69-4、返回值

        没有返回值,即返回None。

69-5、说明

        无

69-6、用法
# 069、__setitem__方法:
# 1、简单的键值存储
class SimpleDict:
    def __init__(self):
        self.data = {}
    def __setitem__(self, key, value):
        self.data[key] = value
if __name__ == '__main__':
    d = SimpleDict()
    d['a'] = 1

# 2、限制键类型的字典
class TypedDict:
    def __init__(self, key_type):
        self.key_type = key_type
        self.data = {}
    def __setitem__(self, key, value):
        if not isinstance(key, self.key_type):
            raise TypeError(f"Key must be of type {self.key_type}")
        self.data[key] = value
if __name__ == '__main__':
    td = TypedDict(str)
    td['hello'] = 'world'

# 3、自定义列表
class CustomList:
    def __init__(self):
        self.data = []

    def __setitem__(self, index, value):
        if index < 0 or index >= len(self.data):
            raise IndexError("Index out of range")
        self.data[index] = value
if __name__ == '__main__':
    cl = CustomList()
    cl.data.extend([0, 0, 0])
    cl[1] = 1

# 4、实现稀疏矩阵
class SparseMatrix:
    def __init__(self, rows, cols):
        self.rows = rows
        self.cols = cols
        self.data = {}
    def __setitem__(self, index, value):
        if value == 0:
            return  # 不存储零值
        row, col = index
        if 0 <= row < self.rows and 0 <= col < self.cols:
            self.data[(row, col)] = value
            # 还需要实现__getitem__等方法...
if __name__ == '__main__':
    sm = SparseMatrix(3, 3)
    sm[(1, 1)] = 1

# 5、实现优先队列
import heapq
class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0
    def __setitem__(self, priority, item):
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1
        # 还需要实现pop等方法...
if __name__ == '__main__':
    pq = PriorityQueue()
    pq[3] = 'c'
    pq[1] = 'a'
    pq[2] = 'b'

# 6、实现一个带有历史记录的字典
class HistoryDict:
    def __init__(self):
        self.data = {}
        self.history = []
    def __setitem__(self, key, value):
        if key in self.data:
            self.history.append((key, self.data[key]))  # 记录旧值
        self.data[key] = value
        self.history.append((key, value))  # 记录新值
if __name__ == '__main__':
    hd = HistoryDict()
    hd['key'] = 'value1'
    hd['key'] = 'value2'

# 7、实现一个只接受整数值的字典
class IntegerValueDict:
    def __init__(self):
        self.data = {}
    def __setitem__(self, key, value):
        if not isinstance(value, int):
            raise ValueError("Value must be an integer")
        self.data[key] = value
if __name__ == '__main__':
    ivd = IntegerValueDict()
    ivd['a'] = 10  # 正常设置
    # ivd['b'] = 'ten'  # 这会抛出 ValueError

# 8、实现一个带有自动排序功能的列表
class SortedList:
    def __init__(self):
        self.data = []
    def __setitem__(self, index, value):
        if index < 0 or index > len(self.data):
            raise IndexError("Index out of range")
        self.data.insert(index, value)
        self.data.sort()
    def __getitem__(self, index):
        return self.data[index]
    # 注意:这里插入元素后对整个列表进行排序可能不是最高效的,
    # 实际中可能会使用更复杂的数据结构(如平衡二叉搜索树)来实现自动排序。
if __name__ == '__main__':
    sl = SortedList()
    sl[0] = 3
    sl[1] = 1
    sl[2] = 2
    print(sl[0], sl[1], sl[2])  # 输出: 1 2 3

# 9、实现一个具有默认值的字典
class DefaultDict:
    def __init__(self, default_value):
        self.data = {}
        self.default_value = default_value
    def __setitem__(self, key, value):
        self.data[key] = value
    def __getitem__(self, key):
        return self.data.get(key, self.default_value)
if __name__ == '__main__':
    dd = DefaultDict(0)
    print(dd['a'])  # 输出: 0(因为'a'不在字典中,返回默认值0)
    dd['a'] = 1
    print(dd['a'])  # 输出: 1

# 10、实现一个二维数组(列表的列表)
class TwoDArray:
    def __init__(self, rows, cols):
        self.data = [[0] * cols for _ in range(rows)]
    def __setitem__(self, index, value):
        if not isinstance(index, tuple) or len(index) != 2:
            raise IndexError("Index must be a tuple of length 2")
        row, col = index
        if not (0 <= row < len(self.data) and 0 <= col < len(self.data[0])):
            raise IndexError("Index out of range")
        self.data[row][col] = value
if __name__ == '__main__':
    ta = TwoDArray(3, 3)
    ta[(1, 1)] = 5

# 11、实现一个具有最大容量限制的列表
class BoundedList:
    def __init__(self, max_size):
        self.data = []
        self.max_size = max_size
    def __setitem__(self, index, value):
        if index < 0 or index >= self.max_size:
            raise IndexError("Index out of range")
        if len(self.data) == self.max_size:
            self.data.pop(0)  # 移除第一个元素以保持最大容量
        self.data.append(None)  # 先在末尾添加一个占位符
        self.data[index] = value  # 设置值
    def __getitem__(self, index):
        if index < 0 or index >= len(self.data):
            raise IndexError("Index out of range")
        return self.data[index]
if __name__ == '__main__':
    bl = BoundedList(3)
    bl[0] = 'a'
    bl[1] = 'b'
    bl[2] = 'c'
    # bl[3] = 'd'  # 这会抛出异常:IndexError: Index out of range

70、__sizeof__方法

70-1、语法
__sizeof__(self, /)
    D.__sizeof__() -> size of D in memory, in bytes # 以字节数的方式
70-2、参数

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

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

70-3、功能

        用于返回对象在内存中占用的字节数。

70-4、返回值

        返回一个整数,表示对象在内存中占用的字节数。

70-5、说明

        无

70-6、用法
# 070、__sizeof__方法:
# 1、自定义列表
class CustomList(list):
    def __sizeof__(self):
        size = super().__sizeof__()
        for item in self:
            size += sys.getsizeof(item)
        return size

# 2、自定义字典
import sys
class CustomDict(dict):
    def __sizeof__(self):
        size = super().__sizeof__()
        for key, value in self.items():
            size += sys.getsizeof(key) + sys.getsizeof(value)
        return size

# 3、自定义字符串类
class CustomString:
    def __init__(self, string):
        self.string = string
    def __sizeof__(self):
        return sys.getsizeof(self.string) + sys.getsizeof(self)

# 4、自定义整数类
class CustomInteger:
    def __init__(self, value):
        self.value = value
    def __sizeof__(self):
        return sys.getsizeof(self.value) + sys.getsizeof(self)

# 5、自定义浮点数类
class CustomFloat:
    def __init__(self, value):
        self.value = value
    def __sizeof__(self):
        return sys.getsizeof(self.value) + sys.getsizeof(self)

# 6、二维点类
class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __sizeof__(self):
        return sys.getsizeof(self.x) + sys.getsizeof(self.y) + sys.getsizeof(self)

# 7、三维点类
class Point3D:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
    def __sizeof__(self):
        return sys.getsizeof(self.x) + sys.getsizeof(self.y) + sys.getsizeof(self.z) + sys.getsizeof(self)

# 8、自定义矩阵类
class Matrix:
    def __init__(self, rows, cols, values=None):
        self.rows = rows
        self.cols = cols
        self.data = [[0 for _ in range(cols)] for _ in range(rows)]
        if values:
            for i, row in enumerate(values):
                for j, val in enumerate(row):
                    self.data[i][j] = val
    def __sizeof__(self):
        size = sys.getsizeof(self) + sys.getsizeof(self.rows) + sys.getsizeof(self.cols)
        for row in self.data:
            for val in row:
                size += sys.getsizeof(val)
        return size

# 9、自定义二叉树节点类
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None
    def __sizeof__(self):
        size = sys.getsizeof(self) + sys.getsizeof(self.value)
        if self.left:
            size += self.left.__sizeof__()
        if self.right:
            size += self.right.__sizeof__()
        return size

# 10、自定义图像类
class Image:
    def __init__(self, width, height, data):
        self.width = width
        self.height = height
        self.data = data  # Assume data is a list of pixel values
    def __sizeof__(self):
        size = sys.getsizeof(self) + sys.getsizeof(self.width) + sys.getsizeof(self.height)
        for pixel in self.data:
            size += sys.getsizeof(pixel)
        return size

# 11、自定义文件缓存类
import os
class FileCache:
    def __init__(self, path):
        self.path = path
        self.cache = {}
    def __sizeof__(self):
        size = sys.getsizeof(self) + sys.getsizeof(self.path)
        for key, value in self.cache.items():
            size += sys.getsizeof(key) + os.path.getsize(value) if isinstance(value, str) and os.path.exists(
                value) else sys.getsizeof(value)
        return size
        # 注意:这里os.path.getsize()用于计算文件大小,但这可能不准确,因为self.cache中的值可能不是文件路径

# 12、自定义图形类(例如矩形)
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    def __sizeof__(self):
        return sys.getsizeof(self) + sys.getsizeof(self.width) + sys.getsizeof(self.height)
        # 注意:这里我们只计算了Rectangle对象本身和其属性占用的内存,没有考虑可能与之关联的图形缓冲区或渲染资源

# 13、自定义集合类
class CustomSet(set):
    def __sizeof__(self):
        size = super().__sizeof__()
        for item in self:
            size += sys.getsizeof(item)
        # 可能需要加上额外的哈希表开销,但这里为了简化我们忽略它
        return size

# 14、自定义日期范围类
from datetime import date
class DateRange:
    def __init__(self, start_date, end_date):
        self.start_date = start_date
        self.end_date = end_date
    def __sizeof__(self):
        return sys.getsizeof(self) + sys.getsizeof(self.start_date) + sys.getsizeof(self.end_date)

# 15、自定义栈类
class Stack:
    def __init__(self):
        self.items = []
    def push(self, item):
        self.items.append(item)
    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        else:
            raise IndexError("Pop from an empty stack")
    def is_empty(self):
        return not bool(self.items)
    def __sizeof__(self):
        size = sys.getsizeof(self) + sys.getsizeof(self.items)
        for item in self.items:
            size += sys.getsizeof(item)
        return size

五、推荐阅读

1、Python筑基之旅

2、Python函数之旅

3、Python算法之旅

4、博客个人主页


版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐