Стилизацию многострочных условий в операторах if?

Иногда я разбиваю длинные условия в if на несколько строк. Самый очевидный способ сделать это:

  if (cond1 == 'val1' and cond2 == 'val2' and
      cond3 == 'val3' and cond4 == 'val4'):
      do_something

Визуально это не очень привлекательно, потому что действие сочетается с условиями. Однако это естественный способ использования правильного отступа Python из 4 пробелов.

На данный момент я использую:

  if (    cond1 == 'val1' and cond2 == 'val2' and
          cond3 == 'val3' and cond4 == 'val4'):
      do_something

Но это не очень красиво. :-)

Можете порекомендовать альтернативный способ?

Если ваш редактор использует пакет Python pep8, чтобы определить, когда следует предупреждать о нарушениях PEP8, вам придется либо отключить ошибку E125, либо найти решение форматирования, которое удовлетворяет критериям пакета pep8. Пакет pep8Выпуск # 126 предназначен для исправления пакета, чтобы строго следовать спецификации PEP8. Обсуждение проблемы включает некоторые предложения по стилю, которые также можно найти здесь.

akaihola 22.10.2013 10:22

Обратите внимание, что в первом примере pep8 выдаст «E129 строка с визуальным отступом с тем же отступом, что и следующая логическая строка».

Taylor Edmiston 06.09.2016 22:24

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

Z4-tier 30.04.2020 02:42

@ Z4-tier: да, это основано на мнении. Но об этом спросили 12 лет назад. Тогда SO было другим местом, добрее. В последнее время накапливаются отрицательные голоса, так как стандарты SO изменились. Тем не менее, после просмотра более 1 миллиона раз, я надеюсь, что он приносит миру больше пользы, чем вреда. Я, конечно, могу видеть, как люди задаются этим же вопросом сегодня, ищут его в Google, заходят в эту дискуссию и находят полезным откалибровать свое мышление. На выбор предлагается несколько ответов, получивших большое количество голосов.

Eli Bendersky 30.04.2020 15:44

@EliBendersky полностью согласен. Это похоже на продолжающийся кризис идентичности в SO: хотя он явно не соответствует «правилам» (количество правильных ответов - свидетельство этого), столь же очевидно, что он добавляет ценности. При прочих равных, я бы предпочел работать с кем-то, кто выработал четкие и аргументированные взгляды на стиль кодирования, даже если их взгляды отличаются от моих.

Z4-tier 05.05.2020 18:52
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
736
5
1 320 378
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

Это не так сильно улучшается, но ...

allCondsAreOK = (cond1 == 'val1' and cond2 == 'val2' and
                 cond3 == 'val3' and cond4 == 'val4')

if allCondsAreOK:
   do_something

Интересная альтернатива. Но две лишние строчки :-)

Eli Bendersky 08.10.2008 10:37

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

Mez 08.10.2008 10:37

Если вы собираетесь использовать переменную только один раз, я бы предпочел вообще не использовать переменную.

Brian 08.10.2008 11:48

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

Mark Baker 08.10.2008 13:00

@MarkBaker Раньше я соглашался с тем, что вы написали, пока не прочитал Мартин Фаулер «Рефакторинг». Он приводит отличный аргумент в пользу того, что такие промежуточные переменные приносят больше вреда, чем пользы. Они препятствуют последующему рефакторингу. Отказ от них приводит к более функциональному стилю программирования, который хорошо поддается рефакторингу. Это меня удивило, но я считаю, что он прав, и с тех пор стараюсь исключить ненужные промежуточные звенья, подобные этому, из моего кода - даже если они используются более одного раза.

Jonathan Hartley 09.10.2012 16:32

Кроме того, я воспользуюсь этой возможностью, чтобы заявить, что использование медленного языка программирования (например, Python) не вызывает проблем с производительностью приложений. За шесть лет, прошедших с тех пор, как я перешел с C++ и C# на Python, я не обнаружил, что мой код больше страдает от проблем с производительностью. Когда это происходит, оказывается, что это связано с неправильным выбором алгоритма или вводом-выводом. Лишь однажды за все это время я обнаружил, что производительность Python каким-либо образом невысока - при морфинге сеток вершин в каждом кадре перед их отправкой в ​​OpenGL. В этом случае правильные и очевидные решения (используйте Numpy или шейдеры) исправили его.

