how to change the scope for recursive asyncio functions - scope

I have two async functions with local scope and one checking function with global scope. The two async functions reference each other (hence recursive behaviour).
All three functions are called from the main async function.
So the setup looks like this:
import asyncio
# globals
x = 0
async def main():
''' this is the main function '''
print('hello from main function')
print('local x:', x)
asyncio.ensure_future(what_is_x())
await first_async(x)
print('got here', x)
async def first_async(x):
''' this is a function '''
print('hello from first function')
x = x + 1
print('local x:', x)
await asyncio.sleep(2)
await second_async(x)
async def second_async(x):
print('hello from second function')
x = x + 1
print('local x:', x)
if x > 5:
print('exit second function...')
return
await asyncio.sleep(2)
await first_async(x)
async def what_is_x():
''' this is a function '''
while True:
print('what is x ?', x)
await asyncio.sleep(3)
if __name__ == "__main__":
syncio.run(main())
The result (of the above) is this:
hello from main function
local x: 0
hello from first function
local x: 1
what is x ? 0
hello from second function
local x: 2
what is x ? 0
hello from first function
local x: 3
what is x ? 0
hello from second function
local x: 4
hello from first function
local x: 5
what is x ? 0
hello from second function
local x: 6
exit second function...
got here 0
I try to avoid usage of the global keyword (supposedly best practice).
So my question is, what is the best way to ensure x is consistent throughout ?
And also, can i make what_is_x() block the other two async functions in the current setup ?

x in yours first_async and second_async functions is a normal, local variable, and changes in it are only withn the scope of each function. Each time a line with x = x + 1 is executed, the local variable changes its reference to a new int object.
THere is nothing "obscure" on this code. If you'd try to make x global in all three functions (by using the "global" statement, instead of passin g it as a parameter), it would be equaly easy to have code that would go into an infinite loop, and still have nothing of obscure going on.
The way it is, though, global x is never changed from its initial value.
If you really think you need everyone to sync using a global value, just do it - change your first_async and second_async functions to read:
async def first_async():
global x
...
instead. Also, there are context_vars that can handle the scope of variables per task, instead of globally: you'd also do not want to pass "x" as a parameter - but maybe it is more useful to whatever is your intent there - https://docs.python.org/3/library/contextvars.html

Related

Is there a way to get give one variable to a function and retrieve a different variable back from the second function and use it in the first function

I am trying to use the output of one function in a second function then retrieving a variable from the second function to be used in the first function. The code below is a simplification of what I am trying to do
def function1():
x=15
return x
function2(y)
print(x+y)
def function2():
y=x-12
return y
function1()
I am not getting an actual value for y when I check by adding a print statement for x in function2. Is there a way to do this or do i need to make a 3rd function to handle this?
Pass variable x to function2, and store the return value from function2 in a local variable in function 1.
def function1():
x=15
y = function2(x)
print(x+y)
return x
def function2(x):
y=x-12
return y
function1()

Problem with calling a variable from one function into another

I am trying to call a variable from one function into another by using the command return, without success. This is the example code I have:
def G():
x = 2
y = 3
g = x*y
return g
def H():
r = 2*G(g)
print(r)
return r
H()
When I run the code i receive the following error NameError: name 'g' is not defined
Thanks in advance!
Your function def G(): returns a variable. Therefore, when you call it, you assign a new variable for the returned variable.
Therefore you could use the following code:
def H():
G = G()
r = 2*G
print (r)
You don't need to give this statement:
return r
While you've accepted the answer above, I'd like to take the time to help you learn and clean up your code.
NameError: name 'g' is not defined
You're getting this error because g is a local variable of the function G()
Clean Version:
def multiple_two_numbers():
"""
Multiplies two numbers
Args:
none
Returns:
product : the result of multiplying two numbers
"""
x = 2
y = 3
product = x*y
return product
def main():
result = multiple_two_numbers()
answer = 2 * result
print(answer)
if __name__ == "__main__":
# execute only if run as a script
main()
Problems with your code:
Have clear variable and method names. g and G can be quiet confusing to the reader.
Your not using the if __name__ == "__main__":
Your return in H() unnecessary as well as the H() function.
Use docstrings to help make your code more readable.
Questions from the comments:
I have one question what if I had two or more variables in the first
function but I only want to call one of them
Your function can have as many variables as you want. If you want to return more than one variable you can use a dictionary(key,value) List, or Tuple. It all depends on your requirements.
Is it necessary to give different names, a and b, to the new
variables or can I use the same x and g?
Absolutely! Declaring another variable called x or y will cause the previous declaration to be overwritten. This could make it hard to debug and you and readers of your code will be frustrated.

