David Sykes
David Sykes

Reputation: 49882

How do I pass a variable by reference?

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

Answers (30)

bterwijn
bterwijn

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.

enter image description here

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:

  • immutable types: bool, int, float, complex, str, tuple, bytes, frozenset

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).

  • mutable types: list, dict, set, class, ... (most other types)

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

enter image description here

Full disclosure: I am the developer of memory_graph.

Upvotes: 0

Chand Priyankara
Chand Priyankara

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

Raymond Hettinger
Raymond Hettinger

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

Trey Hunner
Trey Hunner

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:

  1. Variables do not "contain" objects, but point to objects (or if you prefer they "refer to" or "bind to" objects)
  2. Multiple variables (or even data structures) can point to the same object
  3. The word "change" is often ambiguous in Python:
    • assignments change which object a variable points to
    • mutations (for example the list append method) change an object

Here are two key facts about Python's function calls:

  1. Function arguments work the same way as variables in Python: the variable for each argument points to the corresponding object that the function was called with
  2. There is no way to alter the way variables or arguments work: they're all pointers, references, or bindings (pick your terminology of choice)

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

itmuckel
itmuckel

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

kiriloff
kiriloff

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

Fernando Fabreti
Fernando Fabreti

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

Bhanuday Sharma
Bhanuday Sharma

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

S.B
S.B

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

paulyang0125
paulyang0125

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.

  1. Passing a String

    Enter image description here

  2. Passing a List

    Enter image description here

Upvotes: -2

mo FEAR
mo FEAR

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

Stepan Dyatkovskiy
Stepan Dyatkovskiy

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

JustBeingHelpful
JustBeingHelpful

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

Julian wandhoven
Julian wandhoven

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

Magnus
Magnus

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

Dolf Andringa
Dolf Andringa

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

Joop
Joop

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

Alok Garg
Alok Garg

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

mARK bLOORE
mARK bLOORE

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

textshell
textshell

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 deleting 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

Brad Porter
Brad Porter

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

David Cournapeau
David Cournapeau

Reputation: 80770

It is neither pass-by-value or pass-by-reference - it is call-by-object. See this, by Fredrik Lundh:

Call By Object

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

Celes
Celes

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

Youjun Hu
Youjun Hu

Reputation: 1334

  1. 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.

  2. 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.

  3. 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.

  1. To make comparison with other languages, you can say Python passes arguments by value in the same way as C does, where when you pass "by reference" you are actually passing by value the reference (i.e., the pointer)

Upvotes: 0

eyal0931
eyal0931

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

TVK
TVK

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

Liakos
Liakos

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

Jop Knoppers
Jop Knoppers

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

Lutz Prechelt
Lutz Prechelt

Reputation: 39426

There are no variables in Python

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.

  1. Python has names and objects.
  2. Assignment binds a name to an object.
  3. Passing an argument into a function also binds a name (the parameter name of the function) to an object.

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.

How to read the example in the question

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).

How to pass by reference

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

Daren Thomas
Daren Thomas

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

Related Questions