Jonathan Hartley 09.10.2012 16:37

Это наиболее выразительно и естественно раскладывается. Обычно условным выражениям присваивается красивое осмысленное имя, которое поможет быстро понять, когда вы вернетесь к коду через год. Я много занимаюсь рефакторингом, и очень полезно иметь сложные условные выражения, значимо «помеченные» именем переменной.

Paul Whipp 27.10.2012 10:26

Хорошо, но почему именно CamelCase ?! :)

Leonid Shvechikov 17.03.2013 21:37

@LeonidShvechikov Почему не CamelCase?

CatShoes 11.06.2013 16:07
Ответ принят как подходящий

Вам не нужно использовать 4 пробела во второй условной строке. Может быть, использовать:

if (cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'):
    do_something

Кроме того, не забывайте, что пробелы более гибкие, чем вы думаете:

if (   
       cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'
   ):
    do_something
if    (cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'):
    do_something

Хотя оба они довольно уродливы.

Может быть, скобки потеряны (хотя Гид по стилю этого не одобряет)?

if cond1 == 'val1' and cond2 == 'val2' and \
   cond3 == 'val3' and cond4 == 'val4':
    do_something

По крайней мере, это дает вам некоторую дифференциацию.

Или даже:

if cond1 == 'val1' and cond2 == 'val2' and \
                       cond3 == 'val3' and \
                       cond4 == 'val4':
    do_something

Я думаю, что предпочитаю:

if cond1 == 'val1' and \
   cond2 == 'val2' and \
   cond3 == 'val3' and \
   cond4 == 'val4':
    do_something

Вот Гид по стилю, который (с 2010 года) рекомендует использовать скобки.

Обратите внимание, что завершающие \ solutions не рекомендуются PEP 8. Одна из причин заключается в том, что если пробел добавлен по ошибке после \, он может не отображаться в вашем редакторе, и код станет синтаксически некорректным.

Eric O Lebigot 14.01.2011 13:26

Это неправильно, в руководстве по стилю сказано: «Длинные строки можно разбить на несколько строк, заключив выражения в круглые скобки. Их следует использовать вместо использования обратной косой черты для продолжения строки». Вы можете увидеть это здесь: python.org/dev/peps/pep-0008/#maximum-line-length

joshcartme 22.01.2013 01:54

@joshcartme PEP изменен на hg.python.org/peps/rev/7a48207aaab6, чтобы явно препятствовать обратному слешу. Я обновлю ответ.

Harley Holcombe 22.01.2013 04:36

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

joshcartme 23.01.2013 00:03

Я также предпочитаю ваш последний пример, за исключением того, что я помещаю 'и / или' в начале строк. Так как в стране Python пустое пространство - это значит, что мы не используем скобки для блоков кода, обертывание кода в скобки повторяется снова и просто выглядит уродливо IMO; PEP 8 - это всего лишь рекомендация, что означает, что вам не нужно ей следовать.

Daniel Sokolowski 18.07.2013 21:40

Что-то не так, если мы совместим and с if в последнем примере?

aandis 22.01.2016 13:21
PEP 8 now discourages breaking after the and and if as well.
virtualxtc 11.09.2018 04:46

В качестве примечания к стилю я считаю, что условные выражения более читабельны, когда and и or продолжают строку, а не предыдущую строку.

Boris Yakubchik 14.09.2018 21:35

Я предлагаю переместить ключевое слово and во вторую строку и сделать отступ для всех строк, содержащих условия, двумя пробелами вместо четырех:

if (cond1 == 'val1' and cond2 == 'val2'
  and cond3 == 'val3' and cond4 == 'val4'):
    do_something

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

Я где-то читал в Grows или Dijkstra, что размещение логического оператора в начале строки, делая его более заметным, помогло. И я занимаюсь этим с 90-х годов. И это помогает.

S.Lott 08.10.2008 14:23

Обратите внимание, что Руководство по стилю рекомендует ставить условное выражение в конце строки.

Harley Holcombe 10.10.2008 02:55

Это правда, хотя я никогда с этим не соглашался. В конце концов, это всего лишь руководство.

DzinX 10.10.2008 12:56

PEP8 больше не рекомендует помещает условное выражение в конец строки.

Soren Bjornstad 11.11.2016 06:16

Я предпочитаю этот стиль, когда у меня ужасно большое if-условие:

if (
    expr1
    and (expr2 or expr3)
    and hasattr(thingy1, '__eq__')
    or status= = "HappyTimes"
):
    do_stuff()
else:
    do_other_stuff()

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

mightypile 23.12.2013 03:15

Обратите внимание, что наличие операторов and и or в начале строки нарушает PEP 0008, который указывает «Предпочтительное место для обрыва бинарного оператора - после оператора, а не перед ним».. Мне нравится иметь закрывающую скобку и двоеточие в отдельной строке, чтобы отделить условие if от тела (и вполне возможно сделать это, сохранив логические операторы в конце строки для соответствия PEP-0008).

Mark Amery 05.07.2015 17:24

по состоянию на 2016 год: For decades the recommended style was to break after binary operators. But this can hurt readability in two ways ... In Python code, it is permissible to break before or after a binary operator, as long as the convention is consistent locally. For new code Knuth's style is suggested. (стиль Кнута - начало строки с оператора).

cowbert 11.01.2018 23:14

Я прибегал к следующему в вырожденном случае, когда это просто И или ИЛИ.

if all( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

if any( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

Он сбривает несколько персонажей и дает понять, что в этом состоянии нет никакой тонкости.

Это интересный подход. Однако не решает проблему длительных условий

Eli Bendersky 08.10.2008 15:04

Ничего страшного, если тебя не волнует короткое замыкание.

Constantin 10.10.2008 03:39

@ Константин: Дело было расширяемым, а не быстрым. Для быстрого это не идеально.

S.Lott 10.10.2008 04:02

короткое замыкание не всегда происходит быстро. Хотя это не очень хорошая практика кодирования, у вас может быть существующий код вроде этого: if destroy_world and DestroyTheWorld() == world_is_destroyed: .... Отлично, теперь ты просто случайно разрушил мир. КАК ТЫ МОГ?

Aaron 29.01.2009 00:43

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

Przemek D 29.05.2018 09:09

Это выражение не ленивое. Так что это не эквивалентно, если за некоторым условием защиты, возможно, не удастся.

eugene-bright 06.08.2018 01:35

«all» и «any» подходят для множества условий одного и того же типа case. НО они всегда оценивают все условия. Как показано в этом примере:

def c1():
    print " Executed c1"
    return False
def c2():
    print " Executed c2"
    return False


print "simple and (aborts early!)"
if c1() and c2():
    pass

print

print "all (executes all :( )"
if all((c1(),c2())):
    pass

print

Неверно! Они делают это только потому, что это делают ты. Попробуйте all (f () вместо f в [c1, c2]).

habnabit 27.01.2009 08:39

Я думаю, он использовал функции только в качестве примера, потому что он может легко заставить их что-то напечатать. Если мы рассматриваем серию произвольных выражений, представленных в списке для all(), тогда, если вы не собираетесь обернуть их каждое лямбда-выражением и использовать свой трюк с f(), все они будут оценены. Другими словами, Аарон: Я думаю, Андерс пытался говорить об условиях в целом, используя вызываемые объекты в качестве конкретного примера; но ваше возражение относится только к функциям.

Brandon Rhodes 16.01.2011 09:04

Кто-то должен выступать за использование вертикальных пробелов здесь! :)

if (     cond1 == val1
     and cond2 == val2
     and cond3 == val3
   ):
    do_stuff()

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

if (    cond1 == val1
     or 
        (     cond2_1 == val2_1
          and cond2_2 >= val2_2
          and cond2_3 != bad2_3
        )
   ):
    do_more_stuff()

Да, для ясности мы немного торгуем вертикальной недвижимостью. ИМО оно того стоит.

Это не кажется ни красивым, ни совместимым с PEP8. PEP8 говорит, что предпочтительным местом для прерывания бинарного оператора (например, and, а также or) является оператор после, а не перед ним.

Chris Medrela 25.10.2013 21:25

@ChristopherMedrela, это объясняет причину этого? Я думаю, что размещение разрыва строки перед логическим оператором намного понятнее

Norill Tempest 09.05.2014 03:35

Я предпочитаю это версии PEP8. Полагаю, есть элемент личного вкуса.

Michael Bylstra 14.08.2014 04:25

Ставить оператора на первое место довольно часто в мире узлов. Причина в том, что мы замечаем и читаем то, что слева, намного быстрее, чем то, что справа - по крайней мере, в западных культурах. Очень актуально в JavaScript, где забытая запятая может вызывать скрытые ошибки.

tomekwi 20.10.2014 12:30

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

Urda 04.03.2015 20:56

@Urda Я не согласен. Размещение бинарных операторов в начале строк, а не в конце IMO делает более ясным, какова цель. Во втором примере выше, я думаю, ясно, что операнды and объединяются вместе перед тем, как or объединяется с первым условием. Но, может быть, я так думаю, потому что мне нравится Lisp ...

jamesdlin 13.08.2015 15:39

Что касается текущей версии PEP8, прерывание до или после бинарного оператора считается приемлемым и перед оператором считается более подходящим для нового кода.

Soren Bjornstad 11.11.2016 06:16

Мне больше всего нравится этот ответ, потому что Дзен PythonReadability counts.Special cases aren't special enough to break the rules.Although practicality beats purity.python.org/dev/peps/pep-0020

JayRizzo 30.05.2018 09:30

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

Вы также можете сделать это со словарем:

>>> x = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> y = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> x == y
True

Этот вариант более сложный, но он может оказаться полезным:

class Klass(object):
    def __init__(self, some_vars):
        #initialize conditions here
    def __nonzero__(self):
        return (self.cond1 == 'val1' and self.cond2 == 'val2' and
                self.cond3 == 'val3' and self.cond4 == 'val4')

foo = Klass()
if foo:
    print "foo is true!"
else:
    print "foo is false!"

Не знаю, работает ли это для вас, но это еще один вариант, который стоит рассмотреть. Вот еще один способ:

class Klass(object):
    def __init__(self):
        #initialize conditions here
    def __eq__(self):
        return (self.cond1 == 'val1' and self.cond2 == 'val2' and
               self.cond3 == 'val3' and self.cond4 == 'val4')

x = Klass(some_values)
y = Klass(some_other_values)
if x == y:
    print 'x == y'
else:
    print 'x!=y'

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

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

Что, если мы только вставим дополнительную пустую строку между условием и телом, а все остальное сделаем каноническим способом?

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'):

    do_something

p.s. Я всегда использую табуляцию, а не пробелы; Я не могу настроить ...

Это было бы очень запутанным, особенно когда тело условия длинное, я думаю.

Eli Bendersky 09.10.2008 09:52

Я согласен с Эли, здесь инкапсуляция и отступы сбивают с толку длинные строки. Более того, новое правило означает, что операторы and и or должны начинаться со следующей строки.

virtualxtc 11.09.2018 04:57

Упакуйте свои условия в список и делайте что-л. нравиться:

if False not in Conditions:
    do_something

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

if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4"):
    do_something

Это хорошо работает для «и» и «или» (важно, чтобы они были первыми во второй строке), но гораздо хуже для других длинных условий. К счастью, первое кажется более распространенным случаем, а второе часто легко переписывается с помощью временной переменной. (Обычно это не сложно, но может быть сложно или гораздо менее очевидно / читабельно сохранить короткое замыкание «и» / или «при перезаписи.)

Поскольку я нашел этот вопрос от сообщение в вашем блоге о C++, я добавлю, что мой стиль C++ идентичен:

if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4") {
    do_something
}

Вот мой очень личный подход: длинные условия (на мой взгляд) - это запах кода, который предлагает рефакторинг в функцию / метод, возвращающую логическое значение. Например:

def is_action__required(...):
    return (cond1 == 'val1' and cond2 == 'val2'
            and cond3 == 'val3' and cond4 == 'val4')

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

С другой стороны, их возмущение в моем эстетическом восприятии является стимулом для рефакторинга.

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

Я удивлен, что не вижу своего предпочтительного решения,

if (cond1 == 'val1' and cond2 == 'val2'
    and cond3 == 'val3' and cond4 == 'val4'):
    do_something

Поскольку and является ключевым словом, он выделяется моим редактором и существенно отличается от do_something под ним.

Но линия продолжения по-прежнему не отличается от следующей логической строки ...

Chris Medrela 25.10.2013 21:28

Обратите внимание, что это нарушение PEP 0008 («Предпочтительное место для обрыва бинарного оператора - после оператора, а не перед ним»). Неважно, заботитесь ли вы, конечно, решать вам.

Mark Amery 05.07.2015 17:20

Кстати, это больше не мое любимое решение. ;)

Marius Gedminas 13.07.2015 08:55

Добавляя к тому, что сказал @krawyoti ... Длинные условия пахнут, потому что их трудно читать и трудно понять. Использование функции или переменной делает код более понятным. В Python я предпочитаю использовать вертикальное пространство, заключать скобки и помещать логические операторы в начало каждой строки, чтобы выражения не выглядели «плавающими».

conditions_met = (
    cond1 == 'val1' 
    and cond2 == 'val2' 
    and cond3 == 'val3' 
    and cond4 == 'val4'
    )
if conditions_met:
    do_something

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

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

Techdragon 05.05.2013 18:42

@Techdragon, если условия должны быть где-то еще, то для их помещения в лямбда-блок потребуется имя лямбда-блока, чтобы на него можно было ссылаться позже в условии if. Если лямбда будет названа, почему это, в конце концов, не обычная функция? Мне лично нравится это сокращенное логическое выражение.

Sri Kadimisetty 01.09.2013 13:58

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

Techdragon 13.09.2013 05:24

Это круто. К сожалению, если я включу Path(input).is_dir() или Path(input).is_file() в предложение переменных, я получу TypeError: 'bool' object is not callable.

howdoicode 30.09.2020 18:57

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

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'):
        do_something

@qarma, не хотите ли вы расшириться? Это, безусловно, лучше, чем использовать символы продолжения строки, которые не рекомендуются PEP 8.

xorsyst 13.03.2012 18:27

На самом деле это действительный случай для продолжения строки. IMPO Круглые скобки обозначают кортеж или вызов функции. Использование OP очень похоже на C, я предпочитаю синтаксис Python, когда это возможно. Однако я признаю, что \ не приветствуется всеми.

Dima Tisnek 14.03.2012 15:13

  if cond1 == 'val1' and \
     cond2 == 'val2' and \
     cond3 == 'val3' and \
     cond4 == 'val4':
      do_something

или, если это яснее:

  if cond1 == 'val1'\
     and cond2 == 'val2'\
     and cond3 == 'val3'\
     and cond4 == 'val4':
      do_something

Нет причин, по которым в этом случае отступ должен быть кратен 4, например см. «Выровнено по открывающему разделителю»:

http://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=Indentation#Indentation.

Руководство Google также предоставляет пример сложного состояния, который соответствует «наиболее очевидному способу сделать это», как указано в OP. Хотя в руководстве нет явной поддержки такого форматирования длинных «если».

Anton Strogonoff 08.04.2012 14:05

Лично мне нравится придавать смысл длинным операторам if. Мне пришлось бы искать подходящий пример по коду, но вот первый пример, который приходит на ум: допустим, я случайно столкнулся с какой-то причудливой логикой, когда я хочу отобразить определенную страницу в зависимости от многих переменных.

Английский: «Если вошедший в систему пользователь НЕ является преподавателем-администратором, а является обычным преподавателем, а не самим учеником ...»

if not user.isAdmin() and user.isTeacher() and not user.isStudent():
    doSomething()

Конечно, это может выглядеть нормально, но читать эти операторы if - это большая работа. Как насчет того, чтобы присвоить логику ярлыку, который имеет смысл. «Метка» - это на самом деле имя переменной:

displayTeacherPanel = not user.isAdmin() and user.isTeacher() and not user.isStudent()
if displayTeacherPanel:
    showTeacherPanel()

Это может показаться глупым, но у вас может быть еще одно условие, при котором вы хотите отображать другой элемент ТОЛЬКО тогда и только тогда, когда вы показываете панель учителя ИЛИ если пользователь по умолчанию имеет доступ к этой другой конкретной панели:

if displayTeacherPanel or user.canSeeSpecialPanel():
    showSpecialPanel()

Попробуйте написать приведенное выше условие без использования переменных для хранения и маркировки вашей логики, и вы не только получите очень беспорядочный, трудно читаемый логический оператор, но и просто повторите себя. Хотя есть разумные исключения, помните: не повторяйтесь (СУХОЙ).

Что я обычно делаю:

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'
   ):
    do_something

