Reputation: 4010
I'm learning python. I just finished a tutorial on decorators. I went and found a decorator in some code, but see yet more strange and unfamiliar things.
def state(allowed=['*']):
def decorator(func):
func.__fsm_state__ = True
func.__fsm_allowed__ = allowed
return func
if callable(allowed):
func, allowed = allowed, ['*']
return decorator(func)
return decorator
I dunno what the following line does:
func, allowed = allowed, ['*']
Can someone explain?
Upvotes: 1
Views: 81
Reputation: 432
State is, in this case, not a decorator directly, but rather a meta-decorator, or a decorator-generating function: It is not applied to a function directly, but applied to some other arguments, which it will use to return a "real" decorator:
def a(myargs): # applied to some arguments
def b(func): # decorator
do_smth(func, myargs)
return b # calling a will return the decorator
@a("world")
def hello(): # do_smth(hello, "world") is called
pass
When you type
@state(["something"])
def foo():
pass
this will invoke the state function using ["something"] as the argument, which will in turn return the decorator function, which is finally applied to the function foo, setting the __fsm_state__
and __fsm_allowed__
attributes, depending on the parameters originally passed to @state.
When you instead use
@state()
def foo():
pass
allowed (and, in turn, __fsm_allowed__
) will be set to the default value of ["*"]
, which you can see in the declaration of the state function.
Buf if you miss the brackets, that is,
@state # <- no () there
def foo():
pass
The function foo is taken to be the parameter to state (so allowed
is now foo instead of that list it's actually supposed to be), which might lead to subtle bugs - which is why in the definition of state, there is the check
if callable(allowed):
which catches the mistake of passing foo directly, and just assumes you meant the default arguments (allowed=["*"]
)
The following code,
func, allowed = allowed, ['*']
return decorator(func)
Which can be slightly simplified to
func = allowed
allowed = ["*"]
return decorator(func)
Which effectively means that @state and @state() now do exactly the same thing.
In my opinion, the check should rather be an assertion, so you can quickly find and fix such inconsistencies in your code, but whoever wrote that decided to just ignore them silently.
Upvotes: 1
Reputation: 33335
Also, I dunno what the following line does:
func, allowed = allowed, ['*']
It's a slightly shorter way of writing
func = allowed
allowed = ['*']
Search for "tuple assignment" for more information.
Upvotes: 1