0

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.

onepiece
  • 3,279
  • 8
  • 44
  • 63
  • Code Review is probably better for this kind of thing. But usually I would use the function (ret) rather than the procedure (no_ret) because then whenever you want to splice this code into another project, it's a lot simpler to do so. – Sophie Coyne Oct 25 '15 at 11:28
  • Unlike Visual Basic, there is no distinction in Python between functions that return or don't return values @Rariolu – Burhan Khalid Oct 25 '15 at 11:33
  • @Rariolu I'm also asking about the internals of the return statement in Python, so I figured it would be more suitable here. And could you give an example of "splice this code into another project"? – onepiece Oct 25 '15 at 11:35
  • @BurhanKhalid That depends on the way it's implemented. If you don't return a value then usually you'll be operating on a variable that exists outside the subroutine. Btw fyi, a function returns a value by definition, a procedure does not. A subroutine is a collective term for both. – Sophie Coyne Oct 25 '15 at 11:35
  • @onepiece say for example you had a function that returns a boolean depending on whether or not a letter is a vowel, this could be useful in various other projects. However, if it's specifically written for one project, simply copying and pasting it wouldn't allow it to work for another project. – Sophie Coyne Oct 25 '15 at 11:37

2 Answers2

2

The first thing you should not be doing, is replicating existing functionality with your own methods - however, I assume you are just giving an example and this isn't actual code that you are planning to run.

All methods in Python return a value. If a method doesn't have a return statement, it returns None.

Any function that modifies in-place and does not return; should not return a value. This makes sense because it is how the "normal" in-place methods work.

The correct implementation would be:

def do_something(foo, bar):
   foo.do_something_inplace(bar)
   return None # or simply, return

A blank return is the same as return None, but remember - explicit is better than implicit.

As far as your other question regarding space and time efficiencies, this will depend entirely on the implementation of the method that is actually doing the replacement. As you are writing a proxy to a built-in method in your example, the "speed penalty" is negligible.

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • Could you elaborate more on "this will depend entirely on the implementation of the method that is actually doing the replacement", like what is "the replacement"? And could you give an example when the speed penalty is non-negligible? – onepiece Oct 25 '15 at 12:10
0

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]
Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
  • Thanks for providing examples. I would like to note that the in-place `append()` is an instance method of the default class `list`. However, I can't make my own in-place methods to be instance methods of outside classes, besides making a child class. I suppose this is an unavoidable limitation? – onepiece Oct 25 '15 at 13:53
  • check the edit, python is a language of consenting adults, there is very little that can't be changed at runtime. – Sebastian Wozny Oct 25 '15 at 14:02
  • In my method `ret(l)` above, is new memory allocated when `l` is returned? – onepiece Nov 16 '15 at 05:41
  • Why are you worried about that? – Sebastian Wozny Nov 16 '15 at 06:02
  • I was told that if I'm modifying input in-place (size doesn't change), then I should not return anything to save space. But I thought if I return the input, it would not allocate extra memory since the same reference is being returned. – onepiece Nov 16 '15 at 10:22