Есть ли более эффективный способ написания функции объединения? - PullRequest
0 голосов
/ 08 мая 2019

После просмотра кода была найдена следующая функция:

def coalesce(*args, null=None):
    return next((obj for obj in args if obj is not null and obj != null), null)

Есть ли более эффективный способ выполнения этой операции или более питонский подход к проблеме?

Первая альтернатива была следующей:

def coalesce(*args):
    return next(filter(None, args), None)

Вот второй вариант, который был опробован:

def coalesce(*args, null=None):
    return next(itertools.filterfalse(functools.partial(operator.eq, null), args), null)

Это третья альтернатива, которая пришла в голову:

def coalesce(*args):
    return next((obj for obj in args if obj is not None), None)

Четвертая альтернатива была написана в надежде, что код, написанный на C, будет быстрее:

def coalesce(*args):
    return next(itertools.filterfalse(functools.partial(operator.is_, None), args), None)

Используя timeit, результаты синхронизации для трех различных функций были:

  • +0,7040689999994356
  • +0,3396129999891855
  • +0,8870604000112507
  • +0,5313313000078779
  • +0,8086609000019962

Казалось бы, это указывает на то, что вторая функция предпочтительнее, но это не отвечает на вопрос о том, какая из них наиболее питонская.

1 Ответ

2 голосов
/ 09 мая 2019

Рассматривали ли вы

def coalesce4(*args):
    for x in args:
        if x is not None:
            return x

, что значительно быстрее, чем три функции, показанные в вашем вопросе:

In [2]: import tmp

In [3]: %timeit tmp.coalesce1(None, None, 1)
782 ns ± 1.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit tmp.coalesce2(None, None, 1)
413 ns ± 8.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [5]: %timeit tmp.coalesce3(None, None, 1)
678 ns ± 0.782 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [6]: %timeit tmp.coalesce4(None, None, 1)
280 ns ± 0.218 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

В Python 3.8 у вас будет опция

def coalesce5(*args):
    if any(rv := x for x in args if x is not None):
         return rv

, что фактически совпадает с вашим третьим вариантом, а время работы аналогичной функции показывает, что она примерно равна скорости (~ 680 нс).

...