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()
Is it possible to change the value of variable in decortor?
In this case, I want to reset counter
to 0
, like
def reset():
print(f'{reset.counter}: {reset.matter}')
if reset.counter == 12:
reset.counter = 0
But the code above doesn't work for me.
Any suggestion?
Also, I want to change members of Decorator like schedules
and matters
CodePudding user response:
Your problem is that int
s are immutable, and you're maintaining wrapper.counter
and self.counter
separately, resetting wrapper.counter
to self.counter
on each call (undoing your attempt to reset it via the wrapper). In this case, there is no real benefit to maintaining self.counter
as an instance variable (the EventCounter
object is discarded after decoration completes; it technically exists thanks to closing on self
in wrapper
, but accessing it would be nuts; frankly, the whole class is unnecessary and all of this could be done with simple closures), so the simplest solution is to store a single copy of the counter solely on the wrapper function:
from functools import wraps
class EventCounter(object):
def __init__(self, schedules=None, matters=None):
# Remove definition of self.counter
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 wrapper.counter == self.schedules[0]: # work solely in terms of wrapper.counter
self.schedules.pop(0)
if self.matters:
self.matters.pop(0)
# No need to copy to/from wrapper.counter here
wrapper.schedule = self.schedules[0] if self.schedules else None
wrapper.matter = self.matters[0] if self.matters else None
wrapper.counter = 1
return f(*args, **kwargs)
wrapper.counter = 0 # Initialize wrapper.counter to zero before returning it
return wrapper
if __name__ == '__main__':
@EventCounter([2, 4, 8, 16], [0, 1, 2, 3, 4])
def reset():
print(f'{reset.counter}: {reset.matter}')
if reset.counter == 12:
reset.counter = 0
for _ in range(20):
reset()