Как избежать множественных вызовов в понимании python. [Дубликат]

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

48
задан 1'' 17 August 2014 в 00:29
поделиться

12 ответов

Решение (лучше всего, если у вас есть повторяющееся значение x) было бы memoize функции f, т. е. создать функцию-оболочку, которая сохраняет аргумент, по которому вызывается функция, и сохраняет его, а не возвращает его, если тот же .

действительно простая реализация заключается в следующем:

storage = {}
def memoized(value):
    if value not in storage:
        storage[value] = f(value)
    return storage[value]

[memoized(x) for x in l if memoized(x)]

, а затем использовать эту функцию в понимании списка. Этот подход справедлив в двух условиях: один теоретический и один практический. Первый заключается в том, что функция f должна быть детерминированной, т. Е. Возвращает те же результаты при одном и том же входе, а другая в том, что объект x можно использовать в качестве словарных клавиш. Если первый из них недействителен, вы должны перепроверить f каждый раз по определению, а если второй не удастся, можно использовать несколько более надежные подходы.

Вы можете найти много реализаций memoization net, и я думаю, что в новых версиях python есть что-то, что включено в них.

На боковой ноте никогда не используйте маленький L в качестве имени переменной, это плохая привычка, поскольку ее можно смутить с i или 1 на некоторых терминалах.

EDIT:

, как прокомментировано, возможным решением с использованием генераторов (чтобы избежать создания бесполезных дублированных времен) было бы это выражение:

[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]

Вам нужно весить свой выбор, учитывая вычислительную стоимость f, количество дубликатов в исходном списке и память при вашем расположении. Воспоминание делает компромисс между космическими скоростями, что означает, что он сохраняет следы каждого результата, сохраняя его, поэтому, если у вас есть огромные списки, это может стать дорогостоящим на фронте памяти.

12
ответ дан EnricoGiampieri 18 August 2018 в 10:06
поделиться
  • 1
    В значительной степени мой ответ, только вы можете просто использовать декоратор @memoize. – Inbar Rose 4 April 2013 в 14:41
  • 2
    это грубая реализация декоратора memoization для цели объяснения. о выступлениях, это худшее из другого решения, только если каждый х отличается друг от друга. если даже один из них совпадает с другим, он просто страдает от накладных расходов словаря, который является маргинальным, если f определен как дорогостоящая функция. это означает, что даже с одним повторением он получит много в плане сокращения вызовов функций. это не важно, как другие решения, но это более надежный ... – EnricoGiampieri 4 April 2013 в 14:43
  • 3
    Я отредактировал свой комментарий для лучшего объяснения. Мне лично не нравятся воспоминания, но в таких проблемах я думаю, что это более надежное решение, и я не использовал его, как серебряную пулю, не задумываясь. – EnricoGiampieri 4 April 2013 в 14:48
  • 4
    Это действительно полезно? Ваша функция во всем мире изменяет storage, что означает, что вам нужно сбросить storage, прежде чем вы вызовете это на новом наборе данных. Все, чтобы вы могли легко использовать понимание списка. ИМХО, этого не стоит. Просто используйте цикл. – mgilson 4 April 2013 в 15:05
  • 5
    как и раньше, если f детерминирован, это будет еще лучше, так как любое значение во второй итерации, которое было замечено ранее, не будет нуждаться во второй оценке. На самом деле, чаще всего вам нужно выполнить цикл, тем более полезной является memoization. У каждого tecnique есть хорошая и плохая сторона. – EnricoGiampieri 4 April 2013 в 15:17

Как показали предыдущие ответы, вы можете использовать двойное понимание или использовать memoization. Для проблем с разумным размером это вопрос вкуса (и я согласен с тем, что memoization выглядит более чистым, поскольку он скрывает оптимизацию). Но если вы изучаете очень большой список, есть огромная разница: Memoization будет хранить каждое вычисленное значение и может быстро сдуть вашу память. Двойное понимание с генератором (круглые парнеты, а не квадратные скобки) сохраняет только то, что вы хотите сохранить.

Чтобы перейти к вашей реальной проблеме:

[g(x, f(x)) for x in series if f(x)]

Чтобы вычислить окончательный вам нужны оба x и f(x). Нет проблем, передайте их следующим образом:

[g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]

Опять же: это должно быть использование генератора (раунд parens), а не понимание списка (квадратные скобки). В противном случае вы выведете весь список, прежде чем приступать к фильтрации результатов. Это версия для понимания списка:

