Python - Passing arguments back and forth between functions - python

Say I have some arguments passed to a function, I use those arguments to do some calculations, and then pass the results to another function, where they are further used. How would I go about passing the results back to the first function and skipping to a point such that data is not sent back to the second function to avoid getting stuck in a loop.
The two functions are in two different python scripts.
The way I'm currently doing it is by adding any new arguments supposed to be coming from the second script as non keyword arguments, and passing all the arguments from the first function to the second even if they are not needed in the second. The second function passes all the arguments back to the first, and an if condition on the non keyword argument to check whether it has its default value is used to determine if the data has been sent back by the second function.
In f1.py:
def calc1(a, b, c, d = []):
a = a+b
c = a*c
import f2
f2.calc2(a, b, c)
If d != []: # This checks whether data has been sent by the second argument, in which case d will not have its default value
print(b, d) # This should print the results from f2, so 'b' should
# retain its value from calc1.
return
In the another script (f2.py)
def calc2(a, b, c):
d = a + c
import f1
f1.calc1(a, b, c, d) # So even though 'b' wasn't used it is there in
# f2 to be sent back to calc1
return

Having two methods recursively call each other is usually a bad idea. It's especially bad between two files. It looks like you want to call calc1(), have it call calc2() internally, and then make a decision about what to do based on the result of calc2().
Is this what you are trying to do?
#### f1.py
import f2
def calc1(a, b, c):
a = a+b
c = a*c
d = f2.calc2(a, b, c)
# This checks whether data has been sent by the second argument,
# in which case d will not have its default value
if d:
# This should print the results from f2, so 'b' should retain
# its value from calc1.
print(b, d)
#### f2.py
def calc2(a, b, c):
return a + c

Related

Require parameter if another argument is present

Following is an example of what I would like to do.
def f(a, b, c):
if a == 'method1':
c = 0
return b + c
In this function, parameter c is unneeded if the condition a='method1' is satisfied.
Still, I can call the function with f(a='method1', b=1, c=2), which will have the same effect as f(a='method1', b=1, c=0).
A solution to this is to set the parameter c default to 0
def f(a, b, c=0):
if a == 'method1':
c = 0
return b + c
Now I can call f(a='method1',b=1), which is exactly what I want. The problem is, I can still change the parameter c in the call f(a='method1',b=1,c=1), which I do not want the user to be able to.
Can I enforce this condition in the function signature, and not in the body (i.e. I would not like to use if in the body). Or if there is another better solution, please tell.
Something like
def f(a, b, c = 0 if a == 'method1', else c is required):
return b + c
Thanks in advance.
a, b and c are all assigned dynamically at runtime. There is no way you can make up for this in the signature. It needs to be detected at runtime and you might as well do that in an if as anywhere else. You can specialize at the function name level, though, and python will take care of detecting the number of parameters.
def f(b,c):
return b + c
def f_method1(b):
return f(b, 0)
def f_method2(half_a_c):
return f(0, half_a_c*2)
Hmm... this almost seems like something that you should be able to do with functools.singledispatch and typing.Literal, but I can't quite get them to work together at least in python 3.7 (with Literal coming from the typing_extensions module). I think that in general singledispatch is probably the only tool that will really get what you've asked for as the different registered functions can have entirely different signatures. However, to do this our methods need to be different classes.
from functools import singledispatch
class Method1():
pass
class OtherMethods():
pass
#singledispatch
def f(method, b, c):
return b + c
#f.register(Method1)
def _(method, b):
return b
f(Method1(), 12) # returns 12
f(Method1(), 12, 7) # raises an error
f(OtherMethods(), 12) # raises an error
f(OtherMethods(), 12, 7) # returns 19
Now this is not exactly what you asked for but the arguments are enforced in the signature.
If someone who knows more about the implementation of singledispatch and Literal comes by maybe they can explain the interaction between the two.
One easier thing to do would be to simply define c to default to some invalid value.
def f(a, b, c=None):
if a == 'method1':
c = 0
return b + c
This solves the problem that if the user forgets to set c for a method besides method1 they'll receive a (possibly cryptic) error message. However it doesn't fix the fact that if they set c when using method1 that value will be silently ignored and possibly cause confusion.

Passing a function as an argument vs Calling it inside. Which one is recomended in Python

