Skip to content Skip to sidebar Skip to footer

Most Pythonic Way To Port This Tuple Unpacking With Lambda From Python 2 Into Python 3

I have the following Python 2 code which unpacks a tuple inside a lambda. This lambda is contained inside a for loop. for lab, lab_pred, length in zip(labels, labels_pred, sequ

Solution 1:

I think the best solution would be to not use map and lambda, use a list comprehension instead:

accs += [a == b for a, b in zip(lab, lab_pred)]

Solution 2:

If you prefer functional style there is:

from operator import eq
from itertools import starmap

accs.extend(starmap(eq, zip(lab, lab_pred)))

Solution 3:

This is an intentional regression in Python3 (although I don't know the rationale - if you happen to - please comment below) in order to discourage lambda and in the long run - using Python2 altogether.

Pythonic quick fix

(edited, previous idea didn't really work) See also here: https://stackoverflow.com/a/47947955/1338797

If your lambda is flat, there is an upgrade path to Python3:

lambda (x, y): x + y

becomes

lambda xy: (lambda x, y: x + y)(*xy)

Nice, but it has 2 downsides:

  1. Welcome to Lisp ;) It's intricate (but pythonic, like str.join)

  2. It won't cut nested tuples like: lambda ((x0, y0), (x1, y1)): abs(x1-x0) + abs(y1-y0)

  3. An extra lambda.

Why are they forcing us to use small def functions?

Lambdas are bad for a couple of reasons (about 3-4 may be valid):

  1. Just because: lambda is broken in Python. It's "pythonic" to know this.

  2. Lambdas (in Python, not in some other language! I won't repeat this disclaimer in the following points) can only handle single expressions.

  3. Lambdas have no __name__s.

  4. Lambdas have ugly __repr__esentations.

  5. Lambdas are can make code slow. (when often created unnecessarily)

  6. Lambdas are slow. (to run, compared to competing constructs)

(5 & 6 are relevant only in most performance-critical code anyway).

Small functions in Python are recommended, and they think it's no problem, because it only takes 2 lines more. (2 instead of 0, because a lambda can be inline). Thus lambda x, y: x == y becomes:

def are_equal(x, y):
    return x == y

(forget operator.eq here, not every lambda will have it's equivalent in this module anyway).

Actually, if you stick to PEP-8, this function takes 4 extra lines in local scope, and 6 extra lines in global scope, including empty line padding.

Example situation: how to keep compatibility

Suppose you have a list, or other iterable of tuples. You want to get a max value using max(..., key=...). After that, you can directly unpack the tuple. In the days of Python2 it would be:

x, y, w, h = max(faces, key=lambda (x, y, w, h): w * h)

(Again, you could lecture me here about the superiority of typing.Namedtuples, dataclasses, or @attr.s, but right now I get a list of tuples from OpenCV.

I found the least painful way to be to use a function:

def face_area(*args):
    x, y, w, h = args if len(args) == 4 else args[0]
    return w * h

 x, y, w, h = max(faces, key=face_area)

Python2 will get a nested tuple in args, and Python3 will get a tuple of length 4. You can unpack it manually as above, and then proceed with returning your result.

Some would say, it gives the code an occasion to become more self-documented, because I've had to think of a name for the key function.


Post a Comment for "Most Pythonic Way To Port This Tuple Unpacking With Lambda From Python 2 Into Python 3"