You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止Python绕过setter修改类字典属性,或触发校验?

问题1:能否阻止直接修改字典属性的行为?

可以阻止,核心是不让外部获取到原生的可修改容器对象,而是返回受限制的包装类或只读视图,常见实现方式有两种:

  • 使用只读视图:用types.MappingProxyType包装字典,外部仅能读取,无法直接修改键值对;对于嵌套的列表,可替换为tuple(不可变)或自定义只读列表类,拦截所有修改方法。
  • 自定义受限容器类:继承dict或list,重写__setitem__、append、__delitem__等修改类方法,直接抛出禁止修改的异常,强制用户只能通过类提供的方法(如add_item)操作数据。

示例代码(基于Bookcase场景,用只读视图实现):

import types

class Book:
    def __init__(self, title, weight):
        self.title = title
        self.weight = weight

class Bookcase:
    def __init__(self):
        # 内部用原生容器存储数据
        self._shelves = {
            1: {'books': [], 'max_weight': 10, 'current_weight': 0}
        }
    
    @property
    def shelves(self):
        # 返回字典的只读视图,外部无法修改字典结构
        read_only_shelves = types.MappingProxyType(self._shelves)
        # 进一步将每个书架的books转为只读列表
        for shelf in read_only_shelves.values():
            shelf['books'] = tuple(shelf['books'])
        return read_only_shelves
    
    def add_book(self, shelf_num, book):
        shelf = self._shelves.get(shelf_num)
        if not shelf:
            raise ValueError(f"Shelf {shelf_num} does not exist")
        if shelf['current_weight'] + book.weight > shelf['max_weight']:
            raise ValueError(f"Adding {book.title} exceeds shelf {shelf_num} weight limit")
        shelf['books'].append(book)
        shelf['current_weight'] += book.weight

此时用户执行library.shelves[1]['books'].append(big_book)会抛出AttributeError,因为tuple没有append方法,彻底阻止了绕过行为。


问题2:能否通过魔术方法触发add_item?

可以通过自定义容器类并重写对应魔术方法,拦截所有修改操作,自动触发校验逻辑。针对嵌套结构,需要分别处理字典和列表的修改方法:

  1. 自定义字典类,重写__setitem__拦截键值对修改;
  2. 自定义列表类,重写append、extend等方法拦截元素添加;
  3. 将自定义容器与原类关联,在修改方法中调用原类的校验逻辑(或直接复用add_item方法)。

示例代码(基于Bookcase场景):

class Book:
    def __init__(self, title, weight):
        self.title = title
        self.weight = weight

class BookList(list):
    def __init__(self, parent_shelf, *args):
        super().__init__(*args)
        self.parent_shelf = parent_shelf  # 关联所属书架
    
    def append(self, book):
        # 触发重量校验,逻辑与add_book一致
        if self.parent_shelf['current_weight'] + book.weight > self.parent_shelf['max_weight']:
            raise ValueError(f"Adding {book.title} exceeds shelf weight limit")
        super().append(book)
        self.parent_shelf['current_weight'] += book.weight
    
    # 如需支持其他添加方法,如extend、insert,需同理重写

class ShelveDict(dict):
    def __init__(self, parent_bookcase, *args):
        super().__init__(*args)
        self.parent_bookcase = parent_bookcase  # 关联所属书柜
    
    def __setitem__(self, key, value):
        # 确保books只能是BookList实例,避免被替换为普通列表
        if key == 'books' and not isinstance(value, BookList):
            raise TypeError("Books must be stored in BookList")
        super().__setitem__(key, value)

class Bookcase:
    def __init__(self):
        self.shelves = {}
        self.add_shelf(1, max_weight=10)
    
    def add_shelf(self, shelf_num, max_weight):
        shelf = ShelveDict(self)
        shelf['books'] = BookList(shelf)
        shelf['max_weight'] = max_weight
        shelf['current_weight'] = 0
        self.shelves[shelf_num] = shelf
    
    def add_book(self, shelf_num, book):
        # 复用BookList的append逻辑,避免代码重复
        self.shelves[shelf_num]['books'].append(book)

此时用户无论是调用library.add_book(1, big_book)还是直接执行library.shelves[1]['books'].append(big_book),都会触发重量校验,无法绕过限制。


内容的提问来源于stack exchange,提问作者Joshua Mincer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 02:54:54