Логические операции в Python (например, операторы и/или)

Этот метод ищет первую группу символов слова (то есть: [a-zA-Z0-9_]), возвращая первую совпавшую группу или None в случае неудачи.

def test(str):
    m = re.search(r'(\w+)', str)
    if m:
        return m.group(1)
    return None

Эту же функцию можно переписать так:

def test2(str):
    m = re.search(r'(\w+)', str)
    return m and m.group(1)

Это работает так же и является задокументированным поведением; как на этой странице четко указано:

Выражение x and y сначала оценивает x; если x ложно, возвращается его значение; в противном случае оценивается y и возвращается результирующее значение.

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

Каковы другие варианты использования этого и / или каково обоснование этой довольно неинтуитивной реализации?


person NullUserException    schedule 29.09.2010    source источник
comment
На самом деле это есть во многих языках, это определенно предшествует Python. Он традиционно использовался в качестве замены тернарного условного выражения еще до того, как у нас появились выражения if...else, и его все еще можно использовать как оператор объединения NULL ?? в C#. Лично я бы старался избегать всего, кроме небольших тривиальных применений этого, как из-за потенциальных проблем с «ложными» значениями, так и потому, что «явное лучше, чем неявное». return m and m.group(1) вполне нормально, но если вы пойдете дальше, m is not None and... может быть понятнее.   -  person bobince    schedule 30.09.2010


Ответы (5)


Каковы другие варианты использования этого,

Краткость (и, следовательно, ясность, как только вы к ней привыкнете, так как в конце концов она не нисколько не жертвует читабельностью!-) каждый раз, когда вам нужно что-то проверить и либо использовать это что-то, если оно верно , или другое значение, если это что-то ложное (это для and -- поменяйте местами для or -- и я очень намеренно избегаю фактических ключевых слов или им подобных True и False, так как я я говорю о каждом объекте, а не только bool!-).

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

inverses = [x and 1.0/x for x in values]

на шесть, например:

inverses = []
for x in values:
    if x:
        inverses.append(1.0/x)
    else:
        inverses.append(x)

или более тесные их версии.

и/или каково обоснование этой довольно неинтуитивной реализации?

Далекие от того, чтобы быть "неинтуитивными", новички регулярно сбивались с толку тем фактом, что некоторые языки (такие как стандартный Pascal) не определяли порядок вычисления и природу короткого замыкания and и or; Одно из различий между Turbo Pascal и языковым стандартом, которое в свое время сделало Turbo самым популярным диалектом Pascal всех времен, заключалось именно в том, что Turbo реализовывал and и or так же, как Python позже (а язык C сделал раньше.. .).

person Alex Martelli    schedule 30.09.2010
comment
Разве это не должно быть x and 1.0/x for ...? - person NullUserException; 30.09.2010
comment
@Null, упс, вы правы - видите ли, вы действительно находите конструкцию читаемой, несмотря на мою ужасную опечатку!-) Tx, редактирую, чтобы исправить. - person Alex Martelli; 30.09.2010
comment
Совершенно не по теме; есть !-) смайлик? - person poke; 30.09.2010
comment
@poke: Нет, это артефакт обработки уценки акцентной могилы. - person S.Lott; 30.09.2010
comment
@poke, да, это смайлик, но с повязкой на глазу. - person Geoffrey; 22.10.2010
comment
Неинтуитивной частью является не порядок оценки или короткое замыкание; неинтуитивной частью являются логические операторы, возвращающие небулевы значения. Это имеет гораздо больше смысла, если знать, что в то время, когда в Python были определены and и or, в Python фактически не было выделенного логического типа. - person user2357112 supports Monica; 13.12.2018

Каковы другие варианты использования этого,

No.

каково обоснование этой довольно неинтуитивной реализации?

"неинтуитивный"? Действительно? Я бы не согласился.

Давай подумаем.

"a и b" фальсифицированы, если a ложно. Таким образом, первого ложного значения достаточно, чтобы узнать ответ. Зачем преобразовывать a в другое логическое значение? Это уже ложь. Насколько более ложно False? Одинаково ложно, верно?

Таким образом, значение a, эквивалентное False, достаточно ложно, так что это значение всего выражения. Никакой дальнейшей конвертации или обработки. Готово.

Когда значение a эквивалентно True, тогда значение b — это все, что требуется. Никакой дальнейшей конвертации или обработки. Зачем преобразовывать b в другое логическое значение? Это значение — это все, что нам нужно знать. Если это что-то вроде True, то это достаточно верно. Насколько вернее True?

