I have the following code:
collector_1 = {}
"""
The usage of spam() decorator is to append a function's name
(the function being decorated by the spam() decorator) as a key
and the function itself (unexecuted) as the value to collector_1.
"""
def spam(collector):
def decorator(function):
collector.update({function.__name__: function})
def wrapper():
print("Wrapper is called.")
return function()
return wrapper
return decorator
#spam(collector_1)
def egg():
print("spam & egg is good.")
I then ran the egg function with this single line of code: egg(); and this is the result that I expected:
The wrapper is called.
spam & egg is good.
But when I ran it using the collector_1 dictionary:
collector_1["egg"]()
It only printed out one message:
spam & egg is good.
My task here is to call the function egg using the collector_1 dictionary, so I can import the collector_1 dictionary from different files to use it; is there any way to resolve this? Any help would be greatly appreciated!
You're putting the original function in your collector, not the decorated version, which would be wrapper inside your spam function.
Try this instead:
def spam(collector):
def decorator(function):
def wrapper():
print("Wrapper is called.")
return function()
collector[function.__name__] = wrapper
return wrapper
return decorator
Related
If I create a decorator like following:
def my_decorator(some_fun):
def wrapper():
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
return wrapper
#my_decorator
def just_some_function():
print("Wheee!")
Another decorator can be defined as:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
#my_decorator
def just_some_fun():
print("some fun")
Both decorators will work the same. What is the benefit of using "wrapper" function inside decorator. I didn't get the purpose.
The purpose of having a wrapper function is that a function decorator receives a function object to decorate, and it must return the decorated function.
Your 2nd version of my_decorator doesn't have an explicit return statement, so it returns None. When my_decorator is called via the # decorator syntax
before some_function() is called.
some fun
after some_function() is called.
gets printed, and then None gets assigned to the name just_some_fun. So if you add print(just_some_fun) to the end of that code it will print None.
It may be easier to understand what's going on if we get rid of the # syntactic sugar and re-write your code using normal function calling syntax:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
def just_some_fun():
print("some fun")
just_some_fun = my_decorator(just_some_fun)
The decorators in Python are callable objects which in the simplest case is function taking one parameter which is some function or class. The decorator should return again same type which it takes (so if it takes function it should return function). The point is in the time when the decorator is called.
When you import a Python file or just run it directly the Python interpreter goes through the content and gathering information about what classes and function are defined and if it encounter on some code (not declaration) it will execute it.
If the interpreter encounter on decorator it takes the decorated function, call the decorator and replace the decorated function with returned value from the decorator.
Let's say you have this code:
#my_decorator
def my_function()
print("My_function")
It's equivalent to this call:
def my_function()
print("My_function")
my_function = my_decorator(my_function)
If the decorator would be like this one
def my_decorator(func):
print("decorated)
return 42
then the my_function is not even a function it would be an integer (you can try print(my_function))
So when you define decorator as
def my_decorator2(some_fun):
print("before")
some_fun()
print("after")
then this decorator returns nothing (in python it means it returns None).
#my_decorator2
def decorated():
print("inside")
prints
before
inside
after
but calling decorated() will raise exception 'NoneType' object is not callable because the decorated was replaced with None.
You should always create decorator which return something useful like function or class (which is usually the "wrap" function inside). Sometimes can be useful to return from decorator something else then function/class but it usually obfuscate your code and convert it into something totally non-maintainable.
Its already explained why to use wrapper function, out of curiosity I am just giving
examples what we can do if we don't need a wrapper function.
Type 1
Return a small function which returns None or pass
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
def a(): None # or def a(): pass
return (a)
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function()
# equivalent to
#to_be_decorated_function = decorator_func(to_be_decorated_function)
Type 2
This merely tears out the usage of decorators, and just a slight tweak.What if we don't return , as well not use callable object at all.
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)
Output from both the types
Logging IN: Currrently in function
October 16, 2000
Logging OUT: Currrently in function
I have a decorator that records the functions present in my script:
registry=[]
def register(func):
print('running register(%s)' % func)
registry.append(func)
return func
I then have a series of decorated functions:
#register
def func1():
print('running f1')
#register
def func2():
print('running f2')
This works, after running the script, print(registry) returns:
[<function func1 at 0x0000000008433950>, <function func2 at 0x0000000008F06AE8>]
However calling the functions individually, for example:
func1()
Returns only 'running f1': just the function, without the decoration.
I was expecting it to return something like 'running register( func1) \n running func1'.
So my question is, when you have a decorated function and call it; when will it call the function in isolation and when will it call the decorated function?
Thanks very much.
Your register (decorator) function is only run once when the code is interpreted.
If you want to alter the function behaviour, try something like:
def register(func):
registry.append(func)
print('adding register(%s)' % func)
def wrap(*args, **kwargs):
print('running register(%s)' % func)
return func(*args, **kwargs)
return wrap
The first print is done once, the second one before each call.
Adding the arguments will make your decorator more transparent.
What we call a "decorator" is just a higher order function, and the #decorator syntax nothing more than syntactic sugar so this:
#decorate
def func():
pass
is strictly equivalent to
def func():
pass
func = decorate(func)
As mentionned by Guillaume Deslandes, if this code is at your module or script's top-level, the decorator is only invoked when the module or script is first loaded by the runtime.
In your case, the decorator function register returns it's argument (the function it's applied to) unchanged, so calling the "decorated" function will work exactly as if it never had been decorated.
If you want to modify the decorated function in any way (by executing code before and or after the original function or whatever), you have to return a new function that will "replace" the original (but - usually - keeping a reference to the original function so this new "wrapper" function can still call the original), which is usually done using the fact that Python functions are closure:
def decorate(fun):
def wrapper(*args, **kw):
res = fun(*args, **kw)
print("function {} called with *{}, *{} returned {}".format(fun, args, kw, res)
return res
return wrapper
#decorate
def fun(a):
return a * 2
I am learning to write the python decorators. Following are two examples.
Example 1:
def dec():
def wrapper(func):
print(func)
return func
return wrapper
#dec
def hello():
print('hello')
Example 2:
def dec(name):
def wrapper(fn):
print(fn)
return fn
return wrapper
#dec('a')
def hello(x):
print(x)
So my question is why the example 1 throws an exception that dec() takes 0 positional arguments but 1 was given while the example 2 works well.
Bot examples are incorrect. the outer function of a decorator (dec in this case) must take an argument which is the function it decorates.
So example 1 should look like this:
def dec(func):
def wrapper():
print(func)
return func
return wrapper
#dec
def hello():
print('hello')
hello()
Example two while doesn't crash immediately, would if you tried to call hello (the decorated function).
For a decorator to take an argument you need another level of nesting.
So example 2 should look like this:
def dec_args(name):
def dec(func):
def wrapper():
print(func)
print(name)
return func
return wrapper
return dec
#dec_args('a')
def hello():
print('hello')
hello()
The inner decorator function (wrapper) should take the argument that are passed to the decorated function (hello). In these cases you don't have any
Also note that the wrapper function doesn't really need to return func. wrapper is essentially a replacement of func. That's what decorators do. They replace the function you decorate with the inner function of the decorator (by calling the outer function with the decorated function as an argument).
If I create a decorator like following:
def my_decorator(some_fun):
def wrapper():
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
return wrapper
#my_decorator
def just_some_function():
print("Wheee!")
Another decorator can be defined as:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
#my_decorator
def just_some_fun():
print("some fun")
Both decorators will work the same. What is the benefit of using "wrapper" function inside decorator. I didn't get the purpose.
The purpose of having a wrapper function is that a function decorator receives a function object to decorate, and it must return the decorated function.
Your 2nd version of my_decorator doesn't have an explicit return statement, so it returns None. When my_decorator is called via the # decorator syntax
before some_function() is called.
some fun
after some_function() is called.
gets printed, and then None gets assigned to the name just_some_fun. So if you add print(just_some_fun) to the end of that code it will print None.
It may be easier to understand what's going on if we get rid of the # syntactic sugar and re-write your code using normal function calling syntax:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
def just_some_fun():
print("some fun")
just_some_fun = my_decorator(just_some_fun)
The decorators in Python are callable objects which in the simplest case is function taking one parameter which is some function or class. The decorator should return again same type which it takes (so if it takes function it should return function). The point is in the time when the decorator is called.
When you import a Python file or just run it directly the Python interpreter goes through the content and gathering information about what classes and function are defined and if it encounter on some code (not declaration) it will execute it.
If the interpreter encounter on decorator it takes the decorated function, call the decorator and replace the decorated function with returned value from the decorator.
Let's say you have this code:
#my_decorator
def my_function()
print("My_function")
It's equivalent to this call:
def my_function()
print("My_function")
my_function = my_decorator(my_function)
If the decorator would be like this one
def my_decorator(func):
print("decorated)
return 42
then the my_function is not even a function it would be an integer (you can try print(my_function))
So when you define decorator as
def my_decorator2(some_fun):
print("before")
some_fun()
print("after")
then this decorator returns nothing (in python it means it returns None).
#my_decorator2
def decorated():
print("inside")
prints
before
inside
after
but calling decorated() will raise exception 'NoneType' object is not callable because the decorated was replaced with None.
You should always create decorator which return something useful like function or class (which is usually the "wrap" function inside). Sometimes can be useful to return from decorator something else then function/class but it usually obfuscate your code and convert it into something totally non-maintainable.
Its already explained why to use wrapper function, out of curiosity I am just giving
examples what we can do if we don't need a wrapper function.
Type 1
Return a small function which returns None or pass
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
def a(): None # or def a(): pass
return (a)
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function()
# equivalent to
#to_be_decorated_function = decorator_func(to_be_decorated_function)
Type 2
This merely tears out the usage of decorators, and just a slight tweak.What if we don't return , as well not use callable object at all.
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)
Output from both the types
Logging IN: Currrently in function
October 16, 2000
Logging OUT: Currrently in function
I have found out that decorator arguments are passed at decorator definition rather than invocation like with functions.
Now I wonder if it is possible to make the decorater get the value of a variable at runtime like this, the decorater should print the current value of state instead of the one it head at definition:
def deco(msg):
def decorater(func):
def wrapper(*args, **kwargs):
print msg
func(*args, **kwargs)
return wrapper
return decorater
def func():
local = {
"state": None
}
#deco(local["state"])
def test():
pass
def setState(newState):
local["state"] = newState
setState("start")
test()
setState("test")
test()
func()
In your example, deco() is a decorator factory; you're creating the decorator which will then immediately be invoked. More generally, you invoke a decorator at the time that you're defining the function that you're decorating.
You can do what you're trying to do with minimal changes by just not passing in state, and accessing it as a global from within wrapper(), in which case you don't need deco(); you could just use #decorator directly. That said, I think there are better ways to do what you're trying to do.
John you should read this. In python, the variable is not the object. You question, is it "possible to make the decorator get the value of a variable at runtime", doesn't make sense because of python's scoping rules. The decorator function does not generally have access to the scope where state is defined. There are several ways you could get the behavior you want though.
Without knowing the specifics of what you're trying to do, here are two that might work. The first uses closure:
state = None
def with_closure(f):
def helper(*args, **kwargs):
# state is in scope for this function
print "Current state is: {}.".format(state)
return f(*args, **kwargs)
return helper
#with_closure
def foo():
return "something"
Or you could make an object to keep track of state:
class StateHolder:
def set_state(self, state):
self.state = state
def with_state_object(state_object):
def decorator(f):
def helper(*args, **kwargs):
print "Current state is: {}.".format(state_object.state)
return f(*args, **kwargs)
return helper
return decorator
global_state = StateHolder()
global_state.set_state("some state")
#with_state_object(global_state)
def foo():
return "something"