Python decorator for function subsegments - python

In Python, is there a nice way to apply a decorator (or something similar) not to a whole function, but to a subsegment of a function body?
What I want might look something like this:
def deco_double(f):
def wrapper():
f()
f()
return wrapper
def foo():
#deco_double:
print("hello")
print("stack")
#deco_double:
print("overflow")
foo()
So the execution result be like:
hello
stack
hello
stack
overflow
overflow
It's okay if the solution is not exactly a decorator, what I need is a method to wrap function subsegments, but in a well abstracted way. Thank you in advance :)
edit:
Just separating into multiple functions is not an option for me. I'm developing a framework for programmers and I want them to write functions in a compact way (for writing foo() in the example. Also, what I'm actually doing is far more complicated than just repetitions)
edit2:
By now it seems I have no choice but to expect framework users to somehow declare named functions...

Function bodies are compiled into a single "code" object that is run as a whole - changing the way this code object is run, inserting things in it, and such are things that can be as complicated as the language code itself (i.e. the code that actually "executes" Python bytecode).
So, any changes in the flow of execution are far easier done by using the statements in the language that already do that.
If you want the equivalent of a generic decorator in parts inside a function body, the easiest thing to do is to subdivide that function itself into inner functions - and then you can apply your transforms, or execute each part more than once, by simply calling those functions (and even decorate them directly).
However, in the case you bring in your question, you could, and probably should, just use a plain old for loop:
def foo():
for _ in (0, 1):
print("hello")
print("stack")
for _ in (0, 1):
print("overflow")
For arbitrary "decorator like behavior", as I wrote above, just use nested functions:
def foo():
#deco_double
def part1()
print("hello")
print("stack")
part1()
#deco_double:
def part2():
print("overflow")
part2()

You will have to extract that partial functionality of foo() to separate functions and then apply the decorator as a function and call it expilictly.
def deco_double(f):
def wrapper():
f()
f()
return wrapper
def my_f1():
print("hello")
print("stack")
def my_f2():
print("overflow")
def foo():
deco_double(my_f1)()
deco_double(my_f2)()
foo()

Simply declare two functions with any name with decorator #deco_double and call them in any other function, in your case foo() and then simply call foo().
def deco_double(f):
def wrapper():
f()
f()
return wrapper
#deco_double
def func1():
print("hello")
print("stack")
#deco_double
def func2():
print("overflow")
def foo():
func1()
func2()
foo()
Output of this code.
hello
stack
hello
stack
overflow
overflow

You'll likely need to pass functions using lambda if you want to achieve this:
def doubler(f):
f()
f()
def foo():
doubler(lambda: print("hello"))
doubler(lambda: print("world"))
foo()
"hello"
"hello"
"world"
"world"
Wrap the code you want to be run twice in the lambda, and pass the function to doubler

Related

How to call function that will be defined later?

As title. I want to move code segment if __name__ == '__main__': before all functions that it will call.(I found this more readable, for myself) To achieve this I need to call functions that will be defined later. Is this possible?
While this has been answered in the comment section. To provide more context: I read one line of code:
cur_mod = sys.modules[__name__]
which let me came to this question. (i.e. I thought it would be possible to call something defined later by import itself)
What you can do is to wrap the invocation into a function of its own.
So that
foo()
def foo():
print "Hi!"
will break, but
def bar():
foo()
def foo():
print "Hi!"
bar()
will be working properly.
General rule in Python is not that function should be defined higher in the code but that it should be defined before its usage.

Memory management for functions and decorators

My question starts with how decorators work in python. Let's look at the following code:
def decorator(F):
def wrapper():
print("start")
F()
print("end")
return wrapper
def f1():
print("f1")
decorated_f1 = decorator(f1)
decorated_f1()
It prints,
start
f1
end
First of all, as much as I know, python uses lazy evaluation. Therefore, it does not evaluate F(), until it is required (when actually decorated_f1() is called). By then, the scope of argument is F is over (end of the decorator function). I would like to know, what python stores in memory when a function is created and overall the memory management that happens for decorators.
The second part of my question is about the results that I get after running the following codes,
def decorator(F):
def wrapper():
print("start")
F()
print("end")
return wrapper
def f1():
print("f1")
decorated_f1 = decorator(f1)
def f1():
print("new f1")
decorated_new_f1 = decorator(f1)
decorated_f1()
decorated_new_f1()
it results in,
start
f1
end
start
new f1
end
However, the following code
def f1():
print("f1")
F = f1
def wrapper():
print("start")
F()
print("end")
wrapped_f1 = wrapper
def f1():
print("new f1")
F = f1
def wrapper():
print("start")
F()
print("end")
wrapped_new_f1 = wrapper
wrapped_f1()
wrapped_new_f1()
produces,
start
new f1
end
start
new f1
end
This makes me confused, because I thought these two codes should be very similar in output. That is why I need help for clarifying what and how things are stored in memory when functions or decorators are declared in python.
You are correct that the second and third example are quite close in how they work. However there is one key difference. The scope the functions are evaluated in. The scope in this case can be seen as all the variables some place in the code has access too, and where those come from. You might have heard of the global scope, this is the scope all code is evaluated in and all variables that are global are accessible by any part of the code. There are also function scopes. These are the variables defined inside of the function, and other functions won't have access to these variables.
To further understand the difference there is one more think you should know. Functions are a reference type in Python, this means that when you declare one all later uses reference the value rather then directly accessing the value. This is the same as for lists and dictionaries.
Why does this all matter? Well because a decorator stores the function scope to correctly evaluate later. This includes the current value of F in this function
def decorator(F):
def wrapper():
print("start")
F()
print("end")
return wrapper
However, if you don't pass F as an argument then the decorator will store the function as a reference to the global function. Thus when you chance F in the third example the F() in wrapped_f1 also changes. This explains the output you see.
Lastly, as a tip. There is a specific syntax for using decorators on functions, and it might make using them easier. Usually a decorator would look like this.
def decorator(F):
def wrapper():
print("start decorating")
F()
print("End decorating")
return wrapper
def undecorated_func():
print("Hellor world!")
#decorator
def decorated_func():
print("Hello World!")
undecorated_func()
decorated_func()
Then the output is
Hello world!
Start decorating
Hello World!
End decorating

