"Eğer a veya b veya c ise, hepsi değil" için Python sözdizimi


130

Sıfır veya üç komut satırı argümanı alabilen bir python betiğim var. (Ya varsayılan davranışta çalışır ya da belirtilen üç değerin tümüne ihtiyaç duyar.)

Şunun gibi bir şey için ideal sözdizimi nedir:

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):

?


4
belki `if len (sys.argv) == 0:
Edgar Aroutiounian

6
@EdgarAroutiounian len(sys.argv)her zaman en az 1 olacaktır: çalıştırılabilir dosyayı argv[0].
RoadieRich

10
Sorunun gövdesi, sorunun başlığıyla eşleşmiyor. "A veya b veya c olup hepsi değil" veya "tam olarak a, b ve c'den biri mi" (verdiğiniz ifadede olduğu gibi) kontrol etmek ister misiniz?
Doug McClean

2
A + b + c hakkında ne söyleyebilirsiniz?
gukoff

6
Bekle, soru, sıfır veya üç argüman alabilir. sadece if not (a and b and c)(sıfır bağımsız değişken) ve sonra if a and b and c(üç bağımsız değişken) diyemez miydin ?
acolyte

Yanıtlar:


236

Minimal formdan bahsediyorsanız, bununla devam edin:

if (not a or not b or not c) and (a or b or c):

Hangi sorunuzun başlığını çevirir.

GÜNCELLEME: Volatility ve Supr tarafından doğru bir şekilde söylendiği gibi, De Morgan yasasını uygulayabilir ve eşdeğerini elde edebilirsiniz:

if (a or b or c) and not (a and b and c):

Benim tavsiyem, hangisi siz ve diğer programcılar için daha önemliyse onu kullanmanızdır. Birincisi "yanlış bir şey var, ama aynı zamanda doğru bir şey var" , ikincisi "Bir şey var ama her şey değil" . Bunu donanımda optimize edecek veya yapacak olsaydım, ikincisini seçerdim, burada sadece en okunaklı olanı seçerdim (ayrıca test edeceğiniz koşulları ve isimlerini de dikkate alarak). İlkini ben seçtim.


3
Hepsi harika cevaplar, ancak bu büyük bir kısa devre ile özlülük için kazanır. Hepinize teşekkürler!
Chris Wilson

38
Daha da özlü hale getirir ve devam ederdimif not (a and b and c) and (a or b or c)
Volatility

208
Ya da if (a or b or c) and not (a and b and c)başlığa mükemmel bir şekilde uyması için bile ;)
Supr

3
@HennyH Sorunun "en az bir koşul doğru ama hepsi değil" istediğine inanıyorum, "yalnızca bir koşul doğru" değil.
Volatilite

63
@Suprif any([a,b,c]) and not all([a,b,c])
eternalmatt

238

Peki ya:

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...

Diğer değişken:

if 1 <= sum(map(bool, conditions)) <= 2:
   ...

2
sum(conditions)2Örneğin herhangi biri geri dönerse , yanlış gidebilir True.
eumiro

7
Doğru, çirkin bir sum(map(bool, conditions))
şeye

5
Bunun kısa devre olmadığını unutmayın, çünkü tüm koşullar önceden değerlendirilmiştir.
georg

14
@PaulScheltema İlk form herkes için daha anlaşılır.
cmh

6
Bu "herhangi biri ve hepsi değil", boole yöntemlerinin en iyisi ve en açık olanıdır, sadece mevcut bir argüman ile `` doğru '' olma arasındaki önemli ayrıma dikkat edin
wim

115

Bu sorunun halihazırda yüksek oy alan pek çok yanıtı ve kabul edilmiş bir yanıtı vardı, ancak şimdiye kadar hepsinin dikkati boole sorununu ifade etmenin çeşitli yollarıyla dağıldı ve önemli bir noktayı kaçırdı:

Sıfır veya üç komut satırı argümanı alabilen bir python betiğim var. (Ya varsayılan davranışta çalışır ya da belirtilen üç değerin tümüne ihtiyaç duyar)

Bu mantık ilk etapta kodunuzun sorumluluğu olmamalı,argparsemodültarafından ele alınmalıdır. Karmaşık bir if ifadesi yazmakla uğraşmayın, bunun yerine argüman ayrıştırıcınızı şuna benzer bir şekilde kurmayı tercih edin:

#!/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)

Ve evet, konumsal bir argüman değil bir seçenek olmalı , çünkü sonuçta isteğe bağlı .


düzenlenmiş: Yorumlarda LarsH'nin endişesini gidermek için, 3 veya 0 konumsal bağımsız değişkenli arayüzü istediğinizden eminseniz bunu nasıl yazabileceğinize dair bir örnek aşağıda verilmiştir. Önceki arayüzün daha iyi bir tarz olduğukanısındayım, çünkü isteğe bağlı argümanlar seçenekler olmalıdır, ancak işte bütünlük adına alternatif bir yaklaşım. usageAyrıştırıcınızı oluştururkengeçersiz kılan kwarg'a dikkatedin, çünküargparseaksi takdirde otomatik olarak yanıltıcı bir kullanım mesajı oluşturacaktır!

#!/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)

İşte bazı kullanım örnekleri:

# 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

4
Evet, bunu bilerek ekledim. Argümanı konumsal hale getirmek ve tam olarak 3 veya 0'ın tüketildiğini zorlamak mümkün olabilirdi, ancak bu iyi bir CLI olmazdı, bu yüzden tavsiye etmedim.
wim

8
Ayrı konu. Bunun iyi bir CLI olduğuna inanmıyorsunuz ve bu nokta için tartışabilirsiniz ve OP ikna edilebilir. Ancak cevabınız, spesifikasyon değişikliğinin belirtilmesi gerekecek kadar sorudan yeterince farklı. Değişiklikten bahsetmeden teknik özellikleri mevcut araca uyacak şekilde büküyorsunuz.
LarsH

2
@LarsH Tamam, soruda ima edilen orijinal arayüzle daha iyi uyan bir örnek ekledim. Şimdi mevcut özellikleri karşılamak için aleti büküyor ...;)
wim

2
Oy verdiğim tek cevap bu. Gerçek soruyu cevaplamak için +1 .
Jonathon Reinhart

1
+1. CLI'nin biçimi, başka birinin söylediği gibi tamamen ayrı olmayan, önemli bir teğet meselesidir. Diğerlerinin yanı sıra sizin gönderinize oy verdim - sizinki sorunun kökenine iniyor ve zarif bir çözüm sunarken, diğer gönderiler gerçek soruyu yanıtlıyor. Ve her iki cevap türü de faydalıdır ve + 1'i hak eder.
Ben Lee

32

Ben giderdim:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...

Bunun oldukça verimli bir şekilde kısa devre yapması gerektiğini düşünüyorum

açıklama

condsBir yineleyici yaparak , ilk kullanımı anykısa devre yapar ve herhangi bir öğe doğruysa yineleyiciyi sonraki öğeye işaret eder; aksi takdirde, tüm listeyi tüketecek ve olacaktır False. Bir sonraki any, yinelenebilirdeki kalan öğeleri alır ve başka hiçbir gerçek değerin olmadığından emin olur ... Varsa, tüm ifade doğru olamaz, bu nedenle tek bir benzersiz öğe yoktur (yani kısa devreler tekrar). Sonuncusu anyya geri dönecek Falseya da yinelenebilir olanı tüketecek ve olacaktır True.

not: yukarıdaki yalnızca tek bir koşulun ayarlanıp ayarlanmadığını kontrol eder


Bir veya daha fazla öğenin olup olmadığını kontrol etmek istiyorsanız, ancak her öğenin ayarlanmadığını kontrol etmek istiyorsanız, şunları kullanabilirsiniz:

not all(conds) and any(conds)

5
Ben anlamadım Şöyle okur: if True ve not True. Anlamama yardım et.
rGil

1
@rGil: "Bazı elmalar kırmızıysa, bazıları kırmızı değilse" gibi okur - "bazı elmalar kırmızıdır ama hepsi değil" demekle aynıdır.
georg

2
Açıklamayla bile davranışı anlayamıyorum ... Beklenen çıktı iken [a, b, c] = [True, True, False]kodunuz "yazdırılmamalı" mı? FalseTrue
awesoon

6
Bu oldukça zekice, AMA: Önceden kaç koşulunuz olduğunu bilmiyorsanız bu yaklaşımı kullanırdım, ancak sabit, bilinen bir koşul listesi için okunabilirlik kaybı buna değmez.
kabarık

4
Bu kısa devre yapmaz. Liste, kendisine geçilmeden önce tamamen oluşturulmuştur iter. anyve alllisteyi tembelce tüketecek, doğru, ancak siz oraya vardığınızda liste zaten tamamen değerlendirilmişti!
icktoofay

22

İngilizce cümle:

"A veya b veya c ise ancak hepsi değil"

Bu mantığa çevirir:

(a or b or c) and not (a and b and c)

"Ama" kelimesi genellikle bir birleşim anlamına gelir, diğer bir deyişle "ve". Dahası, "hepsi" bir koşul birleşimi anlamına gelir: bu koşul ve bu koşul ve başka bir durumu. "Değil", tüm birleşimi tersine çevirir.

Cevabın kabul edildiğine katılmıyorum. Yazar, spesifikasyona en basit yorumu uygulamayı ihmal etti ve ifadeyi daha az operatöre basitleştirmek için De Morgan Yasasını uygulamayı ihmal etti:

 not a or not b or not c  ->  not (a and b and c)

cevabın "minimal bir form" olduğunu iddia ederken.


Aslında bu form asgari düzeydedir. Bu var minimal POS formu ifadesi için.
Stefano Sanfilippo

10

Bu True, üç koşuldan biri ve yalnızca biri ise geri döner True. Muhtemelen örnek kodunuzda istediğiniz şey.

if sum(1 for x in (a,b,c) if x) == 1:

@Defuz'un cevabı kadar güzel değil
jamylak

10

Peki ya: (benzersiz durum)

if (bool(a) + bool(b) + bool(c) == 1):

Dikkat edin, iki koşula da izin verirseniz, bunu da yapabilirsiniz.

if (bool(a) + bool(b) + bool(c) in [1,2]):

1
Kayıt için soru iki koşulu soruyor. En az bir tane ama hepsi değil = hepsinden 1 tanesi veya 2 tanesi
Marius Balčytis

IMHO ikinciyi olarak hecelemelisin 1 <= bool(a) + bool(b) + bool(c) <= 2.
Monica'yı eski

6

Açık olmak gerekirse, kararınızı parametrelerin ne kadarının mantıksal DOĞRU olduğuna göre mi vermek istiyorsunuz (dizge bağımsız değişkenlerinde - boş değil)?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)

Sonra bir karar verdin:

if ( 0 < argsne < 3 ):
 doSth() 

Şimdi mantık daha net.


5

Ve neden onları saymıyorsun?

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

5

Biraz şifreli olmayı dert etmezseniz, bir ile iki arasında doğru ifadeniz varsa ve hepsi yanlışsa veya hiçbiri yanlışsa yanlış 0 < (a + b + c) < 3geri dönecektir true.

Bu aynı zamanda, değişkenleri yalnızca bir kez değerlendirdiğiniz için işlevleri değerlendirmek için işlevleri kullanırsanız da basitleştirir ve bu, işlevleri satır içi yazabileceğiniz ve değişkenleri geçici olarak depolamanıza gerek olmadığı anlamına gelir. (Örnek:. 0 < ( a(x) + b(x) + c(x) ) < 3)


4

Soru, üç argümana (a ve b ve c) veya hiçbirine (a veya b veya c) değil) ihtiyacınız olduğunu belirtir.

Bu şunu verir:

(a ve b ve c) veya değil (a veya b veya c)


4

Anladığım kadarıyla, 3 argüman alan bir fonksiyonunuz var, ancak almazsa varsayılan davranışta çalışacak. 1 veya 2 argüman sağlandığında ne olması gerektiğini açıklamadığınız için, bunun sadece varsayılan davranışı yapması gerektiğini varsayacağım. Bu durumda aşağıdaki cevabı çok avantajlı bulacağınızı düşünüyorum:

def method(a=None, b=None, c=None):
    if all([a, b, c]):
        # received 3 arguments
    else:
        # default behavior

Bununla birlikte, 1 veya 2 bağımsız değişkenin farklı şekilde işlenmesini istiyorsanız:

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?)

not: Bu, " False" değerlerin bu yönteme aktarılmayacağını varsayar .


Bir argümanın doğruluk değerini kontrol etmek, bir argümanın mevcut olup olmadığını kontrol etmekten farklı bir konudur
wim

@wim So, bir soruyu cevabınıza uyacak şekilde dönüştürüyor. Argparse modülünün soruyla hiçbir ilgisi yoktur, başka bir içe aktarım ekler ve OP argparse kullanmayı hiç planlamıyorsa, onlara ne olursa olsun yardımcı olmayacaktır. Ayrıca, "komut dosyası" bağımsız değilse, bir modül veya daha büyük bir kod kümesi içindeki bir işlevse, zaten bir bağımsız değişken ayrıştırıcısı olabilir ve bu daha büyük komut dosyası içindeki bu belirli işlev varsayılan veya özelleştirilmiş olabilir. OP'den gelen sınırlı bilgi nedeniyle, yöntemin nasıl davranması gerektiğini bilemiyorum, ancak OP'nin bool'leri geçmediğini varsaymak güvenlidir.
Inbar Rose

Soru açıkça "Sıfır veya üç komut satırı argümanını alabilen bir python betiğim var" dedi, "3 argüman alan bir fonksiyonum var" demedi. Argparse modülü python'da komut satırı argümanlarını işlemenin tercih edilen yolu olduğundan, soruyla otomatik olarak ilgisi vardır. Son olarak, python "piller dahildir" - bu modül standart kitaplıkların bir parçası olduğunda "başka bir içe aktarma eklemenin" herhangi bir dezavantajı yoktur.
wim

@wim Soru oldukça net değil (örneğin gövde başlığı ile eşleşmiyor). Bence soru yeterince açık değil ki , bunun bazı yorumları için geçerli bir cevap .
21'de Monica'yı eski

2

Bir koşul yineleyicisi ile çalışıyorsanız, erişim yavaş olabilir. Ancak her bir öğeye birden fazla erişmeniz gerekmez ve her zaman hepsini okumanız gerekmez. İşte sonsuz jeneratörlerle çalışacak bir çözüm:

#!/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])

0

Her verilen boololduğunda Trueveya her verilen boololduğunda False...
hepsi birbirine eşittir!

Öyleyse, en az bir ve en az bir tane olduğunu bilmek için farklı boole'leri değerlendiren iki öğe bulmamız gerekiyor .
TrueFalse

Kısa çözümüm:

not bool(a)==bool(b)==bool(c)

Kısa devrelere inanıyorum, çünkü AFAIK a==b==ceşittir a==b and b==c.

Genelleştirilmiş çözümüm:

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)

Birden çok yinelenebilir kodla ilgili bazı kodlar da yazdım, ancak buradan sildim çünkü bunun anlamsız olduğunu düşünüyorum. Ancak yine de burada mevcuttur .


-2

Bu temelde bir "bazı (ancak tümü değil)" işlevselliğidir ( any()ve ile karşılaştırıldığındaall() yerleşik işlevlerle karşılaştırıldığında).

Bu , sonuçlar arasında Falses ve True s olması gerektiği anlamına gelir . Bu nedenle aşağıdakileri yapabilirsiniz:

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)

Bu kodun bir avantajı, ortaya çıkan (boolean) öğeleri yalnızca bir kez yinelemeniz gerekmesidir.

Bir dezavantaj, tüm bu doğru ifadelerin her zaman değerlendirilmesidir ve / operatörleri gibi kısa devre yapmaz .orand


1
Bunun gereksiz bir komplikasyon olduğunu düşünüyorum. Sade eski bir set yerine neden bir frozenset? Neden .issupersetsadece uzunluk 2'yi kontrol etmek yerine boolTrue ve False'dan başka bir şey döndüremiyor zaten. Neden sadece def kullanmak yerine bir isme lambda (okuma: anonim işlev) atayasınız?
wim

1
lambda sözdizimi bazıları için daha mantıklı. returnEğer kullanmanız gerektiğinden beri aynı uzunluktadırlar def. bence bu çözümün genelliği güzel. kendimizi boolelerle sınırlamamız gerekmez, esas soru "tüm bu öğelerin listemde yer almasını nasıl sağlayabilirim" dir. neden setdeğişebilirliğe ihtiyacınız yoksa? performansa ihtiyacınız yoksa daha fazla değişmezlik her zaman daha iyidir.
Janus Troelsen

@JanusTroelsen Doğru hedefdesiniz! İşte bu şekilde yapmamın bazı nedenleri; benim için daha kolay ve anlaşılır hale getiriyor. Python'u kodlama yöntemime uyarlama eğilimindeyim :-).
Abbafei

ama sonsuz üreteçlerde çalışmayacak: Cevabıma bakın :) ipucu: tee
cevabıma

@JanusTroelsen Bunu anlıyorum :-). Aslında ilk başta bunun tam tersi oldu (sette Doğru / Yanlış ve yöntem parametresinde yinelenebilir), ancak bunun sonsuz üreteçlerle çalışmayacağını ve bir kullanıcının farkında olmayabileceğini fark ettim (çünkü bu gerçek değil (henüz) belgelerde yinelenebilir küme yöntemi parametreleri için bahsedilmiştir) ve en azından bunun gibi sonsuz yineleyiciler almayacağı açıktır. Farkındaydım itertools.teeama 1) Kopyala yapıştırmayı gerektirecek kadar basit / küçük bir tek
satırlık arıyordum
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.