Position of defining a function - python

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

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

Is there any way to change variables or functions defined outer in a function

Assuming that I have foo.py like below.
This file is not created by me, so I want neither to modify nor copy this.
In other words, foo.py is in some extra package I installed.
# foo.py
def bar():
print('This is bar')
def foo():
print('something')
bar()
print('something')
Then, I want to implement foo_as_baz() behaving as comments.
# baz.py
from foo import foo
def baz():
print('This is baz')
def foo_as_baz():
"""
This function is expected to behave as below
print('something')
baz()
print('something')
"""
pass
I tried below one but it does not work since the namescope differs.
def foo_as_baz():
bar = baz # I expect this `baz` affects `bar` function in `foo`
foo()
For the record: don't do the thing below the horizontal rule in normal code. It's confusing and largely pointless.
If you're doing this kind of thing to test your code, I recommend the standard library unittest.mock package, which provides a systematic way to replace Python objects with fake Python objects for testing purposes.
If you think you have some real non-testing use case, please elaborate...
# foo.py
def bar():
print("This is bar")
def foo():
print("something")
bar()
print("something")
And then then in another file:
import foo
def baz():
print("This is baz")
def foo_as_baz():
original = foo.bar # save a copy of the original bar()
foo.bar = baz # make foo.bar point to baz()
foo.foo()
foo.bar = original # cleanup by restoring bar()
foo_as_baz()
The key difference from your attempt is that I did import foo, which lets me look into the actual module namespace foo. I swap foo.bar for baz, then call foo.foo(), then switch foo.bar back to the original bar function.
This works because functions are effectively attributes of modules. Therefore, we can reassign those attributes as if they were attributes of any other object (because, in Python, everything, including modules, are objects).
You can pass the function itself as an argument to foo by slightly modifying how foo works
def foo(func=bar):
print('something')
func()
print('something')
def foo_as_baz():
foo(baz)
By adding bar as the default argument to foo you retain the original behaviour

Python decorator for function subsegments

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

Override function inside function using custom function defined just beforehand

I know the title is very confusing but essentially here is what I am trying to do:
def foo():
bar(name)
def evaluate_foo(name_table):
def bar(name, lookup_table=name_table):
print(name + "'s last name is " + lookup_table[name])
foo()
Basically, if I know that foo is going to make a call to bar, can I replace bar with my own function right before calling foo?
I am trying to provide a lookup table to bar, but the lookup table is generated inside of evaluate_foo. It's also important to me that someone using the function bar doesn't need to know about the lookup table that is being used by bar within it.
If I understand your question correctly, you can pass your function to foo to be executed. For instance you could do:
def foo(f):
f()
def evaluate_foo(some_param):
baz = some_param.get_special_number()
def bar(bar_param=baz):
print("Bar's result is " + str(baz))
foo(bar)
This won't work because foo() will lookup the function referred to by bar in the namespace it's defined in, not the namespace it's called in.
This sort of situation is what object inheritance is for, instead of having function foo() you should redesign it to be object whatever with method whatever.foo() and whatever.bar(). Then you can subclass whatever and write a new method bar() that foo() will find because it's defined in the class namespace.
Presumably you want to do this because you don't control foo() and so you can't convert it from a function into a method. In that case you can achieve this by monkey-patching bar() in the same namespace where foo() is defined, but that's some deep dark voodoo that is generally discouraged.
If I understand you correctly, you want to override a function definition globally. The following is not pretty, but it works (with some caveats below):
def foo():
print("foo")
def baz():
print("baz")
foo()
def bar():
def local_foo():
print("local foo")
global foo
foo = local_foo
baz()
if __name__ == "__main__":
bar()
The output is
baz
local foo
So, the caveats:
You can only do this for global functions, unless you want to jump through even more hoops.
The second point is that, unless you're just doing this for fun or are desperate, you should use other methods (as described by others) to achieve the same effect. E.g. some kind of design pattern.

Categories