таким образом закрывающая скобка и двоеточие визуально обозначают конец нашего условия.

Почти правильно; PEP 8 теперь рекомендует взломать до and или or.

virtualxtc 11.09.2018 04:52

Вот еще один подход:

cond_list = ['cond1 == "val1"','cond2= = "val2"','cond3= = "val3"','cond4= = "val4"']
if all([eval(i) for i in cond_list]):
 do something

Это также упрощает добавление другого условия без изменения оператора if, просто добавляя другое условие в список:

cond_list.append('cond5= = "val5"')

Вот что я делаю. Помните, что «all» и «any» допускают итерацию, поэтому я просто помещаю длинное условие в список и позволяю «all» выполнять работу.

condition = [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4']

if all(condition):
   do_something

Плохой случай, если в cond4 вы хотите проверить функцию или если cond2 является свойством cond1. Пример условий: object is not None and object.param == 5 and object.is_running()

Oleg 04.03.2021 11:29

Простой и понятный, также проходит проверки pep8:

if (
    cond1 and
    cond2
):
    print("Hello World!")

В последнее время я предпочитаю функции all и any, так как я редко смешиваю сравнения And и Or, это хорошо работает и имеет дополнительное преимущество в виде раннего отказа с пониманием генераторов:

if all([
    cond1,
    cond2,
]):
    print("Hello World!")

Просто не забудьте передать одну итерацию! Передача N-аргументов неверна.

Примечание: any похож на многие сравнения or, all похож на многие сравнения and.


Это прекрасно сочетается с пониманием генератора, например:

# Check if every string in a list contains a substring:
my_list = [
    'a substring is like a string', 
    'another substring'
]

if all('substring' in item for item in my_list):
   print("Hello World!")

# or

if all(
    'substring' in item
    for item in my_list
):
    print("Hello World!")

Подробнее на: генератор понимания

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

ThorSummoner 02.12.2016 22:38

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

if bool(condition1 and
        condition2 and
        ...
        conditionN):
    foo()
    bar()

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

Достаточно забавно, когда я писал это и думал о «проблеме», мне пришла в голову идея еще один, которая устраняет накладные расходы на вызов функции. Почему бы не указать, что мы собираемся ввести сложное условие, с помощью дополнительных пар круглых скобок? Скажем, еще 2, чтобы дать хороший отступ в 2 пробела для подусловий относительно тела оператора if. Пример:

if (((foo and
      bar and
      frob and
      ninja_bear))):
    do_stuff()

Мне это нравится, потому что, когда вы смотрите на это, в вашей голове сразу же звонит колокольчик: "Эй, здесь происходит сложная вещь!". Да, я знаю, что скобки не улучшают читаемость, но эти условия должны появляться достаточно редко, и когда они действительно появляются, вам все равно придется остановиться и внимательно прочитать их (потому что они сложный).

Во всяком случае, еще два предложения, которых я здесь не видел. Надеюсь, это кому-то поможет :)

