Passing by "reference" in Python - python

I'm sorry to repeat the same thing all over again but I've already searched but I still can't solve my problem...
If I want to change a single "int" value, do I HAVE to create a list? It's a mess. It's quite more comfortable in C...
(like void function(int a, b, c, *d, *e, *f))
This is what I'm trying to do (I HAVE to change "d", "e", "f"):
d = 4
e = 9
f = 11
def function(a,b,c,d,e,f)
a = something
b = something
c = 1
d = 2
e = 3
f = 4
Having said this, do I have to create a dictionary or a list to handle simple numbers?
I'm still a newbie but I'd like to know what's the best solution for this problem that is scrambling my mind lately.
Thank you guys.

Yes. Because python can simply return a tuple, you use that to change multiple values:
d = 4
e = 9
f = 11
def function(a,b,c)
a = something
b = something
c = 1
d = 2
e = 3
f = 4
return (d, e, f)
d, e, f = function(d, e, f)
Since passing by reference is mainly used (in C at least) to work around the fact you cannot return more than one value from a function, you don't need passing by reference in Python.

There is a question (and answer) here on S.O that explains what you need to know regarding passing objects as parameters.
Regarding your code, you could always return those values in a tuple and assign them to the references of thos objects in the global namespace, likewise: return (d, e, f). Now to assign them to references in your objects, you could use sequence unpacking, eg d, e, f = function(params)

Related

Using arguments as variable names: Why does it solve this problem?

I'm going through some material about functions and I'm writing Python code to make some sense of the pseudocode examples.
The goal is printing the variables after I call the function, then check the new values.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
my_sum(B, C, A)
my_sum(C, A, B)
my_sum(A, B, C)
my_sum(B, C, A)
print(A, B, C)
My first instinct was to write this procedural approach, but when I do the calling the program won't give the right answer, because A, B and C aren't saving whatever is happening inside the function. So A is always 1, B is 2 and so forth
It turns out when I assign the calling with the arguments, the variables A, B and C receive the new values and they're now keeping it. Finally it prints 21, 8, 13, which is the answer.
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
C, A, B = my_sum(C, A, B)
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
How would you implement it or what are the other ways of writing this algorithm?
The thing is I can't wrap my head around why this works at all! It was just a random guess that happened to solve my problem.
python don't have pass by reference option, only pass by value, so your construction is correct, because you returning NEW values (in tuple form), not changing value of variables, that you are passing in.
In Python, an assignment made to a parameter name never affects the value of the name that the caller uses. They are separate names that initially reference the same object, but once the parameter name is assigned something else (like a sum), it references a different object.
Your second attempt works because the function returns a tuple with the values of the three paramater names and your main program unpacks that tuple back into its own names.
However, since the function doesn't need the original value of the third argument, and it doesn't touch the first two arguments, the caller doesn't really need to pass the third argument, and doesn't need to update its own names for the first two arguments... So the function could be designed to only take two arguments and return the new value:
def my_sum(x, y):
return x + y
A = 1
B = 2
C = my_sum(A, B)
A = my_sum(B, C)
B = my_sum(C, A)
C = my_sum(A, B)
A = my_sum(B, C)
Lets start with your function definition and one call.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
Without the function, this is functionally the same as:
A = 1
B = 2
C = 0
x = A
y = B
z = C
z = x + y
_ = x, y, z
# x, y, and z are discarded since you don't do anything with the return value
You shouldn't expect this to change A, B, or C or if you do you have a misconception about how python variables or names work.
Python variables or names are just a dict with a name pointing to a value.
A = 1
B = 2
C = 0
my_sum(A, B, C)
# this is a very condensed version of what python does in the background
dict_of_globals = dict()
dict_of_globals['A'] = 1
dict_of_globals['B'] = 2
dict_of_globals['C'] = 3
my_sum_local_dict = dict()
my_sum_local_dict['x'] = dict_of_globals['A']
my_sum_local_dict['y'] = dict_of_globals['B']
my_sum_local_dict['z'] = dict_of_globals['C']
# and so on..
Since you only ever assign 1 to dict_of_globals['A'], it would be unreasonable to expect it to be anything other than 1.
The reason this works:
A, B, C = my_sum(A, B, C)
is because you are assigning the return value back to A.
A = x # etc..
# or:
dict_of_globals['A'] = my_sum_local_dict['x']

Optional arguments in nested functions in Python