[g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
8
ответ дан alexis 18 August 2018 в 10:06
поделиться
  • 1
    +1 для рассмотрения использования памяти. – Stefan 4 April 2013 в 17:17
  • 2
    помимо читаемости, будет [g(x, y) for x in series for y in [f(x)] if y] разным в занятии памяти? когда запомненный список будет очищен (зависит от того, где определена запоминаемая функция)? – user110954 14 September 2017 в 17:41
  • 3
    Вычисленный конечный список совпадает, но вам потребуется больше времени, чтобы добраться туда (и сбор мусора также требует времени). – alexis 15 September 2017 в 14:08
  • 4
    PS. Но не верьте мне на слово, попробуйте и время! – alexis 17 September 2017 в 11:42

Как насчет определения:

def truths(L):
    """Return the elements of L that test true"""
    return [x for x in L if x]

Так, например,

> [wife.children for wife in henry8.wives]
[[Mary1], [Elizabeth1], [Edward6], [], [], []]

> truths(wife.children for wife in henry8.wives) 
[[Mary1], [Elizabeth1], [Edward6]]
-1
ответ дан Colonel Panic 18 August 2018 в 10:06
поделиться

Вот мое решение:

filter(None, [f(x) for x in l])
1
ответ дан Eastsun 18 August 2018 в 10:06
поделиться
  • 1
    Как и в случае с другими, вы можете использовать генератор внутри filter - И я обычно предпочитаю filter(bool,...) - filter(None,...). Делают то же самое, но я считаю, что первое было более явным. например Я думаю, что это будет лучше, чем filter(bool,(f(x) for x in l)) – mgilson 4 April 2013 в 15:20

Вы можете использовать memoization . Это метод, который используется для того, чтобы избежать выполнения одного и того же вычисления дважды, сохраняя где-то результат для каждого рассчитанного значения. Я видел, что уже есть ответ, который использует memoization, но я хотел бы предложить общую реализацию, используя декораторы python:

def memoize(func):
    def wrapper(*args):
        if args in wrapper.d:
            return wrapper.d[args]
        ret_val = func(*args)
        wrapper.d[args] = ret_val
        return ret_val
    wrapper.d = {}
    return wrapper

@memoize
def f(x):
...

Теперь f - это сама memoized версия. С этой реализацией вы можете memoize любую функцию, используя декоратор @memoize.

4
ответ дан ehudt 18 August 2018 в 10:06
поделиться

Было много ответов на memoizing. Стандартная библиотека Python 3 теперь имеет lru_cache, который является последним использованным кэшем . Таким образом, вы можете:

from functools import lru_cache

@lru_cache()
def f(x):
    # function body here

Таким образом, ваша функция будет вызываться только один раз. Вы также можете указать размер lru_cache, по умолчанию это 128. Проблема с декораторами memoize, показанная выше, заключается в том, что размер списков может значительно увеличиться.

4
ответ дан Games Brainiac 18 August 2018 в 10:06
поделиться
  • 1
    Это выглядит полезным. Если я правильно понимаю, как работает понимание списка, я мог бы использовать размер lru_cache 1, так как я только хочу повторно использовать последнее используемое значение? – Stefan 10 September 2014 в 12:26
  • 2
    Yup, размер 1 будет работать нормально. – Games Brainiac 11 September 2014 в 07:13

Использовать map() !!

comp = [x for x in map(f, l) if x]

f - это функция f(X), l - список

map() вернет результат f(x) для каждого x в списке.

2
ответ дан Guillaume Lebreton 18 August 2018 в 10:06
поделиться

Вы должны использовать декоратор memoize. Вот интересная ссылка .


Использование memoization из ссылки и вашего «кода»:

def memoize(f):
    """ Memoization decorator for functions taking one or more arguments. """
    class memodict(dict):
        def __init__(self, f):
            self.f = f
        def __call__(self, *args):
            return self[args]
        def __missing__(self, key):
            ret = self[key] = self.f(*key)
            return ret
    return memodict(f)

@memoize
def f(x):
    # your code

[f(x) for x in l if f(x)]
12
ответ дан Inbar Rose 18 August 2018 в 10:06
поделиться
  • 1
    Это моя любимая опция memoization. У вас все еще есть доступ к функции через f.f(...), он сохраняет состояние на основе каждой функции (в отличие от глобальной), он использует dict.__missing__, что так полезно. У тебя есть моя возвышенность. – mgilson 4 April 2013 в 15:23

Нет. Для этого нет ( clean ). Нет ничего плохого в хорошем старомодном цикле:

output = []
for x in l:
    result = f(x)
    if result: 
        output.append(result)

Если вы считаете, что трудно читать, вы всегда можете обернуть его в функцию.

35
ответ дан mgilson 18 August 2018 в 10:06
поделиться
[y for y in [f(x) for x in l] if y]

Для вашей обновленной проблемы это может быть полезно:

[g(x,y) for x in l for y in [f(x)] if y]
10
ответ дан Vaughn Cato 18 August 2018 в 10:06
поделиться
  • 1
    Достаточно справедливо ... Вы выигрываете, там есть способ сделать это. Я бы все еще использовал цикл: -P – mgilson 4 April 2013 в 14:35
  • 2
    Вы даже можете сделать внутреннее понимание генератором для сохранения при создании дубликатов списков. – 9000 4 April 2013 в 14:35
  • 3
    вау на 10 секунд быстрее меня;) – RobertT 4 April 2013 в 14:36
  • 4
    I мог бы использовать это, если бы я угадал внутренний генератор во временную переменную раньше времени ... – mgilson 4 April 2013 в 14:37
  • 5
    Этот выглядит , как решение @ Mahdi, но он будет строить весь список, прежде чем он его фильтрует. Махди лучше: он будет создавать генератор и накачивать его по одному значению за раз. – alexis 4 April 2013 в 15:03
35
ответ дан mgilson 6 September 2018 в 22:33
поделиться
35
ответ дан mgilson 30 October 2018 в 03:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: