Reputation: 49882
I wrote this class for testing:
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
When I tried creating an instance, the output was Original
. So it seems like parameters in Python are passed by value. Is that correct? How can I modify the code to get the effect of pass-by-reference, so that the output is Changed
?
Sometimes people are surprised that code like x = 1
, where x
is a parameter name, doesn't impact on the caller's argument, but code like x[0] = 1
does. This happens because item assignment and slice assignment are ways to mutate an existing object, rather than reassign a variable, despite the =
syntax. See Why can a function modify some arguments as perceived by the caller, but not others? for details.
See also What's the difference between passing by reference vs. passing by value? for important, language-agnostic terminology discussion.
Upvotes: 3346
Views: 2169258
Reputation: 328
You can use memory_graph to make a graph to see that all variables in Python are references to values and therefore Python uses pass-by-reference exclusively. When in the code below we call function add_one() that changes its parameters:
import memory_graph as mg # see install instructions in link above
def add_one(a, b, c):
a += 1
b.append(1)
c.append(1)
mg.show(mg.get_call_stack()) # show the graph
a = 10
b = [4, 3, 2]
c = [4, 3, 2]
add_one(a, b, c.copy())
print(f"a:{a} b:{b} c:{c}")
and show the graph, then we can clearly see that the 'b' variable in the add_one() function shares it data with the 'b' variable in the calling stack frame named "<module>" as a result of exclusive call-by-reference.
So that only 'b' is changed when executing the print statement after the function call is completed:
a:10 b:[4, 3, 2, 1] c:[4, 3, 2]
Data of variable 'c' is not changed because a copy of 'c' is passed to the add_one() function. Data of variable 'a' is not changed because it is of immutable type. Python makes a distinction between:
A value of an immutable type can not be changed in place, a copy is always automatically made when it is changed (therefore no need to draw a reference in the graph).
A value of a mutable type can be changed in place, no automatic copy is made. So to change a string (immutable) you can wrap it in a list (mutable) like this:
import memory_graph as mg
def change(var):
var[0] = 'Changed'
mg.show(mg.get_call_stack()) # show the graph
variable = ['Original']
change(variable)
print(variable[0]) # Changed
Full disclosure: I am the developer of memory_graph.
Upvotes: 0
Reputation: 6782
if you wrap your variable in a namespace [Object] instead to a variable/attribute it'll will keep change the object's attribute:
class ObjectY:
y = 0
def funcY(Y):
Y.y = 1
def funcX(x):
x = 1
def main():
x = 0
print(x)
funcX(x)
print(x)
Y = ObjectY()
print(Y.y)
funcY(Y)
print(Y.y)
if __name__ == "__main__":
main()
Output:
0
0
0
1
Upvotes: 0
Reputation: 226614
Effbot (aka Fredrik Lundh) has described Python's variable passing style as call-by-object: https://web.archive.org/web/20201111195827/http://effbot.org/zone/call-by-object.htm
Objects are allocated on the heap and pointers to them can be passed around anywhere.
When you make an assignment such as x = 1000
, a dictionary entry is created that maps the string "x" in the current namespace to a pointer to the integer object containing one thousand.
When you update "x" with x = 2000
, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).
When you do a new assignment such as y = x
, a new dictionary entry "y" is created that points to the same object as the entry for "x".
Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.
Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y
will print [10]
. The empty list was created. Both "x" and "y" point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both "x" and "y" (just as a database update would be visible to every connection to that database).
Hope that clarifies the issue for you.
Upvotes: 94
Reputation: 11814
I find it easiest to understand Python's function argument passing by first understanding Python's variables. The nature of Python's variables and objects is probably the most widely misunderstood Python fact that also tends to bite newer (and not-so-new) Python users.
Here are a few fundamental facts about Python's variables:
append
method) change an objectHere are two key facts about Python's function calls:
Those facts were explained very well in Ned Batchelder's facts and myths about Python names and values and I revisited those ideas in my own variables and objects in Python some years later. If you'd like to dive deeper into this topic I recommend giving either read/watch.
Upvotes: 1
Reputation: 1420
To simulate passing an object by reference, wrap it in a one-item list:
class PassByReference:
def __init__(self, name):
self.name = name
def changeRef(ref):
ref[0] = PassByReference('Michael')
obj = PassByReference('Peter')
print(obj.name)
p = [obj]
changeRef(p)
print(p[0].name)
Assigning to an element of the list mutates the list rather than reassigning a name. Since the list itself has reference semantics, the change is reflected in the caller.
Upvotes: 9
Reputation: 26333
A method's argument is "passed by assignment": a reference to an object is passed by value.
With
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change( self.variable )
print( self.variable )
def change(self, var):
var = 'Changed'
passbyref = PassByReference()
we can witness that rebinding a value to the reference copy passed as an argument will will not change the value bound to the object in the outer scope.
A way to circumvent is to pass your (mutable) argument as a one-element list, and alter its first (and unique) value in the body of the method.
class PassByReference:
def __init__(self):
self.variable = ['Original']
self.change( self.variable )
print( self.variable[0] )
def change(self, var):
var[0] = 'Changed'
passbyref = PassByReference()
Upvotes: 0
Reputation: 4366
My Tests:
#objects:
def change_me(dict):
dict.update(b=10, c=10)
d = {'a': 1, 'b': 1}
change_me(d)
d
{'a': 1, 'b': 10, 'c': 10}
# conclusion: objects are passed by reference
#strings:
def change_me(str):
str = 'another string'
d = 'a string'
change_me(d)
d
'a string'
# conclusion: strings are passed by value
# you may return a value and capture it again
# like: d = change_me(d)
Upvotes: 1
Reputation: 433
I found other answers a little bit confusing and I had to struggle a while to grasp the concepts. So, I am trying to put the answer in my language. It may help you if other answers are confusing to you too. So, the answer is like this-
When you create a list-
my_list = []
you are actually creating an object of the class list:
my_list = list()
Here, my_list is just a name given to the memory address (e.g., 140707924412080) of the object created by the constructor of the 'list' class.
When you pass this list to a method defined as
def my_method1(local_list):
local_list.append(1)
another reference to the same memory address 140707924412080 is created. So, when you make any changes/mutate to the object by using append method, it is also reflected outside the my_method1. Because, both the outer list my_list and local_list are referencing the same memory address.
On the other hand, when you pass the same list to the following method,
def my_method2(local_list2):
local_list2 = [1,2,3,4]
the first half of the process remains the same. i.e., a new reference/name local_list2 is created which points to the same memory address 140707924412080. But when you create a new list [1,2,3,4], the constructor of the 'list' class is called again and a new object is created. This new object has a completely different memory address, e.g., 140707924412112. When you assign local_list2 to [1,2,3,4], now the local_list2 name refers to a new memory address which is 140707924412112. Since in this entire process you have not made any changes to the object placed at memory address 140707924412080, it remains unaffected.
In other words, it is in the spirit that 'other languages have variables, Python have names'. That means in other languages, variables are referenced to a fixed address in memory. That means, in C++, if you reassign a variable by
a = 1
a = 2
the memory address where the value '1' was stored is now holding the value '2' And hence, the value '1' is completely lost. Whereas in Python, since everything is an object, earlier 'a' referred to the memory address that stores the object of class 'int' which in turn stores the value '1'. But, after reassignment, it refers to a completely different memory address that stores the newly created object of class 'int' holding the value '2'.
Hope it helps.
Upvotes: 3
Reputation: 16546
There are already many great answers (or let's say opinions) about this and I've read them, but I want to mention a missing one. The one from Python's documentation in the FAQ section. I don't know the date of publishing this page, but this should be our true reference:
Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.
If you have:
a = SOMETHING
def fn(arg):
pass
and you call it like fn(a)
, you're doing exactly what you do in assignment. So this happens:
arg = a
An additional reference to SOMETHING
is created. Variables are just symbols/names/references. They don't "hold" anything.
Upvotes: 3
Reputation: 337
I share another fun way for people to comprehend this topic over a handy tool - Python Tutor: Learn Python, JavaScript, C, C++, and Java programming by visualizing code based on the example of passing a mutable list from @Mark Ransom.
Just play it around, and then you will figure it out.
Upvotes: -2
Reputation: 701
A simple answer:
In Python, like C++, when you create an object instance and pass it as a parameter, no copies of the instance itself get made, so you are referencing the same instance from outside and inside the function and are able to modify the component datums of the same object instance, hence changes are visible to the outside.
For basic types, Python and C++ also behave the same to each other, in that copies of the instances are now made, so the outside sees/modifies a different instance than the inside of the function. Hence changes from the inside are not visible on the outside.
Here comes the real difference between Python and C++:
C++ has the concept of address pointers, and C++ allows you to pass pointers instead, which bypasses the copying of basic types, so that the inside of the function can affect the same instances as those outside, so that the changes are also visible to the outside. This has no equivalent in Python, so is not possible without workarounds (such as creating wrapper types).
Such pointers can be useful in Python, but it's not as necessary as it is in C++, because in C++, you can only return a single entity, whereas in Python you can return multiple values separated by commas (i.e., a tuple). So in Python, if you have variables a,b, and c, and want a function to modify them persistently (relative to the outside), you would do this:
a = 4
b = 3
c = 8
a, b, c = somefunc(a, b, c)
# a, b, and c now have different values here
Such syntax is not easily possible in C++, thus in C++ you would do this instead:
int a = 4
int b = 3
int c = 8
somefunc(&a, &b, &c)
// a, b, and c now have different values here
Upvotes: -3
Reputation: 1010
Use dataclasses. Also, it allows you to apply type restrictions (aka "type hints").
from dataclasses import dataclass
@dataclass
class Holder:
obj: your_type # Need any type? Use "obj: object" then.
def foo(ref: Holder):
ref.obj = do_something()
I agree with folks that in most cases you'd better consider not to use it.
And yet, when we're talking about contexts, it's worth to know that way.
You can design an explicit context class though. When prototyping, I prefer dataclasses, just because it's easy to serialize them back and forth.
Upvotes: 2
Reputation: 18990
This might be an elegant object-oriented solution without this functionality in Python. An even more elegant solution would be to have any class you make subclass from this. Or you could name it "MasterClass". But instead of having a single variable and a single Boolean, make them a collection of some kind. I fixed the naming of your instance variables to comply with PEP 8.
class PassByReference:
def __init__(self, variable, pass_by_reference=True):
self._variable_original = 'Original'
self._variable = variable
self._pass_by_reference = pass_by_reference # False => pass_by_value
self.change(self.variable)
print(self)
def __str__(self):
print(self.get_variable())
def get_variable(self):
if pass_by_reference == True:
return self._variable
else:
return self._variable_original
def set_variable(self, something):
self._variable = something
def change(self, var):
self.set_variable(var)
def caller_method():
pbr = PassByReference(variable='Changed') # This will print 'Changed'
variable = pbr.get_variable() # This will assign value 'Changed'
pbr2 = PassByReference(variable='Changed', pass_by_reference=False) # This will print 'Original'
variable2 = pbr2.get_variable() # This will assign value 'Original'
Upvotes: -2
Reputation: 288
Alternatively, you could use ctypes which would look something like this:
import ctypes
def f(a):
a.value = 2398 ## Resign the value in a function
a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)
As a is a c int and not a Python integer and apparently passed by reference. However, you have to be careful as strange things could happen, and it is therefore not advised.
Upvotes: 2
Reputation: 1754
I am new to Python, started yesterday (though I have been programming for 45 years).
I came here because I was writing a function where I wanted to have two so-called out-parameters. If it would have been only one out-parameter, I wouldn't get hung up right now on checking how reference/value works in Python. I would just have used the return value of the function instead. But since I needed two such out-parameters I felt I needed to sort it out.
In this post I am going to show how I solved my situation. Perhaps others coming here can find it valuable, even though it is not exactly an answer to the topic question. Experienced Python programmers of course already know about the solution I used, but it was new to me.
From the answers here I could quickly see that Python works a bit like JavaScript in this regard, and that you need to use workarounds if you want the reference functionality.
But then I found something neat in Python that I don't think I have seen in other languages before, namely that you can return more than one value from a function, in a simple comma-separated way, like this:
def somefunction(p):
a = p + 1
b = p + 2
c = -p
return a, b, c
and that you can handle that on the calling side similarly, like this
x, y, z = somefunction(w)
That was good enough for me and I was satisfied. There isn't any need to use some workaround.
In other languages you can of course also return many values, but then usually in the from of an object, and you need to adjust the calling side accordingly.
The Python way of doing it was nice and simple.
If you want to mimic by reference even more, you could do as follows:
def somefunction(a, b, c):
a = a * 2
b = b + a
c = a * b * c
return a, b, c
x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")
x, y, z = somefunction(x, y, z)
print(F"After : {x}, {y}, {z}")
which gives this result
Before : 3, 5, 10 After : 6, 11, 660
Upvotes: 2
Reputation: 2170
Aside from all the great explanations on how this stuff works in Python, I don't see a simple suggestion for the problem. As you seem to do create objects and instances, the Pythonic way of handling instance variables and changing them is the following:
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.Change()
print self.variable
def Change(self):
self.variable = 'Changed'
In instance methods, you normally refer to self
to access instance attributes. It is normal to set instance attributes in __init__
and read or change them in instance methods. That is also why you pass self
as the first argument to def Change
.
Another solution would be to create a static method like this:
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.variable = PassByReference.Change(self.variable)
print self.variable
@staticmethod
def Change(var):
var = 'Changed'
return var
Upvotes: 10
Reputation: 8108
There are a lot of insights in answers here, but I think an additional point is not clearly mentioned here explicitly. Quoting from Python documentation What are the rules for local and global variables in Python?
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.
Even when passing a mutable object to a function this still applies. And to me it clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.
def test(l):
print "Received", l, id(l)
l = [0, 0, 0]
print "Changed to", l, id(l) # New local object created, breaking link to global l
l = [1, 2, 3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)
gives:
Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632
The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.
Upvotes: 20
Reputation: 174
Pass-by-reference in Python is quite different from the concept of pass by reference in C++/Java.
Java and C#: primitive types (including string) pass by value (copy). A reference type is passed by reference (address copy), so all changes made in the parameter in the called function are visible to the caller.
C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.
Python: Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.” (read here). Both the caller and the function refer to the same object, but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function. This depends upon the type of object passed. For example, an immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized.
A crucial difference between updating or reassigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. The scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.
Upvotes: 3
Reputation: 176
Given the way Python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:
class PassByReferenceIsh:
def __init__(self):
self.variable = 'Original'
self.change('variable')
print self.variable
def change(self, var):
self.__dict__[var] = 'Changed'
In real code you would, of course, add error checking on the dict lookup.
Upvotes: 5
Reputation: 2076
While pass by reference is nothing that fits well into Python and should be rarely used, there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.
The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.
One way is to use global
(for global variables) or nonlocal
(for local variables in a function) in a wrapper function.
def change(wrapper):
wrapper(7)
x = 5
def setter(val):
global x
x = val
print(x)
The same idea works for reading and del
eting a variable.
For just reading, there is even a shorter way of just using lambda: x
which returns a callable that when called returns the current value of x. This is somewhat like "call by name" used in languages in the distant past.
Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:
class ByRef:
def __init__(self, r, w, d):
self._read = r
self._write = w
self._delete = d
def set(self, val):
self._write(val)
def get(self):
return self._read()
def remove(self):
self._delete()
wrapped = property(get, set, remove)
# Left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15
Pythons "reflection" support makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:
class ByRef:
def __init__(self, locs, name):
self._locs = locs
self._name = name
def set(self, val):
self._locs[self._name] = val
def get(self):
return self._locs[self._name]
def remove(self):
del self._locs[self._name]
wrapped = property(get, set, remove)
def change(x):
x.wrapped = 7
def test_me():
x = 6
print(x)
change(ByRef(locals(), "x"))
print(x)
Here the ByRef
class wraps a dictionary access. So attribute access to wrapped
is translated to a item access in the passed dictionary. By passing the result of the builtin locals
and the name of a local variable, this ends up accessing a local variable. The Python documentation as of 3.5 advises that changing the dictionary might not work, but it seems to work for me.
Upvotes: 4
Reputation: 459
I used the following method to quickly convert some Fortran code to Python. True, it's not pass by reference as the original question was posed, but it is a simple workaround in some cases.
a = 0
b = 0
c = 0
def myfunc(a, b, c):
a = 1
b = 2
c = 3
return a, b, c
a, b, c = myfunc(a, b, c)
print a, b, c
Upvotes: 9
Reputation: 80770
It is neither pass-by-value or pass-by-reference - it is call-by-object. See this, by Fredrik Lundh:
Here is a significant quote:
"...variables [names] are not objects; they cannot be denoted by other variables or referred to by objects."
In your example, when the Change
method is called--a namespace is created for it; and var
becomes a name, within that namespace, for the string object 'Original'
. That object then has a name in two namespaces. Next, var = 'Changed'
binds var
to a new string object, and thus the method's namespace forgets about 'Original'
. Finally, that namespace is forgotten, and the string 'Changed'
along with it.
Upvotes: 277
Reputation: 37
def i_my_wstring_length(wstring_input:str = "", i_length:int = 0) -> int:
i_length[0] = len(wstring_input)
return 0
wstring_test = "Test message with 32 characters."
i_length_test = [0]
i_my_wstring_length(wstring_test, i_length_test)
print("The string:\n\"{}\"\ncontains {} character(s).".format(wstring_test, *i_length_test))
input("\nPress ENTER key to continue . . . ")
Upvotes: -2
Reputation: 1334
Python assigns a unique identifier to each object and this identifier can be found by using Python's built-in id()
function.
It is ready to verify that actual and formal arguments in a function call have the same id value, which indicates that the dummy argument and actual argument refer to the same object.
Note that the actual argument and the corresponding dummy argument are two names referring to the same object. If you re-bind a dummy argument to a new value/object in the function scope, this does not effect the fact that the actual argument still points to the original object because actual argument and dummy argument are two names.
The above two facts can be summarized as “arguments are passed by assignment”. i.e.,
dummy_argument = actual_argument
If you re-bind dummy_argument
to a new object in the function body, the actual_argument
still refers to the original object. If you use dummy_argument[0] = some_thing
, then this will also modify actual_argument[0]
. Therefore the effect of “pass by reference” can be achieved by modifying the components/attributes of the object reference passed in. Of course, this requires that the object passed is a mutable object.
Upvotes: 0
Reputation: 59
Most of the time, the variable to be passed by reference is a class member. The solution I suggest is to use a decorator to add both a field that is mutable and corresponding property. The field is a class wrapper around the variable.
The @refproperty
adds both self._myvar
(mutable) and self.myvar
property.
@refproperty('myvar')
class T():
pass
def f(x):
x.value=6
y=T()
y.myvar=3
f(y._myvar)
print(y.myvar)
It will print 6.
Compare this to:
class X:
pass
x=X()
x.myvar=4
def f(y):
y=6
f(x.myvar)
print(x.myvar)
In this case, it won't work. It will print 4.
The code is the following:
def refproperty(var,value=None):
def getp(self):
return getattr(self,'_'+var).get(self)
def setp(self,v):
return getattr(self,'_'+var).set(self,v)
def decorator(klass):
orginit=klass.__init__
setattr(klass,var,property(getp,setp))
def newinit(self,*args,**kw):
rv=RefVar(value)
setattr(self,'_'+var,rv)
orginit(self,*args,**kw)
klass.__init__=newinit
return klass
return decorator
class RefVar(object):
def __init__(self, value=None):
self.value = value
def get(self,*args):
return self.value
def set(self,main, value):
self.value = value
Upvotes: -2
Reputation: 1150
I solved a similar requirement as follows:
To implement a member function that changes a variable, dont pass the variable itself, but pass a functools.partial
that contains setattr
referring to the variable.
Calling the functools.partial
inside change()
will execute settatr
and change the actual referenced variable.
Note that setattr
needs the name of the variable as string.
class PassByReference(object):
def __init__(self):
self.variable = "Original"
print(self.variable)
self.change(partial(setattr,self,"variable"))
print(self.variable)
def change(self, setter):
setter("Changed")
Upvotes: -1
Reputation: 570
Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.
# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
result = a + b
ref['multi'] = a * b # reference the multi. ref['multi'] is number
ref['msg'] = "The result: " + str(result) + " was nice!"
return result
number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.
sum = AddNumbers(number1, number2, ref)
print("sum: ", sum) # the returned value
print("multi: ", ref['multi']) # a referenced value
print("msg: ", ref['msg']) # a referenced value
Upvotes: 4
Reputation: 714
Most likely not the most reliable method but this works, keep in mind that you are overloading the built-in str function which is typically something you don't want to do:
import builtins
class sstr(str):
def __str__(self):
if hasattr(self, 'changed'):
return self.changed
return self
def change(self, value):
self.changed = value
builtins.str = sstr
def change_the_value(val):
val.change('After')
val = str('Before')
print (val)
change_the_value(val)
print (val)
Upvotes: 0
Reputation: 39426
The key to understanding parameter passing is to stop thinking about "variables". There are names and objects in Python and together they appear like variables, but it is useful to always distinguish the three.
That is all there is to it. Mutability is irrelevant to this question.
Example:
a = 1
This binds the name a
to an object of type integer that holds the value 1.
b = x
This binds the name b
to the same object that the name x
is currently bound to.
Afterward, the name b
has nothing to do with the name x
anymore.
See sections 3.1 and 4.2 in the Python 3 language reference.
In the code shown in the question, the statement self.Change(self.variable)
binds the name var
(in the scope of function Change
) to the object that holds the value 'Original'
and the assignment var = 'Changed'
(in the body of function Change
) assigns that same name again: to some other object (that happens to hold a string as well but could have been something else entirely).
So if the thing you want to change is a mutable object, there is no problem, as everything is effectively passed by reference.
If it is an immutable object (e.g. a bool, number, string), the way to go is to wrap it in a mutable object.
The quick-and-dirty solution for this is a one-element list (instead of self.variable
, pass [self.variable]
and in the function modify var[0]
).
The more pythonic approach would be to introduce a trivial, one-attribute class. The function receives an instance of the class and manipulates the attribute.
Upvotes: 110
Reputation: 70344
Think of stuff being passed by assignment instead of by reference/by value. That way, it is always clear, what is happening as long as you understand what happens during the normal assignment.
So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:
a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b # prints [1, 2, 3, 4] ['a', 'b']
Since immutable types cannot be modified, they seem like being passed by value - passing an int into a function means assigning the int to the function's parameter. You can only ever reassign that, but it won't change the original variables value.
Upvotes: 229