I've written a simple decorator:
from functools import wraps
import random
def my_dec(f):
lst = list()
@wraps(f)
def wrapper(*args):
lst.append(random.randint(0, 9))
print(lst)
return f(*args)
return wrapper
@my_dec
def foo():
print("foo called")
Now, if I call foo
multiple times lst
is not being flushed. Instead, it builds up over time. Thus, multiple calls of foo
return an output like this:
foo()
> [4]
> foo called
foo()
> [4, 9]
> foo called
foo()
> [4, 9, 1]
> foo called
...
Why is that? I thought a decorator
is just syntactic sugar for my_dec(foo)
?! I assumed that each call to my_dec
flushes lst
.
You're right... The decorator is just syntactic sugar. Specifically:
@decorator
def foo():
pass
is exactly the same thing as:
def foo():
pass
foo = decorator(foo)
Let's be a little more outlandish and rewrite this another way that is mostly equivalent1:
def bar():
pass
foo = decorator(bar)
del bar
Hopefully written out this way, you can see that if I call foo
a bunch of times, I'm not calling decorator
a bunch of times. decorator
only got called once (to help create foo
).
Now in your example, your decorator creates a list immediately when it gets called:
def my_dec(f):
lst = list() # list created here!
@wraps(f)
def wrapper(*args):
lst.append(random.randint(0, 9))
print(lst)
return f(*args)
return wrapper
The function returned wrapper
gets assigned to your foo
, so when you call foo
, you're calling wrapper
. Note that there is no code in wrapper
that would reset lst
-- only code that would add more elements to lst
so there is nothing here to indicate the lst
should get "flushed" between calls.
1(depending on what the decorator does, you might see some differences in the function's __name__
attribute, but otherwise it's the same thing...)
Also note that you'll have one lst
for each time the decorator is called. We can go crazy with this one if we like and decorate foo
twice:
@my_dec
@my_dec
def foo():
pass
Or we can decorate more than one function:
@my_dec
def foo():
pass
@my_dec
def bar():
pass
And then when we call foo
and bar
, we'll see that they each accumulate their own (distinct) lists of random numbers. In other words, each time your decorator is applied to something, a new list will be created and each time that "something" is called, the list will grow.