Passing values in Python
Python passes references-to-objects by value.
Python passes references-to-objects by
value (like Java), and everything in
Python is an object. This sounds
simple, but then you will notice that
some data types seem to exhibit
pass-by-value characteristics, while
others seem to act like
pass-by-reference... what's the deal?It is important to understand mutable
and immutable objects. Some objects,
like strings, tuples, and numbers, are
immutable. Altering them inside a
function/method will create a new
instance and the original instance
outside the function/method is not
changed. Other objects, like lists
and dictionaries are mutable, which
means you can change the object
in-place. Therefore, altering an
object inside a function/method will
also change the original object
outside.
How do I pass a variable by reference?
Arguments are passed by assignment. The rationale behind this is twofold:
- the parameter passed in is actually a reference to an object (but the reference is passed by value)
- some data types are mutable, but others aren't
So:
If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.
If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.
To make it even more clear, let's have some examples.
List - a mutable type
Let's try to modify the list that was passed to a method:
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
Output:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Since the parameter passed in is a reference to outer_list
, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.
Now let's see what happens when we try to change the reference that was passed in as a parameter:
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
Output:
before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']
Since the the_list
parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list
was a copy of the outer_list
reference, and we had the_list
point to a new list, but there was no way to change where outer_list
pointed.
String - an immutable type
It's immutable, so there's nothing we can do to change the contents of the string
Now, let's try to change the reference
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
Output:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Again, since the the_string
parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string
was a copy of the outer_string
reference, and we had the_string
point to a new string, but there was no way to change where outer_string
pointed.
I hope this clears things up a little.
EDIT: It's been noted that this doesn't answer the question that @David originally asked, "Is there something I can do to pass the variable by actual reference?". Let's work on that.
How do we get around this?
As @Andrea's answer shows, you could return the new value. This doesn't change the way things are passed in, but does let you get the information you want back out:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
Although this seems a little cumbersome.
How do I pass variables across functions?
This is what is actually happening:
global_list = []
def defineAList():
local_list = ['1','2','3']
print "For checking purposes: in defineAList, list is", local_list
return local_list
def useTheList(passed_list):
print "For checking purposes: in useTheList, list is", passed_list
def main():
# returned list is ignored
returned_list = defineAList()
# passed_list inside useTheList is set to global_list
useTheList(global_list)
main()
This is what you want:
def defineAList():
local_list = ['1','2','3']
print "For checking purposes: in defineAList, list is", local_list
return local_list
def useTheList(passed_list):
print "For checking purposes: in useTheList, list is", passed_list
def main():
# returned list is ignored
returned_list = defineAList()
# passed_list inside useTheList is set to what is returned from defineAList
useTheList(returned_list)
main()
You can even skip the temporary returned_list
and pass the returned value directly to useTheList
:
def main():
# passed_list inside useTheList is set to what is returned from defineAList
useTheList(defineAList())
Emulating pass-by-value behaviour in python
There is no pythonic way of doing this.
Python provides very few facilities for enforcing things such as private or read-only data. The pythonic philosophy is that "we're all consenting adults": in this case this means that "the function shouldn't change the data" is part of the spec but not enforced in the code.
If you want to make a copy of the data, the closest you can get is your solution. But copy.deepcopy
, besides being inefficient, also has caveats such as:
Because deep copy copies everything it may copy too much, e.g., administrative data structures that should be shared even between copies.
[...]
This module does not copy types like module, method, stack trace, stack frame, file, socket, window, array, or any similar types.
So i'd only recommend it if you know that you're dealing with built-in Python types or your own objects (where you can customize copying behavior by defining the __copy__
/ __deepcopy__
special methods, there's no need to define your own clone()
method).
Best Way to Pass Arguments from One Function to Another in Python
do_lots_of_stuff("Bob","Mary",activity_1='run',activity_2='jump', eat_fruit_args=["John"], eat_fruit_kwargs={"a_number": 5, "a_fruit": "apples"}):
eat_fruit(*eat_fruit_args, **eat_fruit_kwargs)
return(f'''{a} and {b} {activity_1} and {activity_2}''')
You can pass and forward arguments and keyword arguments. Arguments are in the form of a list. Keyword arguments (kwargs) are in the form of a dictionary, with the key as a string, and the value as the correlating keyword value.
How to pass variables and methods as command line arguments in python
Here is how you can reference the function to call dynamically (from the local symbol table using locals()
)
import sys
def method1(value1, value2):
print(value1)
def method2(value1, value2):
print(value2)
def method_not_found(*args, **kwargs):
print('[-] function not found')
if __name__ == "__main__":
locals().get(sys.argv[3], 'method_not_found')(sys.argv[1], sys.argv[2])
user@pc: python a.py 10 12 method1
10
user@pc: python a.py 10 12 method13
[-] function not found
Explanation:
the way the line:
locals().get(sys.argv[3], 'method_not_found')(sys.argv[1], sys.argv[2])
works is that locals()
return a dict
type and we're calling .get()
method to get refer to the function we're gonna call (if not found, get method_not_found
function instead).
Then after we get the function we want to call, we execute it by passing sys.argv[1], sys.argv[2]
as arguments.
Related Topics
How to Execute Raw SQL in Flask-Sqlalchemy App
Is There a Built in Package to Parse HTML into Dom
Trying to Import a Module: Undefined Symbol: Pyunicodeucs4_Decodeutf8
Priority of the Logical Operators Not, And, or in Python
Converting Integer to Binary in Python
How to Add Custom Methods/Attributes to Built-In Python Types
How to Find Length of Digits in an Integer
Typeerror: '<=' Not Supported Between Instances of 'Str' and 'Int'
Python Pandas: Apply a Function with Arguments to a Series
Appending Turns My List to Nonetype
How to Find All Comments with Beautiful Soup
Python: Get Output of the Shell Command 'History'
Calling Java/Scala Function from a Task
How to Make an Immutable Object in Python
Python: Access Class Property from String
How to Get a Value of Datetime.Today() in Python That Is "Timezone Aware"