38

Consider this code:

def foo(foo_input):
    if 0 <= foo_input <= 100:
        return f_input

This returns None in the case where foo_input > 100. But could it actually not return anything? Or does a function always have to return something?


See also: return, return None, and no return at all?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
beoliver
  • 5,579
  • 5
  • 36
  • 72
  • 1
    Why does it bother you the function returns `None`? – Sven Marnach Apr 08 '12 at 22:20
  • chaining functions, and if I was going to have to write try : except clauses for each of them (because of the none that is passed) – beoliver Apr 08 '12 at 22:22
  • use969617: What would happen if no output was returned? If it was the execution being skipped, then you can get the same functionality using an if statement to check for ``None`` - although if it's more likely you will get a result than None, catching exceptions is a better idea, as Python follows the _ask for forgiveness, not permission_ principle. – Gareth Latty Apr 08 '12 at 22:23
  • I was wondering if I could get away without even having to use if statements :) – beoliver Apr 08 '12 at 22:25
  • 1
    Well, remember that exceptions fall through, so if you want to catch an error due to ``None`` anywhere in your process, then you can simply wrap your entire block in a catch for ``TypeError``s. Obviously only wrap the segment for which this is necessary, but there is no need for a ton of the same ``try ... except ...`` blocks – Gareth Latty Apr 08 '12 at 22:30
  • @Lattyware - do you know of an example that I could view? – beoliver Apr 08 '12 at 22:32
  • @user969617 An example of what, sorry? – Gareth Latty Apr 08 '12 at 22:33
  • @Lattyware - sorry , of wrapping a black in a catch for TypeError – beoliver Apr 08 '12 at 22:37
  • @user969617 Literally just ``try: ... except TypeError: ...`` where ``...`` is any python code you want (properly indented and with new lines the comment doesn't give me, naturally). See the [python docs](http://docs.python.org/tutorial/errors.html) for more. – Gareth Latty Apr 08 '12 at 22:45

6 Answers6

44

Functions always return something (at least None, when no return-statement was reached during execution and the end of the function is reached).

Another case is when they are interrupted by exceptions. In this case exception handling will "dominate over the stack" and you will return to the appropriate except or get some nasty error :)

Regarding your problem I must say there are two possibilities: Either you have something to return or you do not have.

  • If you have something to return then do so, if not then don't.
  • If you rely on something being returned that has a certain type but you cannot return anything meaningful of this type then None will tell the caller that this was the case ( There is no better way to tell the caller that "nothing" is returned then by None, so check for it and you will be fine)
30

No. If a return statement is not reached before the end of the function then an implicit None is returned.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
11

If a return statement is not reached, the function returns None.

def set_x():
    x = 2
Billjk
  • 10,387
  • 23
  • 54
  • 73
4

I'm not sure what you really are trying to do. Here are a few things you might like:

def foo(foo_input, foo_default):
    if 0 <= foo_input <= 100:
        return f_input
    else:
        return foo_default


def foo(foo_input):
    if 0 <= foo_input <= 100:
        return f_input
    raise ValueError, "foo_input was not in range [0, 100]"

Wait, you said "filter". Are you filtering a series of values and you just want to extract the ones that meet a criteria? That's easy in Python:

def foo_check(x):
    return 0 <= x <= 100

filtered_list = [x for x in unfiltered_sequence if foo_check(x)]

And you said "chaining functions". Again that's easy if we are talking about filtering a sequence:

def foo_filter(seq):
    for x in seq:
        if 0 <= x <= 100:
            yield x

def other_filter(seq):
    for x in seq:
        if meets_criterion(x):
            yield x


def do_the_task(seq):
    for x in other_filter(foo_filter(seq)):
        do_something(x)

EDIT: Here is a nice introduction to iterators and generators in Python. http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/

