0

Suppose I a function whose goal it is to modify a list in place (it doesn't just happen to modify the list in place, it's purpose is to do so). Is there a mostly agreed upon convention for which of these patterns to follow? If not, is there a mostly agreed upon convection for which of these patterns NOT to follow?

Option 1: Don't return anything

def zero_first(ls):
    ls[0] *= 0

ls = [1, 2, 3]
zero_first(ls)
print(ls)  # output is [0, 2, 3]

Option 2: Return the list

def zero_first(ls):
    ls[0] *= 0
    return ls

# User can choose, but there is not "one obvious way to do it"
ls = [1, 2, 3]
zero_first(ls)
print(ls)  # output is [0, 2, 3]

# or
ls = [1, 2, 3]
ls = zero_first(ls)
print(ls)  # output is [0, 2, 3]

Option 3: Return a list, but because it's being returned, explicitly copy the list

def zero_first(ls):
    ls = ls.copy()
    ls[0] *= 0
    return ls

# Now there's only one way to do it
ls = [1, 2, 3]
ls = zero_first(ls)
print(ls)  # output is [0, 2, 3]
Alexander Soare
  • 2,825
  • 3
  • 25
  • 53
  • If someone wants to flag this for being opinion based, please consider that I'm asking for a well-known, commonly agreed upon convention. This means there should be something in PEP or some other standard document. – Alexander Soare Jan 11 '22 at 12:36
  • 3
    I doubt this has a PEP (although I might be wrong of course) but personally I would say option 1. If all your function does is modify the input, mutable object, it should not return anything, just modify the object, and a descriptive name will probably help – Tomerikoo Jan 11 '22 at 12:38
  • 1
    [Related](https://stackoverflow.com/questions/26027694/correct-style-for-python-functions-that-mutate-the-argument) – Cory Kramer Jan 11 '22 at 12:38
  • @CoryKramer Do you not think this is a dupe of that? – khelwood Jan 11 '22 at 12:40
  • @khelwood the other one is giving me enough info so that I don't really think another answer here will add anything. From my standpoint, it is – Alexander Soare Jan 11 '22 at 12:41
  • For the record, the option 3 is problematic as you are not actually modifying the list in-place. You are just creating a new object and returning it - that's a separate case – Tomerikoo Jan 11 '22 at 12:42
  • @Tomerikoo is it problematic in general, or only problematic if I don't make it clear that that's what it was designed to do? – Alexander Soare Jan 11 '22 at 12:45
  • 2
    I just meant that it's problematic as an example to your question because it's a bit different. It's not problematic in general. It should just be clear to the user what the function does/returns and whether it modifies the input. You can consider encapsulating your data in a class and making this function a method. Then it is much clearer. See the difference between `sort` and `sorted` - the former is a list ***method*** which modifies in-place and doesn't return anything while the latter is a built-in function that returns a new list – Tomerikoo Jan 11 '22 at 12:51
  • I was just thinking about it a bit more and wanted to clarify some things: why ***not*** use option 2: it seems to me too confusing. Should I just call it or save its return value? Does it matter? Of course it doesn't and this is why I would prefer option 1. Similarly, option 3 is even more confusing: Not only it returns the list, the original is ***not*** modified this time. This gets very confusing and you should simplify it: modifying the input - return nothing and just modify ; returns a new object - just return the new object and not modify – Tomerikoo Jan 11 '22 at 13:15
  • @Tomerikoo "returns a new object - just return the new object and not modify". This is option 3 right? Option 3 is a version that let's assume has documentation which says that it returns a new object. Then that's how it should be implemented. You should first copy the list to make sure it's a new object, then do stuff with it. So I'm confused about why "option 3 is even more confusing". Option 3 should exist for a developer should it not? – Alexander Soare Jan 11 '22 at 13:25
  • I'm sorry I might be getting condused myself. You are right ; my premise for it being "wrong" was the fact that you stated in your question for a function that ***changes*** a list in-place. For that purpose it feels wrong to return a new list. While for the ***expected*** use-case of `l = zero_first(l)` this looks the same to the outside, I could do `new_list = zero_first(first_list)` and end up with two different lists while the function supposedly changes the list in-place. I hope I manage to make myself clear this time :) – Tomerikoo Jan 11 '22 at 13:30
  • Ah yes that's my fault re. the premise. Thanks a lot @Tomerikoo! – Alexander Soare Jan 11 '22 at 13:40

0 Answers0