Обычно я использую:

if ((cond1 == 'val1' and cond2 == 'val2' and
     cond3 == 'val3' and cond4 == 'val4')):
    do_something()

Похоже, стоит процитировать PEP 0008 (официальное руководство по стилю Python), поскольку в нем довольно подробно комментируется эта проблема:

When the conditional part of an if -statement is long enough to require that it be written across multiple lines, it's worth noting that the combination of a two character keyword (i.e. if ), plus a single space, plus an opening parenthesis creates a natural 4-space indent for the subsequent lines of the multiline conditional. This can produce a visual conflict with the indented suite of code nested inside the if -statement, which would also naturally be indented to 4 spaces. This PEP takes no explicit position on how (or whether) to further visually distinguish such conditional lines from the nested suite inside the if -statement. Acceptable options in this situation include, but are not limited to:

# No extra indentation.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# Add some extra indentation on the conditional continuation line.
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

Обратите внимание на «не ограничено» в приведенной выше цитате; Помимо подходов, предложенных в руководстве по стилю, приемлемы и некоторые из подходов, предложенных в других ответах на этот вопрос.

+1 для PEP8. Этот должен следует принять, поскольку это (практически говоря) официальное руководство по стилю Python.

Michael 14.06.2016 17:29

Также стоит подчеркнуть, что PEP8 явно заявляет о своей позиции как Этот PEP не занимает явной позиции относительно того, как (и нужно ли) дополнительно визуально отличать такие условные строки от вложенного набора внутри if -statement. Допустимые варианты в этой ситуации включают, но не ограничиваются: ... (вырезано) Итак, хватит спорить, выбирайте то, что вам нравится!

