0

I am looking through some code and found the following lines:

def get_char_count(tokens):
    return sum(len(t) for t in tokens)


def get_long_words_ratio(tokens, nro_tokens):
    ratio = sum(1 for t in tokens if len(t) > 6) / nro_tokens
    return ratio

As you can see, in the first case the complete expression is returned, whereas in the second case the expression is first evaluated and stored into a variable, which is then returned. My question is, which way is the better, more pythonic way?

I am not entirely sure how Python handles returns from functions. Does it return by reference, or does it return the value directly? Does it resolve the expression and returns that? In summary, is it better to store an expression's value into a variable and return the variable, or is it also perfectly fine (efficiency, and PEP-wise) to return the expression as a whole?

Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • 1
    As per pep8 `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` – Abdul Niyas P M Feb 06 '18 at 13:20
  • I would keep the first and re-write the second as `return sum(len(t) > 6 for t in tokens) / nro_tokens` – Ma0 Feb 06 '18 at 13:22

2 Answers2

1

Does it return by reference[?]

Effectively yes. When you return an object, the id (i.e. memory address) of the object inside the function is the same as the id of the object outside the function. It doesn't make a copy or anything.

[...] or does it return the value directly?

If you're saying "is it like the 'pass-by-value' argument passing system of many programming languages, where a copy is made and changes to the new value don't affect the original one? Except for returning values instead of passing them?", then no, it's not like that. Python does not make a copy of anything unless you explicitly tell it to.

Does it resolve the expression and returns that?

Yes. Expressions are almost always resolved immediately. Times when they aren't include

  • when you have defined a function (but haven't executed it), the expressions in that function will not have been resolved, even though Python had to "pass over" those lines to create the function object
  • when you create a lambda object, (but haven't executed it), ... etc etc.

 

In summary, is it better to store an expression's value into a variable and return the variable, or is it also perfectly fine (efficiency, and PEP-wise) to return the expression as a whole?

From the perspective of any code outside of your functions, both of the approaches are completely identical. You can't distinguish between "a returned expression" and "a returned variable", because they have the same outcome.

Your second function is slightly slower than the first because it needs to allocate space for the variable name and deallocate it when the function ends. So you may as well use the first approach and save yourself a line of code and a millionth of a second of run-time.

Here's an example breakdown of the byte code for two functions that use these different approaches:

def f():
    return 2 + 2

def g():
    x = 2 + 2
    return x

import dis
print("Byte code for f:")
dis.dis(f)

print("Byte code for g:")
dis.dis(g)

Result:

Byte code for f:
  2           0 LOAD_CONST               2 (4)
              2 RETURN_VALUE
Byte code for g:
  5           0 LOAD_CONST               2 (4)
              2 STORE_FAST               0 (x)

  6           4 LOAD_FAST                0 (x)
              6 RETURN_VALUE

Notice that they both end with RETURN_VALUE. There's no individual RETURN_EXPRESSION and RETURN_VARIABLE codes.

Kevin
  • 74,910
  • 12
  • 133
  • 166
0

While I prefer the first approach (Since it uses less memory), both expressions are equivalent in behavior.

The PEP8 Style Guide doesn't really say anything about this, other than being consistent with your return statements.

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).

Arnav Borborah
  • 11,357
  • 8
  • 43
  • 88