Why should functions always return the same type?

Question:

I read somewhere that functions should always return only one type
so the following code is considered as bad code:

def x(foo):
 if 'bar' in foo:
  return (foo, 'bar')
 return None

I guess the better solution would be

def x(foo):
 if 'bar' in foo:
  return (foo, 'bar')
 return ()

Wouldn’t it be cheaper memory wise to return a None then to create a new empty tuple or is this time difference too small to notice even in larger projects?

Asked By: self.self

||

Answers:

If x is called like this

foo, bar = x(foo)

returning None would result in a

TypeError: 'NoneType' object is not iterable

if 'bar' is not in foo.

Example

def x(foo):
    if 'bar' in foo:
        return (foo, 'bar')
    return None

foo, bar = x(["foo", "bar", "baz"])
print foo, bar

foo, bar = x(["foo", "NOT THERE", "baz"])
print foo, bar

This results in:

['foo', 'bar', 'baz'] bar
Traceback (most recent call last):
  File "f.py", line 9, in <module>
    foo, bar = x(["foo", "NOT THERE", "baz"])
TypeError: 'NoneType' object is not iterable
Answered By: lutz

Why should functions return values of a consistent type? To meet the following two rules.

Rule 1 — a function has a “type” — inputs mapped to outputs. It must return a consistent type of result, or it isn’t a function. It’s a mess.

Mathematically, we say some function, F, is a mapping from domain, D, to range, R. F: D -> R. The domain and range form the “type” of the function. The input types and the result type are as essential to the definition of the function as is the name or the body.

Rule 2 — when you have a “problem” or can’t return a proper result, raise an exception.

def x(foo):
    if 'bar' in foo:
        return (foo, 'bar')
     raise Exception( "oh, dear me." )

You can break the above rules, but the cost of long-term maintainability and comprehensibility is astronomical.

“Wouldn’t it be cheaper memory wise to return a None?” Wrong question.

The point is not to optimize memory at the cost of clear, readable, obvious code.

Answered By: S.Lott

It’s not so clear that a function must always return objects of a limited type, or that returning None is wrong. For instance, re.search can return a _sre.SRE_Match object or a NoneType object:

import re
match=re.search('a','a')

type(match)
# <type '_sre.SRE_Match'>

match=re.search('a','b')

type(match)
# <type 'NoneType'>

Designed this way, you can test for a match with the idiom

if match:
    # do xyz

If the developers had required re.search to return a _sre.SRE_Match object, then
the idiom would have to change to

if match.group(1) is None:
    # do xyz

There would not be any major gain by requiring re.search to always return a _sre.SRE_Match object.

So I think how you design the function must depend on the situation and in particular, how you plan to use the function.

Also note that both _sre.SRE_Match and NoneType are instances of object, so in a broad sense they are of the same type. So the rule that “functions should always return only one type” is rather meaningless.

Having said that, there is a beautiful simplicity to functions that return objects which all share the same properties. (Duck typing, not static typing, is the python way!) It can allow you to chain together functions: foo(bar(baz))) and know with certainty the type of object you’ll receive at the other end.

This can help you check the correctness of your code. By requiring that a function returns only objects of a certain limited type, there are fewer cases to check. “foo always returns an integer, so as long as an integer is expected everywhere I use foo, I’m golden…”

Answered By: unutbu

Best practice in what a function should return varies greatly from language to language, and even between different Python projects.

For Python in general, I agree with the premise that returning None is bad if your function generally returns an iterable, because iterating without testing becomes impossible. Just return an empty iterable in this case, it will still test False if you use Python’s standard truth testing:

ret_val = x()
if ret_val:
     do_stuff(ret_val)

and still allow you to iterate over it without testing:

for child in x():
    do_other_stuff(child)

For functions that are likely to return a single value, I think returning None is perfectly acceptable, just document that this might happen in your docstring.

Answered By: Jeffrey Harris

I personally think it is perfectly fine for a function to return a tuple or None. However, a function should return at most 2 different types and the second one should be a None. A function should never return a string and list for example.

Premature optimization is the root of all evil. The minuscule efficiency gains might be important, but not until you’ve proven that you need them.

Whatever your language: a function is defined once, but tends to be used at any number of places. Having a consistent return type (not to mention documented pre- and postconditions) means you have to spend more effort defining the function, but you simplify the usage of the function enormously. Guess whether the one-time costs tend to outweigh the repeated savings…?

Answered By: Pontus Gagge

Here are my thoughts on all that and I’ll try to also explain why I think that the accepted answer is mostly incorrect.

First of all programming functions != mathematical functions. The closest you can get to mathematical functions is if you do functional programming but even then there are plenty of examples that say otherwise.

  • Functions do not have to have input
  • Functions do not have to have output
  • Functions do not have to map input to output (because of the previous two bullet points)

A function in terms of programming is to be viewed simply as a block of memory with a start (the function’s entry point), a body (empty or otherwise) and exit point (one or multiple depending on the implementation) all of which are there for the purpose of reusing code that you’ve written. Even if you don’t see it a function always “returns” something. This something is actually the address of next statement right after the function call. This is something you will see in all of its glory if you do some really low-level programming with an Assembly language (I dare you to go the extra mile and do some machine code by hand like Linus Torvalds who ever so often mentions this during his seminars and interviews :D). In addition you can also take some input and also spit out some output. That is why

def foo():
  pass

is a perfectly correct piece of code.

So why would returning multiple types be bad? Well…It isn’t at all unless you abuse it. This is of course a matter of poor programming skills and/or not knowing what the language you’re using can do.

Wouldn’t it be cheaper memory wise to return a None then to create a new empty tuple or is this time difference too small to notice even in larger projects?

As far as I know – yes, returning a NoneType object would be much cheaper memory-wise. Here is a small experiment (returned values are bytes):

>> sys.getsizeof(None)
16
>> sys.getsizeof(())
48

Based on the type of object you are using as your return value (numeric type, list, dictionary, tuple etc.) Python manages the memory in different ways including the initially reserved storage.

However you have to also consider the code that is around the function call and how it handles whatever your function returns. Do you check for NoneType? Or do you simply check if the returned tuple has length of 0? This propagation of the returned value and its type (NoneType vs. empty tuple in your case) might actually be more tedious to handle and blow up in your face. Don’t forget – the code itself is loaded into memory so if handling the NoneType requires too much code (even small pieces of code but in a large quantity) better leave the empty tuple, which will also avoid confusion in the minds of people using your function and forgetting that it actually returns 2 types of values.

Speaking of returning multiple types of value this is the part where I agree with the accepted answer (but only partially) – returning a single type makes the code more maintainable without a doubt. It’s much easier to check only for type A then A, B, C, … etc.

However Python is an object-oriented language and as such inheritance, abstract classes etc. and all that is part of the whole OOP shenanigans comes into play. It can go as far as even generating classes on-the-fly, which I have discovered a few months ago and was stunned (never seen that stuff in C/C++).

Side note: You can read a little bit about metaclasses and dynamic classes in this nice overview article with plenty of examples.

There are in fact multiple design patterns and techniques that wouldn’t even exists without the so called polymorphic functions. Below I give you two very popular topics (can’t find a better way to summarize both in a single term):

  • Duck typing – often part of the dynamic typing languages which Python is a representative of
  • Factory method design pattern – basically it’s a function that returns various objects based on the input it receives.

Finally whether your function returns one or multiple types is totally based on the problem you have to solve. Can this polymorphic behaviour be abused? Sure, like everything else.

Answered By: rbaleksandar
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.