'Keep state in decorator
I am trying to write a @assert_logged_in decorator.
On paper, it's easy:
def assert_logged_in(meth: Callable):
logged_in = False
def wrapper(self, *args, **kwargs):
if not logged_in:
try:
test_login()
except as err: # bare except only for this example
raise NotLoggedIn from err
logged_in = True # <= that is what I do not understand ‽‽‽
return meth(self, *args, **kwargs)
return wrapper
# somewhere else
@assert_logged_in
def do_something(self):
pass
As long as I do not try to update logged_in
, the code runs, but would test loggedinness every time.
If I try to update logged_in
as in my example, I receive: UnboundLocalError: local variable 'logged_in' referenced before assignment
. Even if I try to use global
. Note that I do not want to keep the state in self (which works), because this decorator will be used across multiple classes.
I am very confused because this example:
def memoize(f):
memo = {}
def memoized_func(n):
if n not in memo:
memo[n] = f(n)
return memo[n]
return memoized_func
does work as intended and keeps state.
There is obviously something I do not understand. What could it be, and how could I get my decorator working?
Solution 1:[1]
From the comments, there are 2 options:
Cleanest option:
Declare logged_in
normally outside the wrapper, and then again as nonlocal
inside the wrapper.
Other possibility
- from a wrapper, you cannot not change an external variable
- but if this variable happens to be a reference (dict, collections...) then the wrapper can update its content without changing its reference.
For instance, have a dict instead of a boolean:
logged_in = {"logged_in": False}
instead of
logged_in = False
Anotherj comment five
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |