Search code examples
pythonreturnin-place

Return statement in method that modifies in-place


Say I wanted a simple method that takes a list and modifies it in-place by appending 5 to it. Below are two methods that do this, but ret(l) returns while no_ret(l) does not.

def ret(l): 
   l.append(5) 
   return l 


def no_ret(l):
   l.append(5)

Which method is more pythonic? I'm guessing no_ret() because if the method is for in-place modification, then there is no need to return since we already have a reference to the object being modified.

And how do they compare in terms of time and space efficiency?

  • For time, I believe ret(l) is slower since the return statement adds additional overhead.
  • For space, I believe they are the same since ret() simply returns a reference (memory address) so no new memory is used.

I noticed that Python provides both the in-place non-returning instance method sort() and non-in-place returning static method sorted(). However I can't make my in-place methods to be instance methods of default classes like list.


Solution

  • When evaluating how pythonic some code is we should look to the standard library. Two mutable objects are:

    list with in-place method append:

    mylist = [1,2,3]
    mylist.append(5).append(3) # Throws error 'NoneType' object has no attribute 'append'
    

    dict with in-place method update:

    mydict={}
    mydict.update(mydict).update(mydict) # Throws error 'NoneType' object has no attribute 'update'
    

    We can conclude that in-place methods should return None

    Performance considerations:

    test.py

    def ret(l): 
       l.append(5) 
       return l
    
    def no_ret(l):
       l.append(5)
    
    def test_ret():
        l=[]
        for i in xrange(100000):
            ret(l)
    
    def test_no_ret():
        l=[]
        for i in xrange(100000):
            no_ret(l)
    

    In IPython with %timeit:

    In [3]: %timeit test_ret()
    10 loops, best of 3: 163 ms per loop
    
    In [4]: %timeit test_no_ret()
    10 loops, best of 3: 161 ms per loop
    

    The not-returning method is not faster than the returning in C-Python.

    edit to comment

    You can make any method a instance method of a class in python:

    class MyObject(object):
        def __init__(self):
            self.l=[]
    
    def ret(self):
        self.l.append(randint(10,15))
        return self
    

    IPython:

    In [3]: setattr(MyObject,"ret", ret)
    
    In [4]: MyObject().ret()
    Out[4]: <test.MyObject at 0x7f6428e8ccd0>
    
    In [5]: MyObject().ret().l
    Out[5]: [15]