Lets say I have three functions in one module as defined below:
def add_nums(a, b):
return a + b
def sum_and_square_one(add_nums, a, b):
result = add_nums(a,b)
return result*result
def sum_and_square_two(a, b):
result = add_nums(a,b)
return result*result
Both functions sum_and_square_one and sum_and_square_two do the same task. But the former takes add_nums as an argument while the latter calls add_nums inside. My question is which one is the better way. Passing a function as an argument or calling inside a function?
If you always want to call the same function then there is no point in passing it as an argument every time. (this is the case in your example)
If you, however, want to dynamically call different (maybe similar) functions, then passing the appropiate function as an argument would make sense.
Depends on your use case. Do you need the function to change the way it works depending on that argument?
Or, rewriting your meaningless example into a bit less meaningless example (but still quite):
def add_nums(a, b):
return a + b
def do_something_then_square(a, b, what_to_do):
result = what_to_do(a, b)
return result * result
def sum_then_square(a, b):
result = add_nums(a, b)
return result * result
# Then you do:
sum_then_square(2, 5) # 49
do_something_then_square(2, 5, what_to_do=add_nums) # 49
do_something_then_square(2, 5, what_to_do=lambda a, b: a + b) # 49
do_something_then_square(2, 5, what_to_do=lambda a, b: a * b) # 100
do_something_then_square(2, 5, what_to_do=min) # 4
do_something_then_square(2, 5, what_to_do=complex) # (-21+20j)
Only question is: do you need that added flexibility? Otherwise it's just useless additional typing. That question must be answered on a case by case basis; it needs a complete example to give a useful answer.

Changing a local variable in a function from another function

First, here's my example code:
EDIT: I should have specified, in my real code, that_func() is already returning another value, so I want it to return one value, and change c in addition
EDIT 2: Code edited to show what I mean
def this_func():
c=1 # I want to change this c
d=that_func()
print(c, d)
def that_func():
this_func.c=2 #Into this c, from this function
return(1000) #that_func should also return a value
this_func()
What I want to do is change the local variable c in this_func() to the value I assign it in that_func(), so that it prints 2 instead of 1.
From what I've gathered online, this_func.c=2 should do just that, but it doesn't work. Am I doing something wrong, or have I misunderstood?
Thanks for any and all help.
Yes, you misunderstood.
functions are not class. You can't access variables of a function like that.
Obviously, it's not the smartest of code that can be written, but this code should give an idea about how to use variables of a function.
def this_func():
c=1 # I want to change this c
c=that_func(c) # pass c as parameter and receive return value in c later
print(c)
def that_func(b): # receiving value of c from this_func()
b=2 # manipulating the value
return b #returning back to this_func()
this_func()
Wrap it in an object and pass it to that_func:
def this_func():
vars = {'c': 1}
d = that_func(vars)
print vars['c'], d
def that_func(vars):
vars['c'] = 2
return 1000
Alternatively, you can pass it in as a regular variable and that_func can return multiple values:
def this_func():
c = 1
c, d = that_func(c)
print c, d
def that_func(c):
c = 2
return c, 1000

Python: variables not cumulative passing through functions

