Cú pháp Python cho, nếu a hoặc b hoặc c nhưng không phải tất cả chúng


130

Tôi có một kịch bản python có thể nhận được không hoặc ba đối số dòng lệnh. (Hoặc nó chạy trên hành vi mặc định hoặc cần cả ba giá trị được chỉ định.)

Cú pháp lý tưởng cho một cái gì đó như:

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
có thể bắt đầu với một cái gì đó như `if len (sys.argv) == 0:
Edgar Aroutiounian

6
@EdgarAroutiounian len(sys.argv)sẽ luôn có ít nhất 1: nó bao gồm các tệp thực thi như argv[0].
RoadieRich

10
Phần chính của câu hỏi không khớp với tiêu đề của câu hỏi. Bạn có muốn kiểm tra "nếu a hoặc b hoặc c nhưng không phải tất cả chúng" hoặc "nếu chính xác là một trong a, b và c" (như biểu thức bạn đã đưa ra)?
Doug McClean

2
Bạn có thể nói gì về a + b + c?
gukoff

6
Đợi, câu hỏi, nó có thể mất không hoặc ba đối số. bạn không thể nói if not (a and b and c)(zero args), và sau đó if a and b and c(cả ba args)?
acolyte

Câu trả lời:


236

Nếu bạn có nghĩa là một hình thức tối thiểu, đi với điều này:

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

Mà dịch tiêu đề của câu hỏi của bạn.

CẬP NHẬT: như đã nói chính xác bởi Biến động và Supr, bạn có thể áp dụng luật của De Morgan và có được tương đương:

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

Lời khuyên của tôi là sử dụng bất kỳ hình thức nào có ý nghĩa hơn đối với bạn và các lập trình viên khác. Cái đầu tiên có nghĩa là "có cái gì đó sai, nhưng cũng có cái gì đó đúng" , cái thứ hai "Có cái gì đó đúng, nhưng không phải là tất cả" . Nếu tôi tối ưu hóa hoặc làm điều này trong phần cứng, tôi sẽ chọn cái thứ hai, ở đây chỉ cần chọn thứ dễ đọc nhất (cũng xem xét các điều kiện bạn sẽ kiểm tra và tên của chúng). Tôi chọn cái đầu tiên.


3
Tất cả các câu trả lời tuyệt vời, nhưng điều này chiến thắng cho sự đồng nhất, với ngắn mạch tuyệt vời. Cảm ơn tất cả!
Chris Wilson

38
Tôi sẽ làm cho nó thậm chí ngắn gọn hơn và đi vớiif not (a and b and c) and (a or b or c)
Biến động

208
Hoặc thậm chí if (a or b or c) and not (a and b and c)để phù hợp với tiêu đề một cách hoàn hảo;)
Supr

3
@HennyH Tôi tin rằng câu hỏi yêu cầu "ít nhất một điều kiện đúng nhưng không phải tất cả", không phải "chỉ một điều kiện đúng".
Biến động

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

238

Làm thế nào về:

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

Biến thể khác:

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

2
sum(conditions)có thể sai nếu bất kỳ trong số họ trả lại 2, ví dụ, đó là True.
eumiro

7
Đúng là bạn sẽ cần một thứ xấu xísum(map(bool, conditions))
jamylak

5
Lưu ý rằng đây không phải là ngắn mạch, bởi vì tất cả các điều kiện được đánh giá trước.
Georgia

14
@PaulScheltema Hình thức đầu tiên dễ hiểu hơn đối với bất kỳ ai.
cmh

6
Này "bất kỳ và không phải tất cả" là tốt nhất và rõ ràng nhất của phương pháp boolean, chỉ hãy chú ý đến sự khác biệt quan trọng giữa một arg có mặt và một arg là 'truthy'
wim

115

Câu hỏi này đã có nhiều câu trả lời được đánh giá cao và một câu trả lời được chấp nhận, nhưng tất cả chúng đều bị phân tâm bởi nhiều cách khác nhau để diễn đạt vấn đề boolean và bỏ lỡ một điểm quan trọng:

Tôi có một kịch bản python có thể nhận được không hoặc ba đối số dòng lệnh. (Hoặc nó chạy trên hành vi mặc định hoặc cần cả ba giá trị được chỉ định)

Logic này không phải là trách nhiệm của mã của bạn ở nơi đầu tiên , thay vào đó nó phải được xử lý bởiargparsemô-đun. Đừng bận tâm viết một câu lệnh if phức tạp, thay vào đó thích thiết lập trình phân tích cú pháp đối số của bạn như thế này:

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

Và vâng, nó nên là một tùy chọn không phải là một đối số vị trí, bởi vì nó là sau tất cả tùy chọn .


đã chỉnh sửa: Để giải quyết mối quan tâm của LarsH trong các bình luận, dưới đây là một ví dụ về cách bạn có thể viết nó nếu bạn chắc chắn rằng bạn muốn giao diện với 3 hoặc 0 đối số vị trí . Tôi cho rằng giao diện trước đó có phong cách tốt hơn, bởi vì các đối số tùy chọn nên là các tùy chọn , nhưng đây là một cách tiếp cận khác để hoàn thiện hơn. Lưu ý kwarg ghi đèusagekhi tạo trình phân tích cú pháp của bạn, bởi vìargparsesẽ tự động tạo một thông báo sử dụng sai lệch!

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

Dưới đây là một số ví dụ sử dụng:

# 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
Vâng, tôi cố ý thêm. Có thể đặt đối số theo vị trí và thực thi rằng chính xác 3 hoặc 0 được tiêu thụ, nhưng nó sẽ không tạo ra CLI tốt vì vậy tôi không khuyến nghị.
wim

8
Vấn đề riêng biệt. Bạn không tin đó là CLI tốt, và bạn có thể tranh luận về điểm đó, và OP có thể bị thuyết phục. Nhưng câu trả lời của bạn đi chệch khỏi câu hỏi đủ đáng kể để thay đổi thông số kỹ thuật cần được đề cập. Bạn dường như đang uốn cong thông số kỹ thuật để phù hợp với công cụ có sẵn, mà không đề cập đến sự thay đổi.
LarsH

2
@LarsH OK, tôi đã thêm một ví dụ phù hợp hơn với giao diện ban đầu được ngụ ý trong câu hỏi. Bây giờ, nó đang bẻ cong công cụ để đáp ứng thông số kỹ thuật có sẵn ...;)
wim

2
Đây là câu trả lời duy nhất tôi nêu lên. +1 để trả lời câu hỏi thực sự .
Jonathon Reinhart

1
+1. Hình thức của CLI là một vấn đề tiếp tuyến quan trọng, không hoàn toàn tách biệt như người khác nói. Tôi ủng hộ bài viết của bạn cũng như những người khác - bài viết của bạn bắt nguồn từ vấn đề và đưa ra một giải pháp tao nhã, trong khi các bài đăng khác trả lời câu hỏi theo nghĩa đen. Và cả hai loại câu trả lời đều hữu ích và xứng đáng +1.
Ben Lee

32

Tôi muốn đi:

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

Tôi nghĩ rằng điều này nên ngắn mạch khá hiệu quả

Giải trình

Bằng cách tạo condsmột iterator, lần sử dụng đầu tiên anysẽ ngắn mạch và để iterator chỉ vào phần tử tiếp theo nếu bất kỳ mục nào là đúng; nếu không, nó sẽ tiêu thụ toàn bộ danh sách và được False. Phần tiếp theo anylấy các mục còn lại trong lần lặp và đảm bảo rằng không có bất kỳ giá trị thực nào khác ... Nếu có, toàn bộ câu lệnh không thể đúng, do đó không có một phần tử duy nhất (vì vậy ngắn mạch lần nữa). Cái cuối cùng anysẽ trở lại Falsehoặc sẽ làm cạn kiệt cái lặp và được True.

lưu ý: kiểm tra ở trên nếu chỉ một điều kiện duy nhất được đặt


Nếu bạn muốn kiểm tra xem một hoặc nhiều mục, nhưng không phải mọi mục đều được đặt, thì bạn có thể sử dụng:

not all(conds) and any(conds)

5
Tôi không hiểu Nó đọc như: nếu đúng và không đúng. Giúp tôi hiểu.
rGil

1
@rGil: nó đọc như "nếu một số táo có màu đỏ và một số thì không" - nó giống như nói "một số táo có màu đỏ, nhưng không phải tất cả chúng".
Georgia

2
Ngay cả với lời giải thích tôi không thể hiểu hành vi ... Không [a, b, c] = [True, True, False]nên mã của bạn "in" False, trong khi đầu ra dự kiến ​​là True?
awesoon

6
Điều này khá thông minh, NHƯNG: Tôi sẽ sử dụng phương pháp này nếu bạn không biết bạn có bao nhiêu điều kiện trước mắt, nhưng đối với một danh sách điều kiện cố định, đã biết, việc mất khả năng đọc chỉ đơn giản là không đáng.
lông mịn

4
Điều này không ngắn mạch. Danh sách này được xây dựng hoàn chỉnh trước khi nó được chuyển đến iter. anyallsẽ lười biếng tiêu thụ danh sách, đúng, nhưng danh sách đã được đánh giá hoàn toàn vào thời điểm bạn đến đó!
icktoofay

22

Câu tiếng anh:

Nếu một hoặc b hoặc c nhưng không phải tất cả trong số họ

Dịch theo logic này:

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

Từ "nhưng" thường bao hàm một từ kết hợp, nói cách khác "và". Bên cạnh đó, "tất cả trong số họ" chuyển đến một kết hợp của các điều kiện: điều kiện này, tình trạng đó, điều kiện khác. "Không" đảo ngược toàn bộ kết hợp.

Tôi không đồng ý rằng câu trả lời được chấp nhận. Tác giả đã bỏ qua việc áp dụng cách giải thích đơn giản nhất cho đặc tả và bỏ qua việc áp dụng Luật De Morgan để đơn giản hóa biểu thức cho ít toán tử hơn:

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

trong khi tuyên bố rằng câu trả lời là "hình thức tối thiểu".


Trên thực tế, hình thức đó là tối thiểu. Đó là dạng PoS tối thiểu cho biểu thức.
Stefano Sanfilippo

10

Điều này trả về Truenếu một và chỉ một trong ba điều kiện là True. Có lẽ những gì bạn muốn trong mã ví dụ của bạn.

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

Không đẹp như câu trả lời của @defuz
jamylak

10

Điều gì về: (điều kiện duy nhất)

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

Lưu ý, nếu bạn cho phép hai điều kiện quá, bạn có thể làm điều đó

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

1
Đối với hồ sơ, câu hỏi yêu cầu hai điều kiện. Ít nhất một, nhưng không phải tất cả trong số họ = 1 trong số tất cả hoặc 2 trong số tất cả
Marius Balčytis

IMHO bạn nên đánh vần cái thứ hai là 1 <= bool(a) + bool(b) + bool(c) <= 2.
Phục hồi lại

6

Để rõ ràng, bạn muốn đưa ra quyết định của mình dựa trên bao nhiêu tham số là TRUE logic (trong trường hợp đối số chuỗi - không trống)?

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

Sau đó, bạn đã đưa ra quyết định:

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

Bây giờ logic đã rõ ràng hơn.


5

Và tại sao không chỉ đếm chúng?

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

Nếu bạn không phiền một chút khó hiểu, bạn có thể mô phỏng theo 0 < (a + b + c) < 3đó sẽ trả về truenếu bạn có từ một đến hai câu lệnh đúng và sai nếu tất cả đều sai hoặc không có gì sai.

Điều này cũng đơn giản hóa nếu bạn sử dụng các hàm để đánh giá các bool vì bạn chỉ đánh giá các biến một lần và điều đó có nghĩa là bạn có thể viết các hàm nội tuyến và không cần lưu trữ tạm thời các biến. (Ví dụ : 0 < ( a(x) + b(x) + c(x) ) < 3.)


4

Câu hỏi nói rằng bạn cần cả ba đối số (a và b và c) hoặc không có đối số nào (không phải (a hoặc b hoặc c))

Điều này mang lại:

(a và b và c) hoặc không (a hoặc b hoặc c)


4

Theo tôi hiểu, bạn có một hàm nhận 3 đối số, nhưng nếu không, nó sẽ chạy trên hành vi mặc định. Vì bạn chưa giải thích điều gì sẽ xảy ra khi 1 hoặc 2 đối số được cung cấp, tôi sẽ cho rằng nó chỉ nên thực hiện hành vi mặc định. Trong trường hợp này, tôi nghĩ bạn sẽ tìm thấy câu trả lời sau rất thuận lợi:

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

Tuy nhiên, nếu bạn muốn 1 hoặc 2 đối số được xử lý khác nhau:

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

lưu ý: Điều này giả định rằng Falsecác giá trị "" sẽ không được truyền vào phương thức này.


kiểm tra giá trị thật của một đối số là một vấn đề khác với việc kiểm tra xem một đối số có mặt hay vắng mặt hay không
wim

@wim Vậy là chuyển đổi một câu hỏi cho phù hợp với câu trả lời của bạn. Mô-đun argparse không liên quan gì đến câu hỏi, nó thêm một lần nhập khác và nếu OP không có kế hoạch sử dụng argparse, nó sẽ không giúp họ được gì. Ngoài ra, nếu "tập lệnh" không phải là độc lập, mà là một mô-đun hoặc một hàm trong một bộ mã lớn hơn, thì nó có thể đã có một trình phân tích cú pháp đối số và chức năng cụ thể này trong tập lệnh lớn hơn đó có thể được mặc định hoặc tùy chỉnh. Do thông tin hạn chế từ OP, tôi không thể biết phương thức này nên hoạt động như thế nào, nhưng sẽ an toàn khi cho rằng OP không vượt qua các phân.
Inbar Rose

Câu hỏi nói rõ ràng "Tôi có một tập lệnh python có thể nhận được 0 hoặc 3 đối số dòng lệnh", nó không nói "Tôi có một hàm nhận 3 đối số". Vì mô-đun argparse là cách ưa thích để xử lý các đối số dòng lệnh trong python, nên nó tự động có mọi thứ để làm với câu hỏi. Cuối cùng, python là "bao gồm pin" - không có bất kỳ nhược điểm nào với "thêm một lần nhập khác" khi mô-đun đó là một phần của các thư viện tiêu chuẩn.
wim

@wim Câu hỏi khá không rõ ràng (ví dụ: cơ thể không khớp với tiêu đề). Tôi nghĩ rằng câu hỏi không đủ rõ ràng rằng đây là một câu trả lời hợp lệ cho một số giải thích về nó.
Phục hồi Monica

2

Nếu bạn làm việc với một trình vòng lặp các điều kiện, nó có thể bị truy cập chậm. Nhưng bạn không cần phải truy cập từng yếu tố nhiều lần và bạn không cần phải luôn đọc tất cả các yếu tố đó. Đây là một giải pháp sẽ hoạt động với các trình tạo vô hạn:

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

Khi mọi thứ đã cho boolTrue, hoặc khi mọi thứ đã cho boolFalse...
tất cả chúng đều bằng nhau!

Vì vậy, chúng ta chỉ cần tìm hai yếu tố đánh giá các bools khác nhau
để biết rằng có ít nhất một Truevà ít nhất một False.

Giải pháp ngắn gọn của tôi:

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

Tôi tin rằng nó ngắn mạch, gây ra AFAIK a==b==cbằng a==b and b==c.

Giải pháp tổng quát của tôi:

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)

Tôi cũng đã viết một số mã xử lý nhiều lần lặp, nhưng tôi đã xóa nó từ đây vì tôi nghĩ nó vô nghĩa. Tuy nhiên nó vẫn có sẵn ở đây .


-2

Về cơ bản, đây là chức năng "một số (nhưng không phải tất cả)" (khi tương phản với any()all() được xây dựng trong chức năng).

Điều này ngụ ý rằng nên có Falses True s trong số các kết quả. Do đó, bạn có thể làm như sau:

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)

Một lợi thế của mã này là bạn chỉ cần lặp lại một lần thông qua các mục kết quả (booleans).

Một nhược điểm là tất cả các biểu thức sự thật này luôn được đánh giá và không thực hiện đoản mạch như các toán tử or/ and.


1
Tôi nghĩ rằng đây là biến chứng không cần thiết. Tại sao một froundredet thay vì một bộ cũ đơn giản? Tại sao .issupersetthay vì chỉ kiểm tra độ dài 2, dù sao boolcũng không thể trả lại bất cứ điều gì khác ngoài Đúng và Sai. Tại sao gán lambda (đọc: hàm ẩn danh) cho một tên thay vì chỉ sử dụng def?
wim

1
cú pháp lambda hợp lý hơn đối với một số người. dù sao chúng cũng có cùng độ dài vì bạn cần returnnếu bạn sử dụng def. Tôi nghĩ rằng tổng quát của giải pháp này là tốt đẹp. Không cần thiết phải giới hạn bản thân vào booleans, câu hỏi cơ bản là "làm thế nào để tôi đảm bảo tất cả các yếu tố này xảy ra trong danh sách của mình". Tại sao setnếu bạn không cần sự biến đổi? bất biến hơn luôn luôn tốt hơn nếu bạn không cần hiệu suất.
Janus Troelsen

@JanusTroelsen Bạn đã đúng mục tiêu! Đây là một số lý do tại sao tôi làm theo cách này; nó làm cho nó dễ dàng và rõ ràng hơn với tôi. Tôi có xu hướng thích ứng Python theo cách mã hóa của mình :-).
Abbafei

nhưng nó sẽ không hoạt động trên các máy phát vô hạn: P xem câu trả lời của tôi :) gợi ý:tee
Janus Troelsen

@JanusTroelsen Tôi nhận ra điều này :-). Lúc đầu, tôi thực sự đã có nó theo cách khác (với True / false trong tập hợp và lặp lại trong param param), nhưng nhận ra rằng điều này sẽ không hoạt động với các trình tạo vô hạn và người dùng có thể không nhận ra (vì thực tế điều này không phải là (chưa) được đề cập trong các tài liệu cho các tham số phương thức tập lặp) và ít nhất như thế này rõ ràng là nó sẽ không lấy các trình lặp vô hạn. Tôi đã biết itertools.teenhưng 1) Tôi đang tìm kiếm một lớp lót đơn giản / đủ nhỏ để đảm bảo dán sao chép, 2) bạn đã đưa ra câu trả lời sử dụng kỹ thuật đó :-)
Abbafei
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.