Correct Style for Python Functions That Mutate the Argument

Correct Style for Python functions that mutate the argument

The first way:

def change(array):
array.append(4)

change(array)

is the most idiomatic way to do it. Generally, in python, we expect a function to either mutate the arguments, or return something1. The reason for this is because if a function doesn't return anything, then it makes it abundantly clear that the function must have had some side-effect in order to justify it's existence (e.g. mutating the inputs).

On the flip side, if you do things the second way:

def change(array):
array.append(4)
return array

array = change(array)

you're vulnerable to have hard to track down bugs where a mutable object changes all of a sudden when you didn't expect it to -- "But I thought change made a copy"...

1Technically every function returns something, that _something_ just happens to be None ...

Preferred way of mutating dictionary inside function/method and returning to the caller?

I'd say better to not return

Well look no further than list.sort()

It sorts list in-place, meaning original list changes, and nothing is returned.

The reason for returning None is so users do not get confused (thinking original list is preserved)

The most important thing is to properly name what you're doing

e.g.

def fill_with_defaults(cfg):
...

And then fill_with_defaults(configuration) is looking perfectly readable

And as for "explicit", no return value is even better because the reader knows something is changed in-place

meaning he wouldn't consider config2 = fill_with_defaults(configuration)

leading to buggy code (cuz changing one config affects other)

Python changing variables vs arrays in functions?

  1. You have to look at the documentation. Generally: numbers, strings and tuples are immutable.

    Regarding specific methods, there is a general rule also: if the method returns something then it doesn't modify the argument. Methods that modify the argument return None.

  2. a += b is equivalent to a = a + b if a is immutable. If a is mutable then using += will mutate the object. Basically += is just a method call (to __iadd__). In the case of immutable objects the method returns a new object and doesn't mutate the original value, for mutable object the object is mutate and it returns itself.

Understanding Python's call-by-object style of passing function arguments

The key difference is that in C-style language, a variable is a box in memory in which you put stuff. In Python, a variable is a name.

Python is neither call-by-reference nor call-by-value. It's something much more sensible! (In fact, I learned Python before I learned the more common languages, so call-by-value and call-by-reference seem very strange to me.)

In Python, there are things and there are names. Lists, integers, strings, and custom objects are all things. x, y, and z are names. Writing

x = []

means "construct a new thing [] and give it the name x". Writing

x = []
foo = lambda x: x.append(None)
foo(x)

means "construct a new thing [] with name x, construct a new function (which is another thing) with name foo, and call foo on the thing with name x". Now foo just appends None to whatever it received, so this reduces to "append None to the the empty list". Writing

x = 0
def foo(x):
x += 1
foo(x)

means "construct a new thing 0 with name x, construct a new function foo, and call foo on x". Inside foo, the assignment just says "rename x to 1 plus what it used to be", but that doesn't change the thing 0.

Edit in-place function should return object reference or not?

The convention in Python is that if a function operates in-place, it returns None. You can see this for example in the list methods .sort and .append, which modify the lists they are called on but return None.

Note, your first example does not need return at all.



Related Topics



Leave a reply



Submit