Самая питоническая форма для отображения серии утверждений?

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

result = [ f(x) for x in list ]

Однако во многих случаях мы хотим выполнить более одного оператора для x, например:

result = [ f(g(h(x))) for x in list ]

Это очень быстро становится неуклюжим и трудным для чтения.

Мое обычное решение - снова развернуть это в цикл for:

result = []
for x in list:
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  result.append(x2)

Одна вещь, которая беспокоит меня без конца, - это необходимость инициализировать пустой список «result». Это мелочь, но меня это огорчает. Мне было интересно, есть ли какие-нибудь альтернативные эквивалентные формы. Одним из способов может быть использование локальной функции (это то, что они называются в Python?)

def operation(x):
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  return x2
result = [ operation(x) for x in list ]

Есть ли какие-либо особые преимущества / недостатки у любой из двух вышеуказанных форм? Или, может быть, есть более элегантный способ?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
251
7

Ответы 7

Да, есть случаи, когда лучше вернуться к циклу for, но чаще я предпочитаю один из следующих подходов:

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

result = [blah(blah(blah(x)))
          for x in list]

Или извлеките (достаточно) логики в другую функцию, как вы упомянули. Но не обязательно местные; Программисты Python предпочитают плоскую структуру вложенной, если вы видите разумный способ выделить функциональность.

Я пришел к Python из мира функционального программирования и разделяю ваше предубеждение.

Если вас беспокоит только последний результат, ваш последний ответ будет лучшим. Для всех, кто смотрит на это, ясно, что вы делаете.

Я часто беру любой код, который начинает усложняться, и перемещаю его в функцию. Это в основном служит комментарием для этого блока кода. (любой сложный код, вероятно, в любом случае нуждается в переписывании, и, вставив его в функцию, я могу вернуться и поработать над ней позже)

def operation(x):
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  return x2
result = [ operation(x) for x in list]

Выбирайте стиль, который больше всего соответствует вашим вкусам. Я бы не беспокоился о производительности; только в том случае, если вы действительно видите какую-то проблему, вы можете попробовать перейти к другому стилю.

Вот еще несколько возможных предложений в дополнение к вашим предложениям:

result = [f(
              g(
                h(x)
                )
              )
            for x in list]

Используйте прогрессивные списки:

result = [h(x) for x in list]
result = [g(x) for x in result]
result = [f(x) for x in result]

Опять же, это только вопрос стиля и вкуса. Выберите тот, который вам больше всего нравится, и придерживайтесь его :-)

Мне нравится ваш второй фрагмент кода, но отступ первого немного жжет мне глаза.

Patrick Harrington 09.12.2008 17:08

Я согласен с вами - первый не такой уж и красивый. По большей части это была своего рода пробная имитация функциональных языков в синтаксисе Python.

rob 09.12.2008 20:38

Я бы заменил постепенное понимание списка конвейером выражений генератора.

ttepasse 02.01.2009 19:40

А как насчет удобочитаемости? Составление списков следует декларативному шаблону, который довольно легко читать и понимать. Генераторы, хотя и более «элегантны», имеют тенденцию скрывать детали реализации далеко от исходного положения кода.

rob 03.01.2009 16:16

Вы можете легко выполнять композицию функций в Python.

Здесь демонстрируется способ создания новой функции, которая представляет собой композицию существующих функций.

>>> def comp( a, b ):
    def compose( args ):
        return a( b( args ) )
    return compose

>>> def times2(x): return x*2

>>> def plus1(x): return x+1

>>> comp( times2, plus1 )(32)
66

Вот более полный рецепт функциональная композиция. Это должно сделать его менее неуклюжим.

Если это то, что вы делаете часто и с несколькими разными операторами, вы можете написать что-нибудь вроде

def seriesoffncs(fncs,x):
    for f in fncs[::-1]:
        x=f(x)
    return x

где fncs - список функций. поэтому seriesoffncs ((f, g, h), x) вернет f (g (h (x))). Таким образом, если позже в коде потребуется отработать h (q (g (f (x)))), вы просто выполните seriesoffncs ((h, q, g, f), x), а не создадите новую функцию операций для каждая комбинация функций.

Что, если f () принимает более одного параметра?

Steve Losh 02.01.2009 17:45

Вариант функции dagw.myopenid.com:

def chained_apply(*args):
    val = args[-1]
    for f in fncs[:-1:-1]:
        val=f(val)
    return val

Вместо seriesoffncs ((h, q, g, f), x) теперь можно вызывать:

result = chained_apply(foo, bar, baz, x)

Насколько мне известно, в Python нет встроенного / собственного синтаксиса для композиции, но вы можете написать свою собственную функцию для компоновки без особых проблем.

def compose(*f):
    return f[0] if len(f) == 1 else lambda *args: f[0](compose(*f[1:])(*args))

def f(x): 
    return 'o ' + str(x)

def g(x): 
    return 'hai ' + str(x)

def h(x, y): 
    return 'there ' + str(x) + str(y) + '\n'

action = compose(f, g, h)
print [action("Test ", item) for item in [1, 2, 3]]

Конечно, сочинять вне понимания не требуется.

print [compose(f, g, h)("Test ", item) for item in [1, 2, 3]]

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

Другие вопросы по теме