Operator trójargumentowy w Pythonie
Operator trójargumentowy (ang: ternary operator) zwany też operatorem warunkowym pozwala zwrócić jedną z dwóch wartości na podstawie wyniku badanego warunku.
Jeśli warunek daje wynik "prawda" to zwracana jest pierwsza wartość. Jeśli warunek daje wynik "fałsz" to zwracana jest druga wartość.
W wielu językach operator trójargumentowy znany jest jako ?:
(nawet w Wikipedii jego angielski opis widnieje pod hasłem ?:) i ma składnię
sprawdzany_warunek ? wartość_zwracana_gdy_prawda : wartość_zwracana_gdy_fałsz
Przykłady użycia w PHP
(podobnie wygląda to w C
i pochodnych)
echo $a != 0 ? "różne od zera" : "równe zero";
# lub
$rok_przestepny = true;
$luty = $rok_przestepny ? 29 : 28;
W Pythonie operator trójargumentowy ma to samo działanie ale inną składnię
wartość_zwracana_gdy_prawda if sprawdzany_warunek else wartość_zwracana_gdy_fałsz
Operator zwraca jedną z wartości, którą można przypisana do zmiennej
luty = 29 if rok_przestepny else 28
co odpowiada zwykłemu if/else
if rok_przestepny:
luty = 29
else:
luty = 28
Zwracaną wartość można też od razu użyć w innym działaniu
luty = 28 + (1 if rok_przestepny else 0)
co mogło by odpowiadać
luty = 28
if rok_przestepny:
luty += 1
else:
luty += 0
Wartość z operatora można też przekazać od razu do funkcji
print("równe zero" if zmienna == 0 else "różne od zera")
co mogło by odpowiadać
if zmienna == 0:
_wartosc_ = "równe zero"
else:
_wartosc_ = "różne od zera"
print(_wartosc_)
Operator może też wywoływać funkcje
print("równe zero") if zmienna == 0 else print("różne od zera")
co odpowiada
if zmienna == 0:
print("równe zero")
else
print("różne od zera")
Pierwszy przykład z print()
jest popularniejszy w użyciu
za to drugi lepiej pokazuje podobieństwo do zwykłego if/else
W operatorze nie da się jednak użyć wyrażeń - przykładowo przypisania wartości czy pass
- które nie zwracają wartości.
Poniższe przykłady nie zadziałają
(luty = 29) if rok_przestepny else (luty = 28)
# Syntax error
print("ten rok jest o dzień dłuższy") if rok_przestepny else pass
# Syntax error
Operator trójargumentowy może wywoływać różne funkcje i wyniki przez nie zwrócone przypisać do zmiennej
wynik = a + b if operacja == "dodawanie" else a - b
co odpowiada
if operacja == "dodawanie":
wynik = a + b
else:
wynik = a - b
Tak samo może wynik działania jednej funkcji przekazać jako argument w innej funkcji
operacja = "dodawanie"
print(operacja, a + b if operacja == "dodawanie" else a - b)
# dodawanie 17
operacja = "odejmowanie"
print(operacja, a + b if operacja == "dodawanie" else a - b)
# odejmowanie 3
co odpowiada
if operacja == "dodawanie":
print(operacja, a + b)
else:
print(operacja, a - b)
Całość operatora trójargumentowego należy wpisać w jednej linii i nie wymaga :
na końcu if
i else
.
Operator trójargumentowy - w przeciwieństwie do normalnego if/else
- nie może zawierać więcej niż jednej funkcji.
Nie da się więc normalnie przerobić poniższego na operator trójargumentowy.
if rok_przestepny:
print('Luty ma o jeden dzień więcej')
luty = 29
else:
print('Luty ma tylko 28 dni')
luty = 28
Można wprawdzie obejść pewne przypadki poprzez użycie listy
[print("równe"), print("zero")] if a == 0 else [print("różne od"), print("zera")]
ale także w liście nie wszystko jest dozwolone - przykładowo nie da się przypisać wartości
[print("równe zero"), luty = 28] if a == 0 else [print("różne od zera"), luty = 29]
# Syntax error
Dodatkowo kod z użyciem listy staje się mniej czytelny niż z normalnym if/else
Operator trójargumentowy może być przydatny w wyrażeniach listowych (ang: list comprehension)
data = ["parzysta" if x % 2 == 0 else "nieparzysta" for x in range(5)]
print(data)
['parzysta', 'nieparzysta', 'parzysta', 'nieparzysta', 'parzysta']
co odpowiada
data = []
for x in range(5):
if x % 2 == 0:
data.append("parzysta")
else:
data.append("nieparzysta")
print(data)
Bardziej rozbudowany przykład
data = [(x, "parzysta" if x % 2 == 0 else "nieparzysta") for x in range(5)]
print(data)
[(0, 'parzysta'), (1, 'nieparzysta'), (2, 'parzysta'), (3, 'nieparzysta'), (4, 'parzysta')]
Dodatkowo operatory trójargumentowe można składać co daje efekt użycia if/elif/else
data = [(x, "zero" if x == 0 else ("ujemna" if x < 0 else "dodatnia")) for x in (1,0,-5,2,-2,0)]
print(data)
[(1, 'dodatnie'), (0, 'zero'), (-5, 'ujemna'), (3, 'dodatnia'), (-2, 'ujemna'), (0, 'zero')]
co odpowiada
data = []
for x in (1,0,-5,2,-2,0):
if x == 0:
data.append( (x, "zero") )
elif x < 0:
data.append( (x, "ujemna") )
else:
data.append( (x, "dodatnia") )
print(data)
Można złożyć więcej niż dwa operatory trójargumentowe i otrzymać odpowiednik if/elif/else
z większą ilością elif
.
Taki kod staje się jednak długi i nieczytelny więc czytelniejsze wydaje się jednak użycie funkcji
def zamien(x):
if x == 0:
return (x, "zero")
elif x < 0:
return (x, "ujemna")
else:
return (x, "dodatnia")
data = [zamien(x) for x in (1,0,-5,2,-2,0)]
print(data)
Operator trójargumentowy może też być zastosowany w lambda
sorted(range(10), key=lambda x: x if x % 2 == 0 else -x)
# [9, 7, 5, 3, 1, 0, 2, 4, 6, 8]
Operator trójargumentowy może zastąpić if/else
lub if/elif/else
ale nie może zastąpić samodzielnego if
bez else
.
Nie da się wstawić pass
po else
lub przed if
aby pominąc jeden z przypadków.
Przez niektórych operator trójargumentowy jest uważany za lukier syntaktyczny (ang: syntatics sugar czyli cechę składni języka, którą daje się zastąpić inną składnią ale istnieje ona dla przyjemności programisty.
Wikipedia: operator warunkowy (ang: ternary operator), operator trójargumentowy w instrukcja warunkowa
Uwaga: w Python 3.8 pojawił się operator :=
(zwany po angielsku walrus
co znaczy mors
),
który przypisuje wartość do zmiennej i zwraca tę wartość dalej co pozwala na wykonanie poniższego
(luty := 29) if rok_przestepny else (luty := 28)
Operator jednak powstał z myślą o innym przeznaczeni i spodziewam się, że powyższe wykorzystanie nie będzie jednak preferowane.
Już raczej będzie się go wykorzystywać do zwracania wartości funkcji lub wartości domyślnej.
wynik = (kwota if (kwota := podatek()) > 0 else 0)
zamiast
wynik = (podatek() if podatek() > 0 else 0)
Szczególnie to będzie przydatne gdy każde wywołanie funkcji zajmuje bardzo dużo czasu (więc po co wywoływać go dwa razy) lub każde wywołaniem zwraca inny wynik (np. iteratory)
wynik = (linia if len(linia := fp.readline()) > 0 else "")
zamiast błędnie dzałającego (bo każde wywołanie readline
zwraca inną linię)
wynik = (fp.readline() if len(fp.readline()) > 0 else "- pusta -")
Będzie to pewnie przypominało operatory, znane z innych języków, które zwracają wartość funkcji lub wartość domyślną
(ang: elvis operator)
Buy a Coffee