RayLuo 14.09.2016 05:22

если наше условие if & an else должно выполнять внутри себя несколько операторов, чем мы можем написать, как показано ниже. Каждый раз, когда у нас есть пример if else с одним выражением внутри него.

Спасибо, это сработало для меня.

#!/usr/bin/python
import sys
numberOfArgument =len(sys.argv)
weblogic_username =''
weblogic_password = ''
weblogic_admin_server_host =''
weblogic_admin_server_port =''


if numberOfArgument == 5:
        weblogic_username = sys.argv[1]
        weblogic_password = sys.argv[2]
        weblogic_admin_server_host =sys.argv[3]
        weblogic_admin_server_port=sys.argv[4]
elif numberOfArgument <5:
        print " weblogic UserName, weblogic Password and weblogic host details are Mandatory like, defalutUser, passwordForDefaultUser, t3s://server.domainname:7001 ."
        weblogic_username = raw_input("Enter Weblogic user Name")
        weblogic_password = raw_input('Enter Weblogic user Password')
        weblogic_admin_server_host = raw_input('Enter Weblogic admin host ')
        weblogic_admin_server_port = raw_input('Enter Weblogic admin port')
#enfelif
#endIf

Вы можете разделить его на две строки

total = cond1 == 'val' and cond2 == 'val2' and cond3 == 'val3' and cond4 == val4
if total:
    do_something()

