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

Python装饰器内部变量及成员属性修改实现方案

问题描述

初始实现的类式EventCounter装饰器代码如下:

from functools import wraps

class EventCounter(object):
    def __init__(self, schedules=None, matters=None):
        self.counter = 0
        self.schedules = schedules
        self.matters = matters
        if not isinstance(schedules, list) or not isinstance(matters, list):
            raise ValueError("schedules and matter must be list.")
        if not all([schedules, matters]):
            raise ValueError("Need to set schedules and matters both.")

    def __call__(self, f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            if self.schedules:
                if self.counter == self.schedules[0]:
                    self.schedules.pop(0)
                    if self.matters:
                        self.matters.pop(0)
            wrapper.counter = self.counter
            wrapper.schedule = self.schedules[0] if self.schedules else None
            wrapper.matter = self.matters[0] if self.matters else None
            self.counter += 1
            return f(*args, **kwargs)
        return wrapper

if __name__ == '__main__':
    @EventCounter([2, 4, 8, 16], [0, 1, 2, 3, 4])
    def reset():
        print(f'{reset.counter}: {reset.matter}')

    for _ in range(20):
        reset()

待解决的问题如下:

  • 是否可以修改Python装饰器内部的变量值?
  • 现有场景下需要在被装饰的reset函数中将counter值重置为0,尝试实现的代码如下,但逻辑无法正常生效;同时还需要支持修改装饰器的schedules、matters等成员属性,该如何实现?
def reset():
    print(f'{reset.counter}: {reset.matter}')
    if reset.counter == 12:
        reset.counter = 0
解决方案

感谢@ShadowRanger的建议,更简洁的实现方式如下:
将原类装饰器改写为函数式装饰器,把counter、schedules、matters属性直接绑定到wrapper函数对象上,即可支持在被装饰函数内直接修改属性,实现counter重置、属性动态更新的需求。

from collections import deque
from functools import wraps

def EventCounter(schedules, matters):
    if not (schedules and matters):
        raise ValueError("schedules and matters must be non-empty.")

    def wrap_func(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            wrapper.schedules = deque(wrapper.schedules)
            wrapper.matters = deque(wrapper.matters)
            if wrapper.schedules and wrapper.counter == wrapper.schedules[0]:
                wrapper.schedules.popleft()
                if wrapper.matters and len(wrapper.matters) != 1: # 保留最后一个matter
                    wrapper.matters.popleft()
            wrapper.schedule = wrapper.schedules[0] if wrapper.schedules else None
            wrapper.matter = wrapper.matters[0] if wrapper.matters else None
            wrapper.counter += 1
            return func(*args, **kwargs)

        wrapper.counter = 0  # 返回前初始化wrapper的counter属性为0
        wrapper.schedules = deque(schedules)
        wrapper.matters = deque(matters)
        return wrapper
    return wrap_func

if __name__ == '__main__':
    @EventCounter([2, 4, 8, 16], [0, 1, 2, 3])
    def reset():
        print(f'{reset.counter}: {reset.matter}')
        if reset.counter == 12:
            reset.counter = 0
            reset.schedules = [1,5,7,11]
            reset.matters = [10, 20, 30, 40, 50]

    for _ in range(30):
        reset()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 17:42:24