Mam skrypt w języku Python, który może otrzymać zero lub trzy argumenty wiersza poleceń. (Albo działa z zachowaniem domyślnym, albo wymaga określenia wszystkich trzech wartości).
Jaka jest idealna składnia dla czegoś takiego:
if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):
?
python
if-statement
Chris Wilson
źródło
źródło
len(sys.argv)
zawsze będzie wynosić co najmniej 1: zawiera plik wykonywalny jakoargv[0]
.if not (a and b and c)
(zero argumentów), a następnieif a and b and c
(wszystkie trzy argumenty)?Odpowiedzi:
Jeśli masz na myśli minimalną formę, idź z tym:
if (not a or not b or not c) and (a or b or c):
Co tłumaczy tytuł twojego pytania.
AKTUALIZACJA: jak słusznie powiedzieli Volatility i Supr, możesz zastosować prawo De Morgana i uzyskać równoważne:
if (a or b or c) and not (a and b and c):
Moja rada jest taka, aby używać dowolnej formy, która jest ważniejsza dla Ciebie i dla innych programistów. Pierwsza oznacza „jest coś fałszywego, ale też coś prawdziwego” , druga „jest coś prawdziwego, ale nie wszystko” . Gdybym miał zoptymalizować lub zrobić to na sprzęcie, wybrałbym drugą, tutaj po prostu wybrałbym najbardziej czytelny (również biorąc pod uwagę warunki, które będziesz testować i ich nazwy). Ja wybrałem pierwszy.
źródło
if not (a and b and c) and (a or b or c)
if (a or b or c) and not (a and b and c)
idealnie pasować do tytułu;)if any([a,b,c]) and not all([a,b,c])
Co powiesz na:
conditions = [a, b, c] if any(conditions) and not all(conditions): ...
Inny wariant:
if 1 <= sum(map(bool, conditions)) <= 2: ...
źródło
sum(conditions)
może się nie udać, jeśli2
na przykład którykolwiek z nich powróci , czyliTrue
.sum(map(bool, conditions))
To pytanie miało już wiele bardzo pozytywnych odpowiedzi i akceptowaną odpowiedź, ale jak dotąd wszystkie z nich były rozpraszane różnymi sposobami wyrażenia problemu logicznego i pominęły kluczowy punkt:
Ta logika nie powinna leżeć w gestii twojego kodu , raczej powinna być obsługiwana przez
argparse
moduł. Nie kłopocz się pisaniem złożonej instrukcji if, zamiast tego wolisz ustawić parser argumentów w następujący sposób:#!/usr/bin/env python import argparse as ap parser = ap.ArgumentParser() parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z']) args = parser.parse_args() print(args.foo)
I tak, powinna to być opcja, a nie argument pozycyjny, bo przecież jest opcjonalna .
edytowane: Aby rozwiązać problem LarsHa w komentarzach, poniżej znajduje się przykład, jak można by to napisać, gdybyś był pewien, że chciałbyś mieć interfejs z 3 lub 0argumentami pozycyjnymi . Jestem zdania, że poprzedni interfejs ma lepszy styl, ponieważ opcjonalnymi argumentami powinny być opcje , ale tutaj jest alternatywne podejście ze względu na kompletność. Zwróć uwagę na nadpisywanie kwarg
usage
podczas tworzenia parsera, ponieważ wargparse
przeciwnym razie automatycznie wygeneruje mylący komunikat o użytkowaniu!#!/usr/bin/env python import argparse as ap parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n') parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z']) args = parser.parse_args() if len(args.abc) != 3: parser.error('expected 3 arguments') print(args.abc)
Oto kilka przykładów użycia:
# default case wim@wim-zenbook:/tmp$ ./three_or_none.py ['x', 'y', 'z'] # explicit case wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3 ['1', '2', '3'] # example failure mode wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 usage: three_or_none.py [-h] [a b c] three_or_none.py: error: expected 3 arguments
źródło
Poszedłbym na:
conds = iter([a, b, c]) if any(conds) and not any(conds): # okay...
Myślę, że to powinno dość skutecznie powodować zwarcie
Wyjaśnienie
Tworząc
conds
iterator, pierwsze użycieany
will powoduje zwarcie i pozostawienie iteratora wskazującego na następny element, jeśli którykolwiek element jest prawdziwy; w przeciwnym razie pochłonie całą listę i będzieFalse
. Następnaany
bierze pozostałe elementy w iterowalnej i upewnia się, że nie ma innych prawdziwych wartości ... Jeśli tak, całe stwierdzenie nie może być prawdziwe, więc nie ma jednego unikalnego elementu (więc krótkie spięcia jeszcze raz). Ostatniany
albo powróci,False
albo wyczerpie iterowalne i będzieTrue
.uwaga: powyższe sprawdza, czy jest ustawiony tylko jeden warunek
Jeśli chcesz sprawdzić, czy co najmniej jeden element jest ustawiony, ale nie każdy element jest ustawiony, możesz użyć:
not all(conds) and any(conds)
źródło
[a, b, c] = [True, True, False]
przypadkuFalse
, gdy kod nie powinien się "drukować" , podczas gdy oczekiwany wynik jestTrue
?iter
.any
iall
leniwie pochłonie listę, to prawda, ale lista była już w pełni oceniona, zanim tam dotrzesz!Zdanie angielskie:
Przekłada się na tę logikę:
(a or b or c) and not (a and b and c)
Słowo „ale” zwykle implikuje koniunkcję, innymi słowy „i”. Co więcej, „wszystkie z nich” przekłada się na połączenie warunków: ten warunek i ten warunek oraz inny warunek. „Nie” odwraca całą koniunkcję.
Nie zgadzam się z zaakceptowaną odpowiedzią. Autor zaniedbał zastosowanie najprostszej interpretacji specyfikacji i zaniedbał zastosowanie prawa De Morgana, aby uprościć wyrażenie dla mniejszej liczby operatorów:
not a or not b or not c -> not (a and b and c)
twierdząc, że odpowiedź jest „minimalną formą”.
źródło
A co z: (wyjątkowy stan)
if (bool(a) + bool(b) + bool(c) == 1):
Zauważ, że jeśli dopuszczasz dwa warunki, możesz to zrobić
if (bool(a) + bool(b) + bool(c) in [1,2]):
źródło
1 <= bool(a) + bool(b) + bool(c) <= 2
.To zwraca,
True
jeśli spełniony jest jeden i tylko jeden z trzech warunkówTrue
. Prawdopodobnie to, czego chciałeś w swoim przykładowym kodzie.if sum(1 for x in (a,b,c) if x) == 1:
źródło
Żeby było jasne, chcesz podjąć decyzję na podstawie tego, ile parametrów jest logicznych PRAWDA (w przypadku argumentów łańcuchowych - niepustych)?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
Wtedy podjąłeś decyzję:
if ( 0 < argsne < 3 ): doSth()
Teraz logika jest bardziej przejrzysta.
źródło
A dlaczego ich nie policzyć?
import sys a = sys.argv if len(a) = 1 : # No arguments were given, the program name count as one elif len(a) = 4 : # Three arguments were given else : # another amount of arguments was given
źródło
Jeśli nie masz nic przeciwko temu, że jesteś trochę tajemniczy, możesz po prostu rzucić, z
0 < (a + b + c) < 3
którym zwróci,true
jeśli masz od jednego do dwóch prawdziwych stwierdzeń i fałszywe, jeśli wszystkie są fałszywe lub żadne nie jest fałszywe.Upraszcza to również, jeśli używasz funkcji do oceny wartości bools, ponieważ obliczasz zmienne tylko raz, co oznacza, że możesz pisać funkcje w tekście i nie musisz tymczasowo przechowywać zmiennych. (Przykład:.
0 < ( a(x) + b(x) + c(x) ) < 3
)źródło
Pytanie mówi, że potrzebujesz albo wszystkich trzech argumentów (a i b i c), albo żadnego z nich (nie (a lub b lub c))
To daje:
(a i b i c) lub nie (a lub b lub c)
źródło
Jak rozumiem, masz funkcję, która otrzymuje 3 argumenty, ale jeśli nie, będzie działać z zachowaniem domyślnym. Ponieważ nie wyjaśniłeś, co powinno się stać, gdy podano 1 lub 2 argumenty, zakładam, że powinno to po prostu działać w sposób domyślny. W takim przypadku myślę, że następująca odpowiedź będzie bardzo korzystna:
def method(a=None, b=None, c=None): if all([a, b, c]): # received 3 arguments else: # default behavior
Jeśli jednak chcesz, aby 1 lub 2 argumenty były obsługiwane inaczej:
def method(a=None, b=None, c=None): args = [a, b, c] if all(args): # received 3 arguments elif not any(args): # default behavior else: # some args (raise exception?)
uwaga: przy założeniu, że
False
wartości „ ” nie będą przekazywane do tej metody.źródło
Jeśli pracujesz z iteratorem warunków, dostęp do niego może być wolny. Ale nie musisz uzyskiwać dostępu do każdego elementu więcej niż jeden raz i nie zawsze musisz czytać całość. Oto rozwiązanie, które będzie działać z nieskończonymi generatorami:
#!/usr/bin/env python3 from random import randint from itertools import tee def generate_random(): while True: yield bool(randint(0,1)) def any_but_not_all2(s): # elegant t1, t2 = tee(s) return False in t1 and True in t2 # could also use "not all(...) and any(...)" def any_but_not_all(s): # simple hadFalse = False hadTrue = False for i in s: if i: hadTrue = True else: hadFalse = True if hadTrue and hadFalse: return True return False r1, r2 = tee(generate_random()) assert any_but_not_all(r1) assert any_but_not_all2(r2) assert not any_but_not_all([True, True]) assert not any_but_not_all2([True, True]) assert not any_but_not_all([]) assert not any_but_not_all2([]) assert any_but_not_all([True, False]) assert any_but_not_all2([True, False])
źródło
Kiedy każdy dany
bool
jestTrue
lub kiedy każdy danybool
jestFalse
…wszyscy są sobie równi!
Więc, po prostu trzeba znaleźć dwa elementy rozpoznawaną różnych
bool
swiedzieć, że istnieje co najmniej jeden
True
, a co najmniej jedenFalse
.Moje krótkie rozwiązanie:
not bool(a)==bool(b)==bool(c)
Wierzę, że to zwarcia, bo AFAIK
a==b==c
równa sięa==b and b==c
.Moje uogólnione rozwiązanie:
def _any_but_not_all(first, iterable): #doing dirty work bool_first=bool(first) for x in iterable: if bool(x) is not bool_first: return True return False def any_but_not_all(arg, *args): #takes any amount of args convertable to bool return _any_but_not_all(arg, args) def v_any_but_not_all(iterable): #takes iterable or iterator iterator=iter(iterable) return _any_but_not_all(next(iterator), iterator)
Napisałem też kod zajmujący się wieloma iteracjami, ale usunąłem go stąd, ponieważ uważam, że to bezcelowe. Jest jednak nadal dostępny tutaj .
źródło
Jest to w zasadzie funkcja „niektóre (ale nie wszystkie)” (w przeciwieństwie do funkcji
any()
iall()
wbudowanych).Oznacza to, że wśród wyników powinny znajdować się
False
s iTrue
s. Dlatego możesz wykonać następujące czynności:some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False)) # one way to test this is... test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412 # Some test cases... assert(some(()) == False) # all() is true, and any() is false assert(some((False,)) == False) # any() is false assert(some((True,)) == False) # any() and all() are true assert(some((False,False)) == False) assert(some((True,True)) == False) assert(some((True,False)) == True) assert(some((False,True)) == True)
Jedną z zalet tego kodu jest to, że wystarczy raz przejść przez wynikowe elementy (wartości logiczne).
Jedną z wad jest to, że wszystkie te wyrażenia prawdy są zawsze oceniane i nie powodują zwarć, takich jak operatory
or
/and
.źródło
.issuperset
zamiast po prostu sprawdzić długość 2,bool
i tak nie może zwrócić niczego innego niż True i False. Po co przypisywać lambdę (czytaj: funkcję anonimową) do nazwy zamiast po prostu używać def?return
jeśli używaszdef
. myślę, że ogólność tego rozwiązania jest fajna. nie jest konieczne ograniczanie się do wartości logicznych, zasadniczo pytanie brzmi: „jak upewnić się, że wszystkie te elementy występują na mojej liście”. dlaczego,set
jeśli nie potrzebujesz zmienności? Większa niezmienność jest zawsze lepsza, jeśli nie potrzebujesz wydajności.tee
itertools.tee
ale 1) szukałem