Python案例中的魔法方法有哪些应用?

wen python案例 4

本文目录导读:

Python案例中的魔法方法有哪些应用?

  1. 字符串表示:__str____repr__
  2. 比较操作:__eq__, __lt__, __gt__
  3. 容器行为:__getitem__, __setitem__, __len__, __contains__
  4. 迭代器支持:__iter____next__
  5. 上下文管理器:__enter____exit__
  6. 运算符重载:__add__, __sub__, __mul__
  7. 属性访问控制:__getattr__, __setattr__, __getattribute__
  8. 可调用对象:__call__
  9. 完整案例:自定义字典类

在Python中,魔法方法(Magic Methods,也叫双下划线方法)能让我们自定义类的行为,使其更像Python内置类型,下面通过具体案例来说明魔法方法的常见应用场景。

字符串表示:__str____repr__

class Book:
    def __init__(self, title, author, price):
        self.title = title
        self.author = author
        self.price = price
    # 给用户看的友好字符串
    def __str__(self):
        return f"《{self.title}》- {self.author}"
    # 给开发者看的无歧义字符串
    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.price})"
book = Book("Python编程", "张三", 59.0)
print(str(book))    # 《Python编程》- 张三
print(repr(book))   # Book('Python编程', '张三', 59.0)

比较操作:__eq__, __lt__, __gt__

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score
    # 定义等于比较
    def __eq__(self, other):
        return self.score == other.score
    # 定义小于比较
    def __lt__(self, other):
        return self.score < other.score
    # 定义字符串表示便于打印
    def __repr__(self):
        return f"{self.name}: {self.score}"
students = [
    Student("小明", 85),
    Student("小红", 92),
    Student("小刚", 78)
]
students.sort()  # 自动使用 __lt__ 排序
print(students)  # [小刚: 78, 小明: 85, 小红: 92]
print(students[0] == students[1])  # False,使用 __eq__

容器行为:__getitem__, __setitem__, __len__, __contains__

class Playlist:
    def __init__(self):
        self._songs = []
    # 添加歌曲
    def add_song(self, song):
        self._songs.append(song)
    # 获取长度
    def __len__(self):
        return len(self._songs)
    # 索引访问
    def __getitem__(self, index):
        return self._songs[index]
    # 索引赋值
    def __setitem__(self, index, song):
        self._songs[index] = song
    # 包含检查
    def __contains__(self, song):
        return song in self._songs
playlist = Playlist()
playlist.add_song("告白气球")
playlist.add_song("七里香")
playlist.add_song("青花瓷")
print(len(playlist))          # 3,使用 __len__
print(playlist[1])            # 七里香,使用 __getitem__
playlist[1] = "稻香"          # 使用 __setitem__
print("稻香" in playlist)     # True,使用 __contains__

迭代器支持:__iter____next__

class CountDown:
    def __init__(self, start):
        self.current = start
    def __iter__(self):
        return self
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        self.current -= 1
        return self.current + 1
for num in CountDown(5):
    print(num, end=" ")  # 5 4 3 2 1 

上下文管理器:__enter____exit__

class FileManager:
    def __init__(self, filename, mode='r'):
        self.filename = filename
        self.mode = mode
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        # 返回 False 会传播异常,True 会抑制异常
        return False
# 使用 with 语句自动管理资源
with FileManager('test.txt', 'w') as f:
    f.write('Hello, Python!')

运算符重载:__add__, __sub__, __mul__

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)       # Vector(4, 6),使用 __add__
print(v1 * 3)        # Vector(3, 6),使用 __mul__

属性访问控制:__getattr__, __setattr__, __getattribute__

class Config:
    def __init__(self):
        self.settings = {
            'host': 'localhost',
            'port': 8080
        }
    def __getattr__(self, name):
        # 当常规属性查找失败时调用
        if name in self.settings:
            return self.settings[name]
        raise AttributeError(f"Config has no attribute '{name}'")
    def __setattr__(self, name, value):
        # 拦截所有属性设置
        if name == 'settings':
            super().__setattr__(name, value)
        else:
            # 存储到 settings 字典中
            if not hasattr(self, 'settings'):
                self.settings = {}
            self.settings[name] = value
config = Config()
print(config.host)      # localhost,通过 __getattr__
config.port = 9090      # 通过 __setattr__ 存储
print(config.port)      # 9090

可调用对象:__call__

class Logger:
    def __init__(self, prefix):
        self.prefix = prefix
    def __call__(self, message, level='INFO'):
        print(f"[{self.prefix}] [{level}] {message}")
logger = Logger("APP")
logger("应用启动")              # [APP] [INFO] 应用启动
logger("错误发生", "ERROR")     # [APP] [ERROR] 错误发生

完整案例:自定义字典类

class CaseInsensitiveDict:
    """键大小写不敏感的字典"""
    def __init__(self, data=None):
        self._data = {}
        if data:
            for key, value in data.items():
                self[key] = value
    def __setitem__(self, key, value):
        self._data[key.lower()] = value
    def __getitem__(self, key):
        return self._data[key.lower()]
    def __delitem__(self, key):
        del self._data[key.lower()]
    def __contains__(self, key):
        return key.lower() in self._data
    def __len__(self):
        return len(self._data)
    def __iter__(self):
        return iter(self._data)
    def __repr__(self):
        return str(self._data)
# 使用示例
d = CaseInsensitiveDict({"Name": "Alice", "Age": 30})
print(d["NAME"])           # Alice,大小写不敏感
print(d["name"])           # Alice
d["AGE"] = 31              # 覆盖原有的 Age
print("age" in d)          # True
print(len(d))              # 2
for key in d:
    print(key, d[key])     # name Alice, age 31

魔法方法的应用核心价值:

  1. 让自定义类更像内置类型 - 支持 len()in、 等操作
  2. 实现特定协议 - 迭代器、上下文管理器、数字类型等
  3. 控制对象行为 - 属性访问、运算符重载、字符串表示
  4. 简化使用接口 - 通过 __call__ 使对象可调用,通过 __enter__/__exit__ 支持 with 语句

你在项目中遇到哪些需要自定义魔法方法的场景吗?我可以帮你分析如何实现。

抱歉,评论功能暂时关闭!