Position of defining a function

Is it necessary to define a function on the top of a code or can we define it in the middle also (i.e in the __main__ segment)? Like we define a function in the middle will it result in error during execution and flow of control?
You can define a function in Python anywhere you want. However, it won't be defined, and therefore callable, until the function definition is executed.
If you're familiar with many other languages this feels odd, as it seems that most compilers/interpreters will identify functions anywhere in your code before execution and they will be available anywhere in your code. Python interpreters do not do that.
The following 2 code samples are both syntactically correct, but the second will fail because hello() isn't defined until after it is called:
Example 1 (works!):
def hello():
print('Hello World')
hello()
Example 2 (fails! - name 'hello' is not defined):
hello()
def hello():
print('Hello World')
Just take a look to Python's definition.
Python is an interpreted high-level general-purpose programming language. (see: https://en.wikipedia.org/wiki/Python_(programming_language))
The interpreted is the key. We can think as if python executes the code line by line before checking the whole file. (It is a bad analogy, but for sake of this problem let's think it is true)
Now there can be many scenarios:
Running a function after declaration
foo()
def foo():
print("foo")
This would fail
Running a function before declaration
def foo():
print("foo")
foo()
this would succeed
Calling a function inside a function
def foo():
print("foo")
def bar():
foo()
bar()
or
def bar():
foo()
def foo():
print("foo")
bar()
These would succeed. Please notice in second example foo declared after bar. But still runs. See: Make function definition in a python file order independent
def foo():
print("foo")
bar()
def bar():
foo()
This would fail

Python decorator function execution

I have below decorator demonstration code. If I execute it without explicitly calling greet function, it is executing print statement inside decorator function and outputs Inside decorator.
I am unable to understand this behavior of decorator. How the time_decorator is called even if I didn't call greet function?
I am using Python 3.
def time_decorator(original_func):
print('Inside decorator')
def wrapper(*args, **kwargs):
start = time.clock()
result = original_func(*args, **kwargs)
end = time.clock()
print('{0} is executed in {1}'.format(original_func.__name__, end-start))
return result
return wrapper
#time_decorator
def greet(name):
return 'Hello {0}'.format(name)
Decorators are called at start time (when the python interpreter reads the code as the program starts), not at runtime (when the decorated function is actually called).
At runtime, it is the wrapped function wrapper which is called and which itself calls the decorated function and returns its result.
So this is totally normal that the print line gets executed.
If, i.e, you decorate 10 functions, you will see 10 times the print output. No need to even call the decorated functions for this to happen.
Move the print inside wrapper and this won't happen anymore.
Decorators as well as metaclasses are part of what is called meta-programming (modify / create code, from existing code). This is a really fascinating aspect of programming which takes time to understand but offers amazing possibilities.
time_decorator is executed during function decoration. wrapper is not (this is function invoked when decorated greet() is called).
# is just syntactic sugar. Following code snippets are equivalent.
Decorator syntax:
#time_decorator
def greet(name):
return 'Hello {0}'.format(name)
Explicit decoration process - decorator is a function that returns new function based on another one.
def greet(name):
return 'Hello {0}'.format(name)
greet = time_decorator(greet)

Can we apply python decorator at function invoke?

Say for example I have a function
def A(): pass
I call this function from two different places
def B():
#some code
A()
#some code
def C():
#some code
A()
#some code
Decorators as we know, in layman language is something like a hook which lets you perform a set of operation before and after the function call. We apply decorators on function definition using #.
My requirement here is like I don't want the decorator to be applied every time the function is called. Like in the above example if I apply decorator on function A. It would be applied in both invokes. Can we use # at the invoke of the function A?
Something like,
def A(): pass
def B():
#some code
A()
#some code
def C():
#some code
#decorator
A()
#some code
This way I don't have to worry about merging my code after every release of particular opensource project.
A decorator is just syntactic sugar, where the outcome of the #expression is called and the return value is used to replace the decorated object.
If you only need to have a decorated function in one location you can do decorator(A)() to have it return a decorated version just to be used in C and call that decorated object on the spot.
Better yet, store that decorated version for C to use so you don't keep decorating it each time:
A_decorated = decorator(A)
def C():
# some code
A_decorated()
# some code

Categories