Is it possible for one function to take as an argument the name of one of the optional arguments of a second function then call the second function with the optional argument set to the value of some variable in the first function?
Code (that obviously doesn't work)
def foo(a=1,b=2,c=3):
d = a + b + c
return d
def bar(variable):
z = get_user_input()
e = foo(variable = z)
return e
print(bar(a))
The desired result is for bar to call foo(a=z) and print whatever z+2+3 is. Of course Python doesn't know what (a) is here. My guess is that I can somehow reference the list of arguments of foo() , but I am stumped as to how you might do that.
Maybe try the code snippet below
def foo(a=1,b=2,c=3):
d = a + b + c
return d
def bar(variable: str):
z = int(input())
e = foo(**{variable: z})
return e
# variable name should be defined as string
print(bar("a"))
The ** parses all arbitrary arguments on dict, in this case is a. Be careful tho, as if passing wrong variable name (differ from a, b or c) will result raising error.

How to use passed arguments as default parameter values in Python?

Given a function with multiple arguments, where all but the first one are variable.
E.g.:
def f(a, b = .., ...)
I am looking for minimalist python-code that realizes the intuitive code below:
def f(a, b = a, ...)
Hence, I could not find any satisfying answers I am asking here although I am without doubts that the answers to this question have been given already somewhere - in that case i apologize.
Cheers!
I specify by another example my desired functionality again intuitively by wrong code:
def f(a,b,c, d = 0, e = [], f = b, g = c, h = a):
...
Thank you in advance!
According to the Python docs:
the expression [used as default parameter value] is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.
So what you are trying to achieve would not work so simply because default parameter values are computed only once at the function definition, not at every call.
Instead, you can set all default parameter values to None and test for this value in the body of the function:
def func(a, b, c, d = 0, e = [], f = None, g = None, h = None):
f = b if f == None else f
g = c if g == None else g
h = a if h == None else h

How to call on a variable which has been defined in a previous function?

My code is as follows...
def addition(a, b):
c = a + b
return c
And I then want to be able to use C later on in the program as a variable. For example...
d = c * 3
However, I get a NameError that 'C' is not defined... But I have returned c, so why can I not use it later on in the code?! So confused. Thanks!
(This is obviously a simpler version of what I want to do but thought I'd keep it simple so I can understand the basics of why I cannot call on this variable outside my function even though I am returning the variable. Thanks)
You have returned the value of c but not the whole variable i.e. the name c exists only within the scope it is instantiated.
So, if you want to use the value returned, you should re-assign it to a new name. You can do it by re-assigning it to c again, but it could be any name you wanted.
def addition(a, b):
c = a + b
return c
new_var = addition(1,2) #new_var gets the value 3
c = addition(2,3) #c gets the value 5
Take a look at this nice explanation about variables and scopes (link)
You usually define a function to use it later in your code. For that case, use another global variable c:
def addition(a, b):
c = a + b
return c
c = addition(1, 2)
d = c * 3 # d == 9
Functions allow this usage of repeated code, or procedure distinction, so that you can later write in your code
m = addition(4, 5)
and it will store the required result of the functionality into m.
If you want to define c in the function and use it later, you can use global variables.
c = 0
def addition(a, b):
global c
c = a + b
return c
It's not considered good to use globals, though. You could also call the function in the variable assignment.
d = addition(a, b) * 3
For this, you need to put real numbers in the place of a and b. I recommend you use the second option.

Convert list of strings into multiple data types in one line

I want to convert a list of strings into their correct types (ie. int, float, boolean, etc) in one line and unpack the values.
Is there a built-in module that can accomplish this better than the following?:
strLst = ["a", "1.0", "2", "True"]
a, b, c, d = [[s[0], float(s[1]), int(s[2]), bool(s[3])] for s in [strLst]][0]
EDIT:
I am creating a, b, c, d from a large text file and I was hoping there was an elegant way to make the conversion on one line. This is not just a question for this specific example but a question about a module that can accomplish something like this:
with open("file.txt") as f:
a, b, c, d = [[s[0], float(s[1]), int(s[2]), bool(s[3])] \
for s in [next(f)[:-1].split()]][0]
Based on Niclas Nilsson's comment I could do the following:
a,b,c,d = [ast.literal_eval(s) for s in next(f)[:-1].split()]
Zipping and applying cast functions works, and is much faster than literal_eval.
Also, literal_eval raises 'ValueError: malformed string' if the string value doesn't contain quotes, which, depending on your data, may be problematic.
from StringIO import StringIO
from time import time
import ast
def zip_test():
# Using StringIO to illustrate using something file-like.
for row in StringIO('a 1.0 2 True\n' * 32):
(a, b, c, d) = [f(v) for (f, v) in zip(
(str, float, int, lambda v: v == 'True'), row.split())]
def ast_test():
for row in StringIO('"a" 1.0 2 True\n' * 32):
(a, b, c, d) = [ast.literal_eval(s) for s in row.split()]
for f in (zip_test, ast_test):
start = time()
for i in range(100):
f()
print '%s: %s' % (f.func_name, time() - start)
# [ ** Results ** ]
#
# zip_test: 0.0131301879883
# ast_test: 0.0835828781128
I know the question is too old. But my very first question is why it has to be a one-liner? I mean, even if the solution takes 100 lines, you can always put them under a function and call the function everywhere else and so the solution will be one-liner right?
I did came up with a two-liner which is faster and simpler than the suggested, if speed is of any necessity.
for row in StringIO('a 1.0 2 True\n' * 32):# Took the idea from derek's answer
(a, b, c, d) = row.split(" ", 3)
b, c, d = float(b), int(c), 'True' in d
As I mentioned, in case this cannot be used and you definitely need a one-liner, you can always do something like this:
def string_to_multiple_type_list(data):
multi_list = []
for line in data.split("\n"):
a, b, c, d = line.split(" ", 3)
multi_list.append([a, float(b), int(c), 'True' in d])
return multi_list
And whenever you need the converted values, you can call it as one-liner:
new_multi_list = string_to_multiple_type_list(data)
Even though the function call takes a little time(definitely in micro seconds), it is faster and more elegant than using zip.
Borrowing derek's test code, I could see a 20% reduced time with this user defined function and 30% reduced time in case of two liner.

Categories