Зачем создавать ложные дополнительные объекты?

Тот же анализ для или.

Зачем преобразовывать в логические значения? Это уже достаточно верно или достаточно ложно. Насколько больше True он может получить?


Попробуй это.

>>> False and 0
False
>>> True and 0
0
>>> (True and 0) == False
True

Хотя (True and 0) на самом деле равно 0, оно равно False. Это достаточно ложно для всех практических целей.

Если это проблема, то bool(a and b) вызовет явное преобразование.

person S.Lott    schedule 30.09.2010

В основном a and b возвращает операнд, который имеет то же значение истинности, что и все выражение.

Это может показаться немного запутанным, но просто сделайте это в уме: если a равно False, то b больше не имеет значения (потому что False and anything всегда будет False), поэтому он может сразу вернуть a.

Но когда a равно True, тогда имеет значение только b, поэтому он сразу же возвращает b, даже не глядя.

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

person Jochen Ritzel    schedule 29.09.2010
comment
Я знаю об оценке короткого замыкания, меня беспокоит только то, что это выражение имеет совершенно разные типы возвращаемых значений. - person NullUserException; 30.09.2010
comment
@NullUserException: совсем не отличается. Логическое эквивалентное значение такое же. - person S.Lott; 30.09.2010

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

Хотя не все значения являются bools, обратите внимание, что на самом деле все значения являются логическими – они представляют собой истинное значение. (В Python bool фактически является значением, которое только представляет собой истину или ложь.) Число 0 не является логическим, но явно (в Python) имеет логическое значение. Ложь.

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

Может быть, это ретроактивное оправдание; Я не уверен, но в любом случае поведение булевых операторов Python кажется естественным.


Когда его использовать?

В вашем примере test2 кажется мне более понятным. Я могу сказать, что они оба делают одинаково: конструкция в test2 не усложняет понимание. При прочих равных, более лаконичный код в test2 — в незначительной степени — понимается быстрее. Тем не менее, это тривиальная разница, и я не настолько предпочитаю, чтобы прыгать, чтобы переписать что-либо.

Это может быть так же полезно в других отношениях:

a = {
    "b": a and a.val,
    "c": b and b.val2,
    "d": c and c.val3,
}

Это можно было бы переписать по-другому, но это ясно, прямо и лаконично.

Не переусердствуйте; «a() и b() или c()» вместо «a()? b():c()» опасно и сбивает с толку, так как вы получите c(), если b() ЛОЖЬ. Если вы пишете троичный оператор, используйте троичный синтаксис, даже если он ужасно уродлив: b() if a() else c().

person Glenn Maynard    schedule 29.09.2010
comment
Я бы сказал, что test2 затрудняет понимание, если читатель не знаком с этой темой. Тот факт, что тема обсуждается, говорит мне, что явное if/else более понятно. - person Russell Borogove; 30.09.2010
comment
@Russell Borogove: Для любого фрагмента синтаксиса есть кто-то, кто его не понимает, и еще больше людей его обсуждают. Это элементарно в Python (и некоторых других языках; Python не изобретал это), и если вы не пишете обучающий код, предназначенный для наименьшего общего знаменателя начинающих программистов, я думаю, что это нормально. - person Glenn Maynard; 30.09.2010

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

В большинстве языков возвращаемое значение активной функции определяется типом функции. Если только он не был явно перегружен. Пример: ожидается, что функция типа 'strlen' будет возвращать целое число, а не строку.

В линейных функциях, таких как основные подагрические и логические функции (+-/*|&!) еще более ограничены, потому что за ними также стоит история формальной математической теории. (Подумайте обо всех аргументах о порядке операций для этих функций)

Чтобы фундаментальные функции возвращали что-либо, кроме их наиболее распространенного типа данных (логического или числового), следует классифицировать как целенаправленное запутывание.

Почти в каждом общеупотребительном языке «&», или «&&», или «И» — это логическая или логическая функция. За кулисами компиляторы оптимизации могут использовать сокращенную логику, как указано выше в LOGIC FLOW, но не модификацию DATA STRUCTURE (любой оптимизирующий компилятор, который изменил значение таким образом, считался бы неработающим), но если ожидается, что значение будет использоваться в переменной для дальнейшей обработки он должен быть логического или логического типа, поскольку в большинстве случаев это «формальный» тип для этих операторов.

person Cosmo F    schedule 26.11.2014