0

What I'm trying to achieve here is changing the value of a name passed to a function, like in C#, one can pass a variable then any changes made to it persist even after execution. I was reading on name binding, and AFAIK, if one passes a name to a function, that function creates a name in its local namespace binded to the object passed by reference (everything in python is an object). Since i was working with lists, i thought of a work-around which is clearing the list, then extending it with another one (through mutable objects).

EXAMPLE:

def function(array):
    #initial length 
    length = len(array)
    #A bunch of code
    #At this point of execution, list length should have changed greatly
    array = array[length:] 
    #my workaround: temp = array[:length] ; array.clear() ; array.extend(temp)
    return something_else
#Before execution list = [1,2,3]
function(var) # inside function, list = [4,5,6]
#After execution list = [1,2,3,4,5,6] #because i use list.append(x)

NOTE:

Is there any way to mimic that effect in case it's actually not possible.

L0neW0lf
  • 33
  • 7
  • 1
    C# and python both pass objects by reference, so there shouldn't be any difference. You can mutate objects just like in C# and the changes will persist even after the function, so I'm a bit confused about your question. – Ted Klein Bergman Feb 17 '21 at 18:27
  • 3
    Python is more like Java in the way it passes parameters. It does not have `out` or `ref` options like C#. Re-assigning the parameter (`lst = something`) has no effect on the original after the function ends. However, as you have learned, mutating it (`lst.append()`, etc) does work. Here's a good explanation: [How do I pass a variable by reference?](https://stackoverflow.com/a/986145) – 001 Feb 17 '21 at 18:29
  • Am I missing something here? why not having "array" as a global variable outside the function and use it inside the function (no need to pass it as function args)? – Payam Feb 17 '21 at 18:29
  • @Payam Because you might not want the scope of the variable to be global, but restricted to local scope. – Ted Klein Bergman Feb 17 '21 at 18:30
  • This smells of [an XY problem](https://meta.stackexchange.com/q/66377/322040). Can you explain why you don't want to use any of the caller passed values within the function, but still want to use the same name? Seems like the simple solution is making your own, separate, `list` within the function, and at some point extending the caller's `list` before returning, while otherwise not using the caller's `list` at all. It's not Pythonic (the Python solution is usually just returning multiple values, e.g. `return something_else, new_list`), but it's simpler than using one name for two things. – ShadowRanger Feb 17 '21 at 18:31
  • @Payam, because i would have to declare global for each variable i want to modify inside the function itself. – L0neW0lf Feb 17 '21 at 18:35
  • @TedKleinBergman, i want to use features of C#, out or ref keywords, as said above by Johny Mopp, so i can change the value of a name of an outer scope for a new one. In trying to achieve this i relied on mutable objects, which i would want to avoid... because it wont be always about mutable objects. (i hope i dont have this comment duplicate) – L0neW0lf Feb 17 '21 at 18:38
  • @L0neW0lf You can't really do that, not without things that would be considered hacks (like using the `globals` dict). Question is why you need it? C# might need it so you can "return" multiple values, but Python allows you to return multiple values natively. Can't you just do `var = function(var)`? Or what is the problem you're trying to solve? – Ted Klein Bergman Feb 17 '21 at 18:42
  • @TedKleinBergman, thanks for your reply, actually i cant, because i return a boolean value to tell whether operation was successful or not, execution flow depends a lot on that value, that's why i'm quite in a strife. – L0neW0lf Feb 17 '21 at 18:46
  • 2
    @L0neW0lf Yes, but you can return multiple values. Like `success, var = function(var)`. And then check the `success` parameter. Or raise an exception on failure and surround it with a try-except? And as said above, you can still in many cases modify `var` inside the function by simply mutating it (i.e. not rebinding it), if it's an object. – Ted Klein Bergman Feb 17 '21 at 18:51
  • @TedKleinBergman, thanks, i finally got to the idea that this feature is not part of python way for returning multiple values, i'll experiment with the hacky way (globals), then to solve the problem, implent returning a tuple. – L0neW0lf Feb 17 '21 at 18:54
  • Now title should go according to my real question. – L0neW0lf Feb 17 '21 at 19:07

1 Answers1

0

Don't write functions that mutate their arguments; idiomatic Python basically never does this, and writing code that does this is just asking for trouble. C# keeps this manageable by requiring both function and caller to explicitly acknowledge the out semantics; with Python, like with C++, it's invisible from the caller side, and many people may not realize mutation will happen.

Just copy as needed to avoid modifying the caller's values, and use multiple returns to return all the things you need to return as a tuple (it's trivial in Python; even C# encourages value tuples for this purpose in C# 7+):

def function(inarray):
    array = inarray[:]  # Shallow copy input array

    # Do stuff modifying array and computing something_else

    # Return both
    return something_else, array

var = [...]  # Some initial value of var
something, var = function(var)  # Call and unpack result

To solve your problem as described, even if it's usually a bad solution, your workaround isn't needed; you can del slices, not just read and write them, so instead of:

def function(array):
    #initial length 
    length = len(array)
    #A bunch of code
    #At this point of execution, list length should have changed greatly
    array = array[length:]
    return something_else

you'd do:

def function(array):
    #initial length 
    length = len(array)
    #A bunch of code
    #At this point of execution, list length should have changed greatly
    del array[:length]  # Deletes all entries that existed when function was entered
    return something_else

which modifies array in place, removing the first length items.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • I buy your explanation, many thanks, I agree totally with you, also in that mutating mutable parameters in python is a discouraged design and even could lead to a waste of time and frustration. As a matter of fact I used the function without getting to remember that right after execution my list would mutate. – L0neW0lf Feb 18 '21 at 00:13