Есть ли в Python функция «или равно», например || = в Ruby?

83

Если нет, как лучше всего это сделать?

Прямо сейчас делаю (для проекта django):

if not 'thing_for_purpose' in request.session:
    request.session['thing_for_purpose'] = 5

но это довольно неудобно. В Ruby это будет:

request.session['thing_for_purpose'] ||= 5

что намного приятнее.

Шон В.
источник
23
Обратите внимание, что эти два фрагмента кода на самом деле очень разные: версия Python устанавливает его на 5, если его вообще нет в dict, где версия Ruby также устанавливает его на 5, если для него установлено любое ложное значение.
Гленн Мейнард,
2
@Glenn, не сильно отличается, но совсем другой. Поскольку возвращается неинициализированное хеш-значение nil(false в логическом контексте), эта идиома часто используется именно для той цели, с которой ее использовал sean. Идиома работает только в том случае, если, конечно, nilи falseне являются допустимыми значениями в хэше (что очень часто бывает правдой, поэтому идиома в порядке)
horseyguy
8
@banister: Я не знаю, где можно провести грань между «очень» и «вполне», но дело в том, что это не эквивалентные утверждения, и важно понимать разницу. (Ложь очень часто является допустимым значением в хэше, которое укусит вас, если вы захотите установить значение по умолчанию для логического поля в значение true; это значительный недостаток идиомы Руби.)
Гленн Мейнард,
Да, пример Python больше похож на defined-or , как //=в perl . Я думаю, что это ||=тоже происходит из Perl.
draegtun
Ах, хорошие моменты, все. Я не заметил этой разницы.
Шон В.

Ответы:

188

Принятый ответ хорош для dicts, но заголовок ищет общий эквивалент оператора || = в Ruby. Обычный способ сделать что-то вроде || = в Python:

x = x or new_value
Джозеф Шиди
источник
36
Вероятно, это то, что ищут люди, пришедшие сюда из поисковой системы Google.
gradi3nt 08
Это действительно лучший ответ.
AdamC
6
Обратите внимание, что в отличие от Ruby, этот код будет работать только в том случае, если xон был ранее определен (например, установлен Noneв метод инициализации класса).
oli
3
Правда, это не совсем так, как у Ruby || =. Еще одна вещь, на которую следует обратить внимание, - это «правдивость» x. Если x == 0 в Python, вы получите new_value, поскольку 0 считается ложным, но в Ruby вы получите 0.
Джозеф Шиди,
1
Это так же эффективно, как || =? || = не выполняет присваивание, если не имеет значения null, но всегда выполняет присваивание. Компилятор Python умнее, чем я думаю?
jsarma
17

dictесть setdefault().

Итак, если request.sessionэто dict:

request.session.setdefault('thing_for_purpose', 5)
Джон-Эрик
источник
Смутно похоже, но никоим образом не эквивалент Ruby's || =.
AdamC
1
Это существенно отличается тем, что выражение по умолчанию оценивается, даже если ключ уже установлен. Невозможно решить исходный вопрос, не оценив значение по умолчанию, не получив доступ к ключу более одного раза, или и то, и другое.
slinkp
13

Точный ответ: Нет Python не имеет один встроенный оператор , opкоторый может вылиться x = x or yв x op y.

Но это почти так. Побитовый оператор «или-равно» ( |=) будет работать, как описано выше, если оба операнда обрабатываются как логические, с оговоркой. (Какое предупреждение? Ответ, конечно же, ниже.)

Во-первых, базовая демонстрация функциональности:

x = True
x    
Out[141]: True

x |= True
x    
Out[142]: True

x |= False
x    
Out[143]: True

x &= False
x    
Out[144]: False

x &= True
x    
Out[145]: False

x |= False
x    
Out[146]: False

x |= True
x   
Out[147]: True

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

def  my_clear_list(lst):
    if not lst:
        return False
    else:
        del lst[:]
        return True

Теперь мы можем видеть короткое замыкание следующим образом:

x = True
lst = [1, 2, 3]
x = x or my_clear_list(lst)
print(x, lst)

Output: True [1, 2, 3]

Однако переключение на orпобитовое или ( |) устраняет короткое замыкание, поэтому функция my_clear_listвыполняется.

x = True
lst = [1, 2, 3]
x = x | my_clear_list(lst)
print(x, lst)

Output: True []

Выше x = x | my_clear_list(lst)эквивалентно x |= my_clear_list(lst).

Apollys поддерживает Монику
источник
10

Установка значения по умолчанию имеет смысл, если вы делаете это в промежуточном программном обеспечении или в чем-то еще, но если вам нужно значение по умолчанию в контексте одного запроса:

request.session.get('thing_for_purpose', 5) # gets a default

бонус: вот как это сделать на ||=Python.

def test_function(self, d=None):
    'a simple test function'
    d = d or {}

    # ... do things with d and return ...
Брайан Хикс
источник
1

В общем пользоваться можно dict[key] = dict.get(key, 0) + val.

curtissv
источник