steveha
  • 74,789
  • 21
  • 92
  • 117
  • and this would be fine if I was filtering 100 or so a second? – beoliver Apr 08 '12 at 22:35
  • There is no reason to use a function there, it's clearer just to do it as ``[x for x in sequence if 0 <= x <= 100]``. – Gareth Latty Apr 08 '12 at 22:36
  • @steveha: why use a list comprehension when there is a built-in `filter` function? `filtered_list = filter(foo_filter, unfiltered_sequence)` – Nobody moving away from SE Apr 08 '12 at 22:36
  • @Lattyware: I would say otherwise. Also it depends on how used the reader is to the one or the other construction. And as a lazy programmer I would say: it is shorter :) – Nobody moving away from SE Apr 08 '12 at 22:38
  • in a larger context, I am reading a serial Connection, ( strings between 0 - 1023 ), I am filtering out strings that contain characters as well ie. '102?3' , this filter is the third in a series, that then only allows a through ones between 1-100. – beoliver Apr 08 '12 at 22:41
  • If you were filtering 100 a second, this would be fine. For highest performance in Python you should write "lazy" code that doesn't build temporary lists and then delete them, so the `yield` code is good for chaining filters. – steveha Apr 08 '12 at 22:42
  • this is why i wasn't touching list comprehension – beoliver Apr 08 '12 at 22:42
  • or at least I didn't think I was – beoliver Apr 08 '12 at 22:43
  • @steveha Or just use generator expressions. ``(x for x in (x for x in unfiltered_iterator if other_filter(x)) if foo_check(x))``. – Gareth Latty Apr 08 '12 at 22:43
  • @Nobody, if you are part of the functional programming community, you probably prefer `filter()`. The Python community usually prefers list comprehensions. Also, in Python 2.x, `filter()` is not lazy and always builds an object (by default a list). – steveha Apr 08 '12 at 22:44
  • I'm using python 3.X - and I'm quite partial to Haskell – beoliver Apr 08 '12 at 22:45
  • @Lattyware, I agree that for this simple example putting the expression in the list comprehension is reasonable. I have a tendency to break things out into functions for readability, especially when answering questions on StackOverflow. – steveha Apr 08 '12 at 22:46
  • @steveha Naturally readability is a judgement call and what works in one situation won't always work in others. – Gareth Latty Apr 08 '12 at 22:47
  • @steveha: Well I am not that "functional" ^^ And for me it seemed always the other way round. Also I do not see why the filter function should create an additional list compared to the comprehension. – Nobody moving away from SE Apr 08 '12 at 22:47
  • In Python 3.x, the built-in `filter()` is lazy. But you can also write a loop that will `yield` answers as I showed, and you can also write a generator expression. – steveha Apr 08 '12 at 22:48
  • 1
    @Nobody, fire up Python 2.x, and from the `>>>` prompt type `help(filter)` and read what it says. It says, quite plainly, that it will return a list, a tuple, or a string. Now fire up Python 3.x and do the same thing; it says, quite plainly, that it will return an iterator. So maybe you don't see why it should do it, but I am talking about what it actually does. I think the reason is historical: I think `filter()` was introduced before iterators were added. Of course in Python 2.x you can use `itertools.ifilter()` or write your own function that loops and calls `yield`. – steveha Apr 08 '12 at 22:52
  • @steveha what constitutes a 'sequence'? if they are numbers read from a serial connection - they are still a sequence? – beoliver Apr 08 '12 at 22:53
  • A "sequence" means anything that Python can iterate over. A list or tuple is a sequence. A string is a sequence of characters. Any iterator is a sequence as well (generators are a kind of iterator). You should probably have a function that reads the serial data and calls `yield` to return it. If you have a class wrapping your serial port, the class instance should work as an iterator: http://stackoverflow.com/questions/19151/build-a-basic-python-iterator – steveha Apr 08 '12 at 22:59
  • @steveha: Well of course filter returns a list but the list comprehension does this too and that was what I asked for. But lets not stretch this discussion unnecessary. Thank you for pointing me at the `help` I forget about it from time to time (I am a google zombie :). – Nobody moving away from SE Apr 08 '12 at 23:06
  • Nice answer. A noob would have found it useful to have an explanation about the use of ``yield``, as opposed to ``return`` or something else. – PatrickT Jun 29 '16 at 16:41
1

I sort of like the implicit return None but pylint flags it as bad style, warning:

Either all return statements in a function should return an expression, or none of them should.pylint(inconsistent-return-statements)

Hence,

def foo(foo_input):
    if 0 <= foo_input <= 100:
        return f_input
    return None

might be better style, even if they are functionally the same.


More info available here, where the Pylint change-log states:

A new Python checker was added to warn about inconsistent-return-statements. A function or a method has inconsistent return statements if it returns both explicit and implicit values ...

According to PEP8, if any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None, and an explicit return statement should be present at the end of the function (if reachable).

Dustin Michels
  • 2,951
  • 2
  • 19
  • 31
0

Functions always return something.

All 3 functions below return None:

def test1():
    print("test1")
    return None

def test2():
    print("test2")
    return

def test3():
    print("test3")
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129