Czy Python ma funkcję „lub równa się”, jak || = w Rubim?


83

Jeśli nie, jaki jest najlepszy sposób, aby to zrobić?

Teraz robię (dla projektu django):

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

ale jest to dość niezręczne. W Rubim byłoby to:

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

co jest o wiele przyjemniejsze.


23
Zauważ, że te dwa bity kodu są w rzeczywistości bardzo różne: wersja Pythona ustawia go na 5, jeśli w ogóle nie ma go w dyktandzie, podczas gdy wersja Ruby ustawia go również na 5, jeśli jest ustawiona na jakąkolwiek wartość fałszywą.
Glenn Maynard,

2
@Glenn, niezbyt różne, ale zupełnie inne. Ponieważ niezainicjowana wartość skrótu zwraca nil(fałsz w kontekście logicznym), ten idiom jest często używany dokładnie w celu, w jakim go użył. Idiom działa tylko jeśli choć, oczywiście, nili falsenie są uzasadnione wartości w hash (co jest bardzo często prawdziwe, więc idiom jest w porządku)
horseyguy

8
@banister: Nie wiem, gdzie można wyznaczyć granicę między „bardzo” a „całkiem”, ale chodzi o to, że nie są to równoważne stwierdzenia i ważne jest, aby zrozumieć różnicę. (Fałsz jest bardzo często prawidłową wartością w hashu, która ugryzie Cię, gdy chcesz ustawić domyślne pole boolowskie na true; jest to znacząca wada idiomu Rubiego.)
Glenn Maynard

Tak, przykład Pythona jest bardziej podobny do zdefiniowanego-lub , takiego jak //=w perlu . Myślę, że ||=również pochodzi z Perla.
draegtun

Ach, dobre punkty, wszystko. Nie zauważyłem tej różnicy.
Sean W.,

Odpowiedzi:


188

Zaakceptowana odpowiedź jest dobra w przypadku dykt, ale tytuł szuka ogólnego odpowiednika operatora || = Rubiego. Typowym sposobem zrobienia czegoś takiego jak || = w Pythonie jest

x = x or new_value

36
Ta odpowiedź jest prawdopodobnie tym, czego szukają ludzie przychodzący tutaj z wyszukiwarki Google.
gradi3nt

To naprawdę najlepsza odpowiedź.
AdamC

6
Zauważ, że w przeciwieństwie do Rubiego, ten kod będzie działał tylko wtedy, gdy xzostał wcześniej zdefiniowany (na przykład ustawiony na Nonew metodzie inicjalizacji klasy).
oli

3
To prawda, to nie działa dokładnie tak, jak Ruby || =. Kolejną rzeczą, na którą należy zwrócić uwagę, jest „prawdziwość” x. Jeśli x == 0 w Pythonie, otrzymasz new_value, ponieważ 0 jest uważane za fałsz, ale w Ruby otrzymasz 0.
Joseph Sheedy

1
Czy to jest tak wydajne jak || =? || = nie wykonuje przypisania, chyba że jego wartość jest zerowa, ale to zawsze powoduje przypisanie. Czy kompilator Pythona jest sprytniejszy, niż myślę, że jest?
jsarma

17

dictma setdefault().

Więc jeśli request.sessionjest dict:

request.session.setdefault('thing_for_purpose', 5)

Zupełnie podobny, ale w żaden sposób odpowiednik || = Rubiego.
AdamC

1
Różni się znacznie tym, że domyślne wyrażenie jest oceniane, nawet jeśli klucz jest już ustawiony. Nie ma sposobu, aby rozwiązać pierwotne pytanie bez oceny wartości domyślnej lub uzyskania dostępu do klucza więcej niż raz, lub obu.
slinkp

13

Precyzyjna odpowiedź: Nie. Python nie posiada jeden wbudowany operatora op, który może przełożyć x = x or ysię x op y.

Ale prawie tak. Operator bitowy lub równości ( |=) będzie działał w sposób opisany powyżej, jeśli oba operandy są traktowane jako wartości logiczne, z zastrzeżeniem. (Jakie jest zastrzeżenie? Odpowiedź jest oczywiście poniżej.)

Najpierw podstawowa demonstracja funkcjonalności:

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

Zastrzeżenie jest spowodowane tym, że Python nie jest ściśle wpisywany, a zatem nawet jeśli wartości są traktowane jako wartości logiczne w wyrażeniu, nie zostaną zwarte, jeśli zostaną podane operatorowi bitowemu. Na przykład załóżmy, że mamy funkcję boolowską, która czyści listę i zwraca, Truejeśli zostały usunięte elementy:

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

Teraz możemy zobaczyć zachowanie związane ze zwarciem w następujący sposób:

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

Output: True [1, 2, 3]

Jednak przełączenie na orbitowe lub ( |) usuwa zwarcie, więc funkcja jest my_clear_listwykonywana.

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

Output: True []

Powyżej x = x | my_clear_list(lst)jest równoważne z x |= my_clear_list(lst).


10

Ustawienie wartości domyślnej ma sens, jeśli robisz to w oprogramowaniu pośredniczącym lub w czymś takim, ale jeśli potrzebujesz wartości domyślnej w kontekście jednego żądania:

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

bonus: oto jak naprawdę zrobić ||=w Pythonie.

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

    # ... do things with d and return ...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.