Как выразить условное выполнение внутри лямбда-выражений Python?

Что я узнал:

В разделе Погружение в Python я читал об особой природе операторов and и or и о том, как можно использовать упрощенную оценку логических операторов для более краткого выражения условных выражений с помощью и/или трюк, который очень похож на тернарный оператор в C.

С:

result = condition ? a : b

Питон:

result = condition and a or b

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

Начиная с Python 2.5, inline-if, кажется, пришел на помощь как более читаемый синтаксис для трюка and/or:

result = a if condition else b

Итак, я предполагаю, что это питоническая замена менее читаемой и-или-конструкции. Даже если я хочу вложить несколько условий, это все равно выглядит довольно исчерпывающе:

result = a if condition1 else b if condition2 else c

Но в мире неопределенности я часто пишу такой код для доступа к a.b.c :

result = a and hasattr(a, 'b') and hasattr(a.b, 'c') and a.b.c or None 

Таким образом, с помощью inline-if я, вероятно, мог бы избавиться от некоторых и и или, в результате чего получился вполне читаемый фрагмент кода:

result = a.b.c if hasattr(a, 'b') and hasattr(a.b, 'c') else None

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

result = (a, b)[condition] 

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

Что я хотел бы знать:

Теперь мне интересно, считается ли предпочтительным / более питоническим использовать встроенный-if как можно больше, если нисходящая совместимость не вызывает беспокойства, или это всего лишь вопрос вкуса и насколько человек чувствует себя как дома в мире коротких- оценка цепи?

Обновлять

Я только что понял, что inline-if - это больше, чем синтаксический сахар для трюка и-или, поскольку он не подведет, когда a будет ложным в логическом контексте. Так что это, вероятно, более отказоустойчиво.


person codecraft    schedule 28.02.2011    source источник
comment
На мой взгляд, большая часть того, что является pythonic, — это удобочитаемость, которую легче всего читать, не задумываясь о том, что делает строка? Имея это в виду, я бы предположил, что inline-if является наиболее питоническим, поскольку его можно читать почти как английский.   -  person Stephen Paulger    schedule 28.02.2011


Ответы (4)


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

Конечно, переломный момент для всех будет разным, но если вы обнаружите, что пишете:

return a.b.c if hasattr(a, 'b') and hasattr(a.b, 'c') else None

слишком много, подумайте о том, чтобы сделать это вместо этого:

try:
     return a.b.c
except AttributeError:
     return None
person Duncan    schedule 28.02.2011
comment
это имеет большой смысл. Я часто ловлю себя на том, что пытаюсь максимально увеличить количество кода, умещающегося в строке, чтобы я мог просматривать больше кода без прокрутки... - person codecraft; 28.02.2011
comment
На самом деле мне нравится решение try/except — для него нужно 4 строки, но легко понять, что происходит, и вам не нужно избыточно вводить имена атрибутов. Но имейте в виду, что если b или c являются свойствами с нетривиальными методами получения, конструкция try/except может перехватывать AttributeError, которые на самом деле являются ошибками! - person Oben Sonne; 28.02.2011

Поскольку существует специальная языковая конструкция со встроенными if-else, которая делает то, что вы хотите, и которая была введена для замены уродливых обходных путей, подобных упомянутым вами, рекомендуется использовать ее. Тем более, что такие хаки, как трюк and-or, обычно имеют неожиданные угловые случаи/ошибки.

Например, трюк and-or в этом случае не работает:

a = 0
b = 1
c = True and a or b

c будет 1, чего вы не ожидаете, если ищете семантику if-else.

Так зачем использовать ошибочные обходные пути, когда есть языковая конструкция, которая делает именно то, что вам нужно?

person sth    schedule 28.02.2011
comment
В этом есть смысл. Тоже хороший пример! - person codecraft; 28.02.2011

Я бы предпочел четко указать, что делает мой код. Встроенный, если очень явно выполняет условное присваивание, и читабельность действительно учитывается. Встроенное, если бы не попало в язык, если бы и/или побочные эффекты считались предпочтительными.

Pep для условных выражений более подробно описывает, почему был выбран этот конкретный синтаксис, и конкретно обсуждает хак и/или:

http://www.python.org/dev/peps/pep-0308/

person stderr    schedule 28.02.2011

Это для конкретного случая, о котором вы упомянули, но я думаю, что в большинстве случаев, когда вам нужна цепная логика короткого замыкания, можно справиться с более элегантным решением. Что, очевидно, является делом вкуса, позвольте мне добавить, так что если вы считаете, что вышеприведенное лучше, чем это:

try:
    foo = a.b.c

except AttributeError:
    print "woops"

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

РЕДАКТИРОВАТЬ: кстати, утиный ввод.

person slezica    schedule 28.02.2011
comment
Спасибо! Не могли бы вы порекомендовать некоторые источники кода Python для изучения, чтобы я почувствовал хорошее и элегантное кодирование? Я изучил исходный код django, но обнаружил, что части кода максимально абстрагированы (с использованием метапрограммирования и т. д.), поэтому по коду часто трудно сказать, что происходит. - person codecraft; 28.02.2011
comment
Сама стандартная библиотека (особенно новые части, такие как коллекции, itertools, числа, десятичные числа). - person ncoghlan; 28.02.2011