Make variables declared in caller function available in the called function

I have two functions. They are not a part of any class. Just individual functions.
function f1 calls f2. What I would like to do is print in f2 the variables declared in f1.
How can I achieve this?
def f1():
a = 10
b = 20
f2()
def f2():
print(a)
print(b)
def f1():
a = 10
b = 20
f2(locals())
def f2(dic):
for key in dic:
print(key, '=', dic[key])
f1()
locals() returns the the local variables and values in a dictionary.
Calling f2 inside f1 captures the locals of f1 and is passed as
the argument dic for processing in f2.
You could define the variables outside of the function first and then use the global modifier as in the second example. In this simple case however it would probably make more sense to just pass the variables to f2() (example 1).
You could also take a look at Access a function variable outside the function without using `global` if this is what you are looking for?
You generally also don't want to have code that checks what the caller function is as in this example. In any case your code must not be able to reach a state where a and b do not (yet) exist.
example 1
def f1():
a = 10
b = 20
f2(a, b)
def f2(a, b):
print(a)
print(b)
As stated in the comments this is not what you were looking for, so that's why you also have this possibility:
example 2
a=5
b=5
def f1():
global a
global b
a = 10
b = 20
f2()
def f2():
print(a)
print(b)
Calling f1() will print 10 and 20.

How to use Global Variable in Python

I'm trying to write a simple code that uses a global variable. I'm getting the following error
UnboundLocalError: local variable 'x' referenced before assignment
global x
def update():
x = x + 1
x = 0
update()
print(x)
Your error occurred because in the function update, you are trying to edit a variable (x) that is not defined, at least not locally.
The global keyword should be inside the function, and hence tell that the x you are speaking about is the one defined outside of the function (therefore globally defined) :
def update():
global x
x = x + 1
x = 0
update()
print(x)
This would output 1, as expected.
You can take a look at this well detailed answer regarding the use of the global keyword.

Why does assigning lambdas not work as expected in Python 3.5?

I know Python is supposed to have first class functions, and I've written some Python which "confirms" that, but the following explodes (runs out of stack depth) my IDLE in 3.5:
k = lambda x:x+1
k = lambda x:k (x+1)
k(0)
Shouldn't it evaluate to 2?
k = lambda x:x+1
j = lambda x:k (x+1)
j(0)
certainly does.
Visualize with lambda
A little printing may help to understand what is going on:
k = lambda x: x+1
print('before:', id(k))
k = lambda x: print('in :', id(k))
k(0)
print('after :', id(k))
before: 4428659024
in : 4428658208
after : 4428658208
The id of the last lambda is the same as the one use inside it. This nicely demonstrates the late binding.
Translate into function statements
Things might a bit clearer if you translate the lambdas into function define with def:
def k(x):
return x + 1
def k(x):
return k(x+1)
k(0)
It is pretty clear that the first definition does make sense as it is overridden by the second one.
In addition, even you use different names for the functions and re-assign the function name:
def k(x):
return x + 1
def j(x):
return k(x+1)
k = j
it appears to be clearer that the first function definition doesn't make sense because the function can never be reached.
The name lookup of k is done at runtime, which means that the function calls itself an infinite number of times (and that the first line does nothing useful). If you want the result you expect then you will have to use the ternary operator to return immediately instead of recursing.

Resources