Marrior
Marrior

Reputation: 43

Passing value from called function from module (Python)

I am trying to call a function passvalue from another file named "File1" as module to the "Main File". I expect the value of x in "Main File" to be 4 instead of 1 and the value of y to be 3 instead of 2 after calling the function.

Question: How do I ensure the value from the module's function from "File1" is passed onto the "Main File"? For this case, x=4 and y=3.

My thoughts: I tried using global value but it does not seem to work, return in this case is only going to return either x or y value back to the function itself which probably defeat the purpose.

Felt like I am missing out something simple but can't seem to get it.

File1:

def passvalue(a,b)
  b=b+a
  a=b+b

Main:

import File1
x=1
y=2
File1.passvalue(x,y)
print(x)
print(y)

Upvotes: 0

Views: 216

Answers (2)

r.ook
r.ook

Reputation: 13898

There are multiple reasons it doesn't work:

First, objects assigned in a function are created locally. When you do a=... and b=..., a and b are local objects that you created within the function that assigned to the parameter values. Once the function exits, a and b no longer hold any relevance to code outside of the immediate function's scope.

Second, while objects are passed in reference in Python, what remains immutable are immutable. Since x and y are integers, they are immutable to change, i.e. each time you perform any operation on them, a new value is being assigned to the object instead of it changed.

To understand this, consider the following function:

def mod_lists(list_1, list_2, list_3):
    print("obj ID of rlst_1 = {0}\n"
          "obj ID of rlst_2 = {1}\n"
          "obj ID of rlst_3 = {2}\n".format(id(rlst_1), id(rlst_2), id(rlst_3)))
    print("Before change:\n"
          "obj ID of list_1 = {0}\n"
          "obj ID of list_2 = {1}\n"
          "obj ID of list_3 = {2}\n".format(id(list_1), id(list_2), id(list_3)))
    list_1.append("hello")
    list_2 = list_1 + ["world"]
    list_3 = list_1 + ["world"]
    print("After change:\n"
          "obj ID of list_1 = {0}\n"
          "obj ID of list_2 = {1}\n"
          "obj ID of list_3 = {2}\n".format(id(list_1), id(list_2), id(list_3)))
    return list_3

Now consider I have three lists I want to pass:

rlst_1 = list()
rlst_2 = list()
rlst_3 = list()

And I call the function as follows:

rlst_3 = mod_lists(rlst_1, rlst_2, rlst_3)

Now you might expect rlst_1 = ['hello'], rlst_2 = ['hello', 'world'] and rlst_3 = ['hello', 'world'], but that is wrong. If you ran it, you'll notice rlst_2 will actually be an empty list (unchanged).

So what makes rlst_1 and rlst_3 changes? Let's take a look at the object IDs printed out and compare:

# NOTE: You will see different numbers on your system.
obj ID of rlst_1 = 52178816
obj ID of rlst_2 = 52337240
obj ID of rlst_3 = 51607312

Before change:
obj ID of list_1 = 52178816
obj ID of list_2 = 52337240
obj ID of list_3 = 51607312

After change:
obj ID of list_1 = 52178816
obj ID of list_2 = 52336920
obj ID of list_3 = 52336840

Before any changes were made in the function, you can see the object IDs are exactly the same respectively, that means list_1 is an exact reference of rlst_1, etc. Now after the changes were done is where you notice the changes. You can see that list_2 and list_3 now have a different object ID. Why is that? That's because in the lines list_2=... and list_3=... you are effectively reassigning both objects to a new reference, which is list_1 + ['world']. You might also wonder why they both have a different ID because the value should be the same. While list_1 is the same reference, each ['world'] is a new instance of the list object containing the word "world" as an item, so they are different objects even though they have the same values.

Why doesn't list_1's object reference get affected though? That's because when you call the append function on list_1, it doesn't reassign but is changing the object in reference, appending the value "hello" into the same object.

Now after the function call, when you print the rlsts, you will see:

# rlst_1
# ['hello']
# rlst_2
# []
# rlst_3
# ['hello', 'world']

rlst_1 is changed, as expected, because the same object in reference was modified. rlst_2 is unchanged, because even though list_2 was reassigned a new list, it was a local object created within the function, and lost after function exits.

Why did rlst_3 get updated? You might think it's updated, but it's actually a NEW object with the same name. Note the last part of the function returns the locally created list_3 which has the value of list_1 + ['world']. So the function call evaluates as follows:

rlst_3 = mod_lists(rlst_1, rlst_2, rlst_3)
    (local) list_1 = ['hello']
    (local) list_3 = list_1 + ['world']
    (local) list_3 = ['hello', 'world']
rlst_3 = list_3
rlst_3 = ['hello', 'world']

If that's still confusing, run a id(rlst_3), and you'll see 52336840, which is exactly the object ID of the locally created list_3 (again, you'll probably see a different number, but the id(rlst_3) will always equal id(list_3)).

Applying this logic, now you can understand why it is impossible to pass immutable integers in reference and hoping to modify them in the function. The only way you can change the outside object is reassign them to the newly created local objects being returned by the function.

This answer is obviously much more long winded than Stephen Rauch's, but it gives a little bit more background to why it doesn't work.

Upvotes: 1

Stephen Rauch
Stephen Rauch

Reputation: 49842

You need to return the values like:

def passvalue(a,b)
    b=b+a
    a=b+b
    return a, b

This returns a tuple. The tuple can the unpacked like:

# Main File
import File1
x=1
y=2
x, y = File1.passvalue(x,y)
print(x)
print(y)

Upvotes: 2

Related Questions