Или даже добавляйте по одному условию за раз. Таким образом, по крайней мере, он отделяет беспорядок от if.

Я знаю, что эта ветка устарела, но у меня есть код Python 2.7, а PyCharm (4.5) все еще жалуется на этот случай:

if foo is not None:
    if (cond1 == 'val1' and cond2 == 'val2' and
        cond3 == 'val3' and cond4 == 'val4'):
            # some comment about do_something
            do_something

Даже с предупреждением PEP8 «строка с визуальным отступом и тем же отступом, что и следующая логическая строка», фактический код полностью в порядке? Это не «чрезмерный отступ»?

... бывают случаи, когда мне хотелось бы, чтобы Python укусил пулю и просто оставил фигурные скобки. Интересно, сколько ошибок было случайно введено за эти годы из-за случайного неправильного отступа ...

Все респонденты, которые также предоставляют несколько условий для оператора if, столь же уродливы, как и представленная проблема. Вы не решите эту проблему, сделав то же самое ...

Даже ответ PEP 0008 отталкивает.

Вот гораздо более читаемый подход

condition = random.randint(0, 100) # to demonstrate
anti_conditions = [42, 67, 12]
if condition not in anti_conditions:
    pass

Хотите, чтобы я съел свои слова? Убедите меня, что вам нужны несколько условий, и я буквально распечатаю это и съем для вашего развлечения.

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

