3

I have the following example function:

def example(inp):
    if not isinstance(inp, list):
        return 'Not list'
    else:
        return 'List'

>>> example('asdf')
'Not list'
>>> example(['asdf',])
'List'

And yet pylint complains that:

no-else-return: Unnecessary "else" after "return"

Why does it raise that warning, which seems so silly?

samuelbrody1249
  • 4,379
  • 1
  • 15
  • 58

1 Answers1

8

Because it is equivalent to this shorter code:

if not isinstance(inp, list):
    return 'Not list'
return 'List'

Specifically, when the body of an if ends with a return, it doesn't matter whether the code for the false condition is in an else or not: it will only be executed if the if condition is false, because if the condition is true, the function will return at the end of the if body.

Pylint seems to prefer the more compact version. But both versions are perfectly correct, and I'd say that sometimes, the if/else version is clearer. Personally, I do if/else if the two bodies are roughly the same size, and only the if if its body is short and the would-be else body is long, thus saving an indentation level for it. Bonus: if you have a long if body and a short else body and the else ends in return (or there's nothing more in the function after the if/else), negate the condition and end up with a short if body and no else.

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • 1
    I see, thanks for explaining. Yes I have it as two separate indentations for readability. I could just as well combine everything into a single line and `return 'List' if isinstance(inp, list) else 'Not List'`. I don't agree with pylint here, but anyways I guess that's an opinion and thanks for your detailed answer. – samuelbrody1249 Apr 03 '21 at 20:57
  • 3
    I agree, the `else` version is preferable IMO. Pylint has a lot of dumb ideas, this isn't the only one. – juanpa.arrivillaga Apr 03 '21 at 20:58
  • @juanpa.arrivillaga out of curiosity, what are some of the other odd ones, and I'll add those to my pylintrc? – samuelbrody1249 Apr 03 '21 at 21:06
  • 2
    @samuelbrody1249 one that is clearly stupid is: "W1638 ange built-in referenced when not iterating Used when the range built-in is referenced in a non-iterating context (returns an iterator in Python 3)" It's dead wrong about `range` returning an iterator. a `range` object is a perfectly acceptable container. – juanpa.arrivillaga Apr 03 '21 at 21:08
  • https://pycodequ.al/docs/pylint-messages/w1638-range-builtin-not-iterating.html – samuelbrody1249 Apr 03 '21 at 21:12
  • 1
    @AasmundEldhuset a python 3 range object is not an iterator- you can call list on it multiple times, you can use efficient `in` contains operations etc. – Chris_Rands Apr 03 '21 at 21:14
  • @AasmundEldhuset you are wrong. `range` objects **are not generators**, they aren't even iterators, try `next(range(10))`. You can iterate over a range object as many times as you want, since it isn't an iterator. these are all basic facts about `range` objects that you can easily verify for yourself. `range` objects are essentially [immutable sequences](https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes), that support all sequence operations, indexing, slicing (constant time!) etc. They also have constant time membership testing for `int` objects – juanpa.arrivillaga Apr 03 '21 at 21:15
  • Here's a basic implementation of a similar idea to Python's built-in `range`: https://gist.github.com/juanarrivillaga/f4372d098913a1f66764833376f0e020 to get a good idea of what it actually is – juanpa.arrivillaga Apr 03 '21 at 21:20
  • 1
    @juanpa.arrivillaga: Indeed - today I learned! Apologies, and thanks for correcting me. Deleted my comment (which, for posterity, incorrectly claimed that a `range` is a generator and can only be used once). – Aasmund Eldhuset Apr 03 '21 at 22:19