I am writing a function for a text adventure I'm making that acts as a progress bar when the player receives experience (which then levels the player up upon reaching 100). For some reason altering the values of my variables inside the function does not change their values outside the function even though I've returned all three. This becomes apparent when I try calling the function twice, each time adding 85 to the variable a.
Within the function, the objective is to pass the value of a to b, check if b is greater than or equal to 100, if so add 1 to c and remove 100 from b, then reset a to 0, print the result, and return the new values.
a = new experience b = current experience c = player level
a = 0
b = 0
c = 1
def func_1(a, b, c):
b = b + a
loop1 = 0
while loop1 < 1:
if b >= 100:
print(" ")
print("Add 1!")
print(" ")
c = c + 1
b = b - 100
else:
loop1 = loop1 + 1
a = a - a
print("c is " + str(c))
print("b is " + str(b))
print("a is " + str(a))
return a
return b
return c
a = a + 85
func_1(a, b, c)
a = a + 85
func_1(a, b, c)
print a, b, c
I'm really new to programming so I apologize for any inefficiencies. Thank you for any help and let me know if something doesn't make sense/is unclear.
Couple of things I see here:
First, out of the three return statements at the end of your code, only the first, return a, is executed. A return statement immediately stops execution of the function; return b and return c are never touched.
Second, you're having some confusion with the scope of the variables. a, b, and c defined outside of the function are global variables, while the a, b, and c passed into the function are local to the scope of the function. Thus, any modifications to the variables in your function won't affect the global variables.
You can do two things. First, have a global declaration at the beginning of the function:
def func_1():
global a
global b
global c
# Function body
This indicates to the function that a, b, and c are global variables that exist outside the function. Thus, passing in arguments is no longer needed; the function will search for variables outside the function. However, this is bad programming practice. The better option is to have func_1 return the modified values and reassign the global values to these new values, like so:
def func_1(a, b, c):
# Function body
return (a, b, c)
Then, to call the function and modify the variables outside the function:
a, b, c = func_1(a, b, c)
A couple suggestions:
You have a lot of incrementing going on, and python has specialized syntax for it: b = b + a can be shortened to b += a, c = c + 1 to c += 1, b = b - 100 to b -= 100. Also, simply reset a to 0 with a = 0 instead of subtracting a - a. Also, you don't need print(" "); print() will suffice.
Next, your while loop is unnecessary. Your function only needs to check once whether b >= 100, so there's no need to set up a loop. Increment b and then use a simple if statement to level up if necessary:
def func_1(a, b, c):
b += a
if b >= 100:
print("\nAdd 1!\n")
c += 1
b -= 100
a = 0
# Rest of the function
Inside func_1() the names a,b,c are local. When you change them nothing happens to the external a,b,c. You return the values correctly in the function, but then when calling the function you need to assign the values to the variables like this: a,b,c=func_1(a,b,c).
Returning a value doesn't set it unless you explicitly assign it in the calling function:
a, b, c = func_1(a, b, c)
Assigning inside the function doesn't affect the outer ones because they are considered "local scope". To counter that declare them global to affect the outer variables:
def func_1():
global a
global b
global c
Only one of these should be implemented
It is generally preferred not to declare globals. Ideally you should make a class for all of this, but these two options would require the least refactoring of your existing code
Note that a global variable (the variables outside of the function) are completely separate from the local variables (the variables inside the function).
This doesn't work because you can only return once. When you returned a, the function immediately stopped.
Also, since you didn't set any variable to the returned value, the global variables outside of the loop were unaffected. What you can do is return the a, b, and c values as a tuple, and then set the a, b and c global variables to that tuple.
return (a, b, c)
a, b, c = func_1(a, b, c)

Functions as arguments to functions

I saw this example in a Python book, which showcases how to use a function as an argument to another function:
def diff2(f, x, h=1E-6):
r = (f(x-h) - 2*f(x) + f(x+h))/float(h*h)
return r
def g(t):
return t**(-6)
t = 1.2
d2g = diff2(g, t)
print d2g
My question is, how does this script work without providing an argument to function g? The line in question is:
d2g = diff2(g,t)
Shouldn't it be done like:
d2g = diff2(g(t), t)
g is passed as an argument to diff2. In diff2, that argument is called f, so inside diff2 the name f refers to the function g. When diff2 calls f(x-h) (and the other calls it does), it is calling g, and providing the argument.
In other words, when you do diff2(g, t), you are telling diff2 that g is the function to call. The arguments to g are provided in diff2:
f(x-h) # calls g with x-h as the argument
f(x) # calls g with x as the argument
f(x+h) # calls g with x+h as the argument
If you called diff2(g(t), t), you would be passing the result of g(1.2) as the argument. g would be called before calling diff2, and diff2 would then fail when it tries to call f, because f would be a number (the value g(1.2)) instead of a function.
The functions in question are rather random, and perhaps difficult to understand. Let's consider a simple example, a function sum which takes two numbers a and b, and returns their sum. Actually, we can easily define another function prod, which returns their product too.
def sum(a,b):
return a + b
def prod(a,b):
return a * b
Let's say we have another function compute, which takes as its arguments the operation (a function), and two operands (two numbers to call the function on). In compute, we call the given operation on the arguments.
def compute(fn, a, b):
return fn(a, b)
We can compute different things. We can compute the sum of two numbers:
compute(sum, 1, 3)
We can compute the product of two numbers:
compute(prod, 1, 3)
Basically, without parentheses after the function name, we're not actually calling the function, it's just another object in the namespace (which happens to be a function which we can call). We don't call the function until inside of compute, when we do fn(a,b).
Let's see what the console outputs look like:
>>> compute(sum,1,3)
4
>>> compute(prod,1,3)
3
>>> sum
<function sum at mem-address>
>>> prod
<function prod at mem-address>
>>> sum(1,2)
3

Categories

Resources