Deprecate bare return statements

FWIW “bare returns” (i.e. return vs return None ) I find are unintuitive as well. I think much of what is being said about bare except can also be said about “bare returns”. And while I feel PEP-8 does make an attempt to clarify this:

“”"
Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. 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).
“”"

Adding some more clarification about Bare Returns, and Bare Excepts would help me sleep at night. It’s also an incremental improvement which is non-breaking to users, which is also kindof neat for obvious reasons.

Also, howdy, I’m new.

-brugz

Would you mind clarifying what exactly applies for return? The main argument against except is it encourages mistakes (people use except when they mean except Exception). I don’t see return having a similar problem; it means 100% the same as return None and can be used interchangably without ambiguity.

While it is arguable whether the except situation should be handled by the language, I feel the return situation can be handled entirely by linters and formatters without drawbacks.

1 Like

Thanks for the suggestion.

Although I’m not in agreement that bare returns should be deprecated, I can definitely understand your argument. Ultimately, I think this comes down to more of a styling decision rather than something that should be officially enforced.

Agreed, the output of return without a value afterwards is very consistent, the None is implied in all cases.

Additionally, bare returns occur very frequently across the CPython repository. I don’t have an exact count, but I gathered an approximate one using git grep:

aeros@ArchAeros ~/r/cpython> git grep -E "^\s*return\$" | wc -l
1228

Even if it was determined that the meaning of return None is a bit more clear than return, it would require a non-trivial amount of refactoring across the repository. The change would not be overly difficult, but it would still require a significant time investment from the person who submits the PR, reviewers, and core devs to approve the changes.

Not to mention that it would require an update across the majority of Python textbooks if we were to deprecate it, and require any existing Python developers to relearn a fundamental component of the language. In this case, I don’t think the potential return (ba-dum-tss, pun intended) is worth the cost.

Hi, welcome to the Python development community!

From my experience, there are far more rejected ideas than there are approved ones, from both new members of the Python community and core developers. It is a million times better to make the effort to come up with a proposal and have it rejected than it is to never have made one at all. Even rejected ideas can lead to productive discussions. FWIW, my first idea was also rejected.

Don’t let this first experience discourage you, and if you’re at all interested in contributing I’d highly recommend checking out this brief “where should I start?” guide from Victor Stinner.

For a more in-depth guide, check out cpython-core-tutorial.readthedocs.io and devguide.python.org.

1 Like

aeros167, uranusjr,

Thank you for your responses.

I haven’t taken time to figure out the markdown style for Discord yet. So, rather than block quotes, let me try and respond to some of the concerns raised by both of you:

  1. return and return None can be used interchangebly, making them “intuitive enough”, but this does not makes them unambiguous.

    For example, void type methods don’t exist in Python[1]. So, to someone familiar with the concept, doing return might be surprised that return is actually returning anything at all. This might be a little bit weird for Java converts, for example, as they learn that void is not a Python concept, really.

  2. Updating PEP-8 for more clarity and encouraging Linters to do the same would be an acceptable start. I don’t think the language standard needs to be updated to actually deprecate the bare return. Perhaps we could in the future, similar to [2], but such a change would have to be much more deliberate, and probably go through the PEP dance.

  3. Re: updating the repos to default to return None over return being a non-zero engineering effort – I could probably do that in a weekend if folks would find the change beneficial. It’d be good experience learning GitHub/git/Python Developer Workflow, etc.

  4. aeros167@, thanks for getting-started resources

Further, I’d like to support my position with the following:

return vs return None is inconsistent with PEP 20 [3].

“Explicit is better than implicit.”

“There should be one-- and preferably only one --obvious way to do it.”

return is implicit, and the fact that the language spec supports both return and return None is repetitive and non-obvious.

Cheers,
Brugz

[1] https://www.tutorialspoint.com/How-to-return-void-from-Python-function
[2] Deprecate bare except
[3] https://www.python.org/dev/peps/pep-0020/

You can turn the table on this statement though. The void return concept (procedure) is by no means universal; there is a whole other linage of programming languages that does not have a distinct procedure type, but has everything as function (and thus returns something). Not having an interchangeable bare return is equally weird to e.g. Ruby and Rust converts. I consider this as something you have to learn when picking up a new language due to the language using a different paradigm. Python just happen to choose the one Java converts are not familiar with.

But readability counts, and that way may not be obvious at first unless you’re Dutch. :stuck_out_tongue:

In practice, return is often used to signify the function author’s intention. Using bare returns (or not using any returns at all) implies the function has side effects (modifies an input parameter, including self). Conversely, if a function explicitly returns something, including None, best practices suggest it should be without side effects (although it is much more common to see less-planned code in this regard). Using convention to distinguish between intentions is, in a way, another interpretation to have One Obvious Way.

2 Likes

That doesn’t necessarily dismiss my core point. Sure, void is not a concept that exists in several languages, but that doesn’t make return unambiguous.

I would argue that intentions are irrelevant. Either a style is explicit or it isn’t. I would further argue that this is why packages like MyPy and Typing are getting popular. Explicit code is easier to maintain.

But of course practicality beats purity, so, I’m open to other thoughts.

Perhaps as a hypothetical exercise: why not void? Why does:

def foo(bar):
  print(bar)

Return None and not have a behavior like void?

I could do X = foo('baz'), and this does not raise some kind of error. Should it? Why? Why not?

A bare return is not ambiguous. Let us list all the things that a
bare return could possibly mean in Python:

  1. same as “return None”

That’s one meaning, not two. That makes it unambiguous.

What is important are the rules of Python, not arbitrary other
languages. If you allow other languages, then you would have to argue
that “return None” is ambiguous, because None could be a variable
instead of the keyword.

Or that 2^3 is ambiguous, because in some languages ^ means
exponentiation.

Or to take it to an extreme, that everything is ambiguous, since there
are languages like Forth or Lisp which can redefine their own syntax or
create new control structures.

I would strongly object to deprecating bare returns. They are harmless,
and useful. I use them to signal to the reader that the function is
“procedure-like”, that it that you aren’t intended to use the return
value:

def mysort(sequence):
sequence.sort()
return

mysort(L)

as opposed to “return None”, which indicates that you should use the
return value:

def myfind(sequence, x):
index = sequence.find(x)
if index == -1:
return None
else:
return index

Obviously those are toy examples, but they illustrate the convention I
use: bare return for “procedure-like” functions, and return None for
returning None as a value or sentinel.

2 Likes