dim_user 06.03.2018 23:09

@SaulCruz на самом деле нет Мало того, что переменную условия не нужно повторять, вы также экономите на множестве дубликатов проверки каждого значения, это просто помещает только значения в массив и позволяет движку выполнять свою (оптимизированную) работу в проверяю состояние для вас

Stoff 12.03.2018 16:19

@Stoff Спасибо, что удалили мой комментарий. Я хотел указать, что ваш подход не отвечает на вопрос ОП. Предоставленный вами код нельзя применить к коду, указанному в вопросе. Если вы думаете иначе, вам следует добавить код OP, отформатированный в соответствии с вашим подходом, чтобы доказать свою точку зрения.

Jeyekomon 06.06.2018 12:11

Это не общепринятый ответ, однако это явно альтернативный подход (другие согласны). ТАК поощрял альтернативные ответы, так что же именно за аргумент? Четко сформулируйте свой вопрос, возможно, подумайте о том, чтобы открыть свой вопрос, если вам требуется должное внимание. P.s. Я не модификатор SO, я не могу удалять комментарии

Stoff 06.06.2018 12:14

Я думаю, что решение @zkanda было бы хорошим с небольшим поворотом. Если бы у вас были свои условия и значения в их собственных списках, вы могли бы использовать понимание списка для сравнения, что сделало бы вещи немного более общими для добавления пар условие / значение.

conditions = [1, 2, 3, 4]
values = [1, 2, 3, 4]
if all([c==v for c, v in zip(conditions, values)]):
    # do something

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

if (condition1==value1) and (condition2==value2) and \
   (condition3==value3) and (condition4==value4):

И просто выбросить другое решение с Оператор iand:

proceed = True
for c, v in zip(conditions, values):
    proceed &= c==v

if proceed:
    # do something

Ради интереса: all(map(eq, have, expected)). (с from operator import eq)

Gabriel Garcia 05.09.2017 21:23

Простите мою глупость, но бывает, что я не так хорошо разбираюсь в #Python, как кто-либо из вас здесь, но бывает, что я нашел нечто подобное при написании сценариев моих собственных объектов в моделировании 3D BIM, поэтому я адаптирую свой алгоритм к что из питона.

Проблема, которую я здесь обнаружил, двусторонняя:

  1. Мои ценности кажутся чужеземными для того, кто может попытаться расшифровать сценарий.
  2. Обслуживание кода будет дорого стоить, если эти значения будут изменены (наиболее вероятно) или если должны быть добавлены новые условия (нарушенная схема).

Чтобы обойти все эти проблемы, ваш скрипт должен выглядеть так:

param_Val01 = Value 01   #give a meaningful name for param_Val(i) preferable an integer
param_Val02 = Value 02
param_Val03 = Value 03
param_Val04 = Value 04   # and ... etc

conditions = 0           # this is a value placeholder

########
Add script that if true will make:

conditions = conditions + param_Val01   #value of placeholder is updated
########

### repeat as needed


if conditions = param_Val01 + param_Val02 + param_Val03 + param_Val04:
    do something

Плюсы этого метода:

  1. Скрипт читабельный.

  2. Скрипт легко поддерживать.

  3. Условия - это операция сравнения 1 с суммой значений, которая представляет желаемые условия.
  4. Нет необходимости в многоуровневых условиях

Надеюсь, это поможет вам всем

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