17

This is a simple question, but I could not find a similar one in SO. Is return by reference available in Python in anyway? (Maybe with simple hacks.)

Return by reference explanation:

def func(lst):
    return reference of lst[2] somehow

func([7, 8, 9, 10])        # returns 9
func([7, 8, 9, 10]) = 11   # lst is changed to [7, 8, 11, 10]

Is this behavior achievable? Maybe with some operator overloading?

An example of this usage in C++: Returning values by reference in C++

martineau
  • 119,623
  • 25
  • 170
  • 301
Rockybilly
  • 2,938
  • 1
  • 13
  • 38

4 Answers4

6

What you should probably do is just return 2 and then use that as an index into the list you passed in:

lyst = [7, 8, 9, 10]
def func(lst):
     # ...
     return 2

lyst[func(lyst)] = 11

You can create an object that encapsulates a reference to the container plus the index (or key, in the case of a dictionary) of the particular element:

class ElementRef(object):

    def __init__(self, obj, key):
        self.obj = obj
        self.key = key

    @property
    def value(self): return self.obj[self.key]

    @value.setter
    def value(self, value): self.obj[self.key] = value

Then your function looks like this:

 def func(lyst):
     # ...
     return ElementRef(lyst, 2)

And you can modify the element by setting its value attribute like this:

 func(lyst).value = 11

But I don't see a lot of value in this over just returning the index. Especially because the object can become useless if items are added to or removed from the list before it's used. Explicitly returning an index makes this an obvious problem, but if you get back an ElementRef you may assume it contains some kind of magic that it doesn't.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • Heh. Was about to edit in a similar thing to my answer, but I think they work better as separate answers, so I'll avoid duplication. If you wanted to go a bit further, you could make `ElementRef` a proxy-style class (via `__getattr__` or the like), so aside from changing the referenced value, you could use it mostly as the underlying type. Not really worth the bother, just mentioning it as a "if you want to write terrible Python because you're a masochist" option. :-) Up-voted regardless. – ShadowRanger Apr 19 '17 at 00:38
3
def func(lst):
    return reference of lst[2] somehow

If you do:

def func(lst):
    return lst[2]

a = [1, 2, [3]]
b = func(a)

then b is a reference to a[2]. To be more precise, both b and a[2] are references to the same list object ([3]).

Therefore,

b.append(4)
print(a)  # --> [1, 2, [3, 4]]

But you have to understand that the assignment operator = in Python does not mutate the left-hand side, it only assigns a new name to the right-hand side.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • Can you provide documentation? – Bob Feb 28 '18 at 22:18
  • @Adrian The answers, and linked external resources, of this question are quite comprehensive: https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference – mkrieger1 Mar 01 '18 at 11:39
1

No. = in Python rebinds, so you can't return a C++-style reference. And Python slicing copies, so trying to return a list slice and assign to that doesn't affect the original list.

The closest you could get without extreme measures would be to return the index, and let the caller use it to reassign, e.g.:

def func(lst):
    return ... some computed index, e.g. 2 ...

mylist = [7, 8, 9, 10]
mylist[func(mylist)]        # Gets 9
mylist[func(mylist)] = 11   # mylist is changed to [7, 8, 11, 10]

This is terrible code though; typically, you'd write a function to either return a new, mutated list (leaving the original unchanged) or mutate a list in place (returning nothing, implicitly None).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
1

You can also do something like this by passing the assignment as a argument for the function, since Python does not support C++ returning values by reference:

def func(lst, change_val = None):
    if change_val is not None:
        lst[2] = change_val
    return lst[2] 
lst = [7, 8, 9, 10]
print(func(lst))        # prints 9
print(func(lst, 11))  # prints 11; lst is changed to [7, 8, 11, 10]
print(lst) # prints [7, 8, 11, 10] since it was changed before
Taku
  • 31,927
  • 11
  • 74
  • 85