'và' (boolean) so với '&' (bitwise) - Tại sao sự khác biệt trong hành vi với danh sách so với mảng numpy?


142

Điều gì giải thích sự khác biệt trong hành vi của các phép toán boolean và bitwise trên danh sách so với mảng NumPy?

Tôi bối rối về việc sử dụng &vs andtrong Python thích hợp , được minh họa trong các ví dụ sau.

mylist1 = [True,  True,  True, False,  True]
mylist2 = [False, True, False,  True, False]

>>> len(mylist1) == len(mylist2)
True

# ---- Example 1 ----
>>> mylist1 and mylist2
[False, True, False, True, False]
# I would have expected [False, True, False, False, False]

# ---- Example 2 ----
>>> mylist1 & mylist2
TypeError: unsupported operand type(s) for &: 'list' and 'list'
# Why not just like example 1?

>>> import numpy as np

# ---- Example 3 ----
>>> np.array(mylist1) and np.array(mylist2)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
# Why not just like Example 4?

# ---- Example 4 ----
>>> np.array(mylist1) & np.array(mylist2)
array([False,  True, False, False, False], dtype=bool)
# This is the output I was expecting!

Câu trả lời nàycâu trả lời này đã giúp tôi hiểu rằng đó andlà một phép toán boolean nhưng &là một phép toán bitwise.

Tôi đọc về các hoạt động bitwise để hiểu rõ hơn về khái niệm này, nhưng tôi đang vật lộn để sử dụng thông tin đó để hiểu ý nghĩa của 4 ví dụ trên của tôi.

Ví dụ 4 dẫn tôi đến đầu ra mong muốn của tôi, vì vậy đó là tốt, nhưng tôi vẫn còn nhầm lẫn về thời gian / cách / tại sao tôi nên sử dụng andvs &. Tại sao danh sách và mảng NumPy hoạt động khác nhau với các toán tử này?

Bất cứ ai có thể giúp tôi hiểu sự khác biệt giữa các phép toán boolean và bitwise để giải thích lý do tại sao chúng xử lý các danh sách và mảng NumPy khác nhau?


2
Trong NumPy có np.bitwise_and()np.logical_and()và bạn bè để tránh nhầm lẫn.
Dietrich

1
Trong ví dụ 1, mylist1 and mylist2không đưa ra kết quả giống như mylist2 and mylist1, vì những gì đang được trả về là danh sách thứ hai như được chỉ ra bởi delnan.
dùng2015487

Câu trả lời:


113

andkiểm tra cho dù cả hai biểu thức là một cách logic Truetrong khi &(khi được sử dụng với True/ Falsegiá trị) kiểm tra nếu cả hai đều True.

Trong Python, các đối tượng tích hợp trống thường được coi là logic Falsetrong khi các đối tượng tích hợp không trống là logic True. Điều này tạo điều kiện cho trường hợp sử dụng phổ biến trong đó bạn muốn làm một cái gì đó nếu một danh sách trống và một cái gì đó khác nếu danh sách không có. Lưu ý rằng điều này có nghĩa là danh sách [Sai] là hợp lý True:

>>> if [False]:
...    print 'True'
...
True

Vì vậy, trong ví dụ 1, danh sách đầu tiên không trống và do đó về mặt logic True, do đó, giá trị thật của anddanh sách này giống với danh sách thứ hai. (Trong trường hợp của chúng tôi, danh sách thứ hai không trống và do đó về mặt logic True, nhưng việc xác định sẽ yêu cầu một bước tính toán không cần thiết.)

Ví dụ 2, các danh sách không thể được kết hợp một cách có ý nghĩa theo kiểu bitwise vì chúng có thể chứa các phần tử không giống nhau tùy ý. Những thứ có thể được kết hợp theo bitwise bao gồm: Trues và Falses, số nguyên.

Đối tượng NumPy, ngược lại, hỗ trợ tính toán véc tơ. Đó là, họ cho phép bạn thực hiện các hoạt động tương tự trên nhiều mẩu dữ liệu.

Ví dụ 3 thất bại vì mảng NumPy (có độ dài> 1) không có giá trị thật vì điều này ngăn ngừa sự nhầm lẫn logic dựa trên vectơ.

Ví dụ 4 chỉ đơn giản là một andhoạt động bit vector .

Dòng dưới cùng

  • Nếu bạn không xử lý các mảng và không thực hiện các thao tác toán học của các số nguyên, có lẽ bạn muốn and.

  • Nếu bạn có các vectơ của các giá trị thật mà bạn muốn kết hợp, hãy sử dụng numpyvới &.


26

Trong khoảng list

Đầu tiên một điểm rất quan trọng, từ đó mọi thứ sẽ theo sau (tôi hy vọng).

Trong Python thông thường, listkhông có gì đặc biệt (ngoại trừ việc có cú pháp dễ thương để xây dựng, phần lớn là một tai nạn lịch sử). Khi một danh sách [3,2,6]được tạo, nó dành cho tất cả các ý định và mục đích chỉ là một đối tượng Python thông thường, như một số 3, tập hợp {3,7}hoặc một hàm lambda x: x+5.

(Vâng, nó hỗ trợ thay đổi các yếu tố của nó, và nó hỗ trợ lặp lại, và nhiều thứ khác, nhưng đó chỉ là một loại: nó hỗ trợ một số hoạt động, trong khi không hỗ trợ một số hoạt động khác. làm cho nó trở nên rất đặc biệt - đó chỉ là một int. Lambda hỗ trợ gọi, nhưng điều đó không làm cho nó trở nên rất đặc biệt - đó là những gì lambda dành cho, sau tất cả :).

Trong khoảng and

andkhông phải là một toán tử (bạn có thể gọi nó là "toán tử", nhưng bạn cũng có thể gọi "cho" một toán tử :). Các toán tử trong Python là các phương thức (được thực hiện thông qua) được gọi trên các đối tượng thuộc loại nào đó, thường được viết như một phần của loại đó. Không có cách nào để một phương thức tổ chức đánh giá một số toán hạng của nó, nhưng andcó thể (và phải) làm điều đó.

Hậu quả của điều đó là andkhông thể bị quá tải, giống như forkhông thể bị quá tải. Nó hoàn toàn chung chung và giao tiếp thông qua một giao thức được chỉ định. Những gì bạn có thể làm là tùy chỉnh một phần của giao thức, nhưng điều đó không có nghĩa là bạn có thể thay đổi andhoàn toàn hành vi . Giao thức là:

Tưởng tượng Python diễn giải "a và b" (điều này không xảy ra theo nghĩa đen theo cách này, nhưng nó giúp hiểu). Khi nói đến "và", nó nhìn vào đối tượng mà nó vừa đánh giá (a) và hỏi nó: bạn có đúng không? ( KHÔNG : bạn có phải Truekhông?) Nếu bạn là tác giả của một lớp, bạn có thể tùy chỉnh câu trả lời này. Nếu acâu trả lời là "không", and(bỏ qua b hoàn toàn, nó hoàn toàn không được đánh giá và) nói: alà kết quả của tôi ( KHÔNG : Sai là kết quả của tôi).

Nếu akhông trả lời, hãy andhỏi nó: chiều dài của bạn là bao nhiêu? (Một lần nữa, bạn có thể tùy chỉnh điều này với tư cách là tác giả của alớp). Nếu acâu trả lời 0, andthực hiện tương tự như trên - coi đó là sai ( KHÔNG sai), bỏ qua b và đưa ra akết quả.

Nếu acâu trả lời khác 0 cho câu hỏi thứ hai ("chiều dài của bạn là bao nhiêu") hoặc nó không trả lời gì cả, hoặc nó trả lời "có" cho câu hỏi thứ nhất ("bạn có đúng không"), hãy andđánh giá b và nói: blà kết quả của tôi. Lưu ý rằng nó KHÔNG hỏi bbất kỳ câu hỏi.

Một cách khác để nói tất cả những điều này a and blà gần giống như b if a else a, ngoại trừ a chỉ được đánh giá một lần.

Bây giờ hãy ngồi trong vài phút với bút và giấy và tự thuyết phục bản thân rằng khi {a, b} là tập con của {Đúng, Sai}, nó hoạt động chính xác như bạn mong đợi của các toán tử Boolean. Nhưng tôi hy vọng tôi đã thuyết phục bạn rằng nó chung chung hơn nhiều, và như bạn sẽ thấy, theo cách này hữu ích hơn nhiều.

Đặt hai cái đó lại với nhau

Bây giờ tôi hy vọng bạn hiểu ví dụ của bạn 1. andkhông quan tâm nếu mylist1 là một số, danh sách, lambda hoặc một đối tượng của một lớp Argmhbl. Nó chỉ quan tâm đến câu trả lời của mylist1 cho các câu hỏi của giao thức. Và tất nhiên, mylist1 trả lời 5 cho câu hỏi về độ dài, và trả về mylist2. Và đó là nó. Nó không liên quan gì đến các yếu tố của mylist1 và mylist2 - họ không nhập hình ảnh vào bất cứ đâu.

Ví dụ thứ hai: &trênlist

Mặt khác, &là một toán tử như bất kỳ khác, +ví dụ như. Nó có thể được định nghĩa cho một kiểu bằng cách định nghĩa một phương thức đặc biệt trên lớp đó. intđịnh nghĩa nó là bitwise "và", và bool định nghĩa nó là logic "và", nhưng đó chỉ là một tùy chọn: ví dụ: tập hợp và một số đối tượng khác như chế độ xem khóa chính xác định nó là giao điểm đã đặt. listchỉ không định nghĩa nó, có lẽ bởi vì Guido đã không nghĩ ra bất kỳ cách định nghĩa rõ ràng nào.

numpy

Mặt khác: -D, mảng numpy đặc biệt, hoặc ít nhất là chúng đang cố gắng để được. Tất nhiên, numpy.array chỉ là một lớp, nó không thể ghi đè andbằng bất kỳ cách nào, vì vậy nó sẽ làm điều tốt nhất tiếp theo: khi được hỏi "bạn có đúng không", numpy.array đưa ra ValueError, nói một cách hiệu quả "vui lòng viết lại câu hỏi, của tôi quan điểm về sự thật không phù hợp với mô hình của bạn ". (Lưu ý rằng thông điệp ValueError không nói về and- bởi vì numpy.array không biết ai đang hỏi nó câu hỏi; nó chỉ nói về sự thật.)

Đối với &, đó là câu chuyện hoàn toàn khác. numpy.array có thể định nghĩa nó theo ý muốn và nó định nghĩa &nhất quán với các toán tử khác: theo chiều. Vì vậy, cuối cùng bạn đã có được những gì bạn muốn.

HTH


23

Các toán tử boolean ngắn mạch ( and, or) không thể bị ghi đè bởi vì không có cách nào thỏa mãn để làm điều này mà không giới thiệu các tính năng ngôn ngữ mới hoặc hy sinh ngắn mạch. Như bạn có thể biết hoặc không, họ đánh giá toán hạng thứ nhất cho giá trị thật của nó và tùy thuộc vào giá trị đó, đánh giá và trả về đối số thứ hai hoặc không đánh giá đối số thứ hai và trả về đối số thứ nhất:

something_true and x -> x
something_false and x -> something_false
something_true or x -> something_true
something_false or x -> x

Lưu ý rằng (kết quả đánh giá) toán hạng thực tế được trả về, không phải giá trị thật của chúng.

Cách duy nhất để tùy chỉnh hành vi của họ là ghi đè __nonzero__(được đổi tên thành __bool__Python 3), do đó bạn có thể ảnh hưởng đến toán hạng nào được trả về, nhưng không trả về một cái gì đó khác. Danh sách (và các bộ sưu tập khác) được định nghĩa là "trung thực" khi chúng chứa bất cứ thứ gì và "falsey" khi chúng trống.

Mảng NumPy bác bỏ quan niệm đó: Đối với các trường hợp sử dụng mà chúng nhắm đến, hai khái niệm khác nhau về sự thật là phổ biến: (1) Cho dù bất kỳ yếu tố nào là đúng và (2) liệu tất cả các yếu tố có đúng không. Vì hai cái này hoàn toàn (và âm thầm) không tương thích, và rõ ràng không chính xác hơn hoặc phổ biến hơn, NumPy từ chối đoán và yêu cầu bạn sử dụng rõ ràng .any()hoặc .all().

&|(và not, nhân tiện) có thể được ghi đè hoàn toàn, vì chúng không bị đoản mạch. Họ có thể trả lại bất cứ thứ gì khi overriden và NumPy sử dụng tốt điều đó để thực hiện các thao tác phần tử, giống như thực tế với bất kỳ hoạt động vô hướng nào khác. Danh sách, mặt khác, không phát sóng hoạt động trên các yếu tố của họ. Cũng như mylist1 - mylist2không có nghĩa gì cả và mylist1 + mylist2có nghĩa là một cái gì đó hoàn toàn khác, không có &toán tử cho danh sách.


3
Một ví dụ đặc biệt thú vị về những gì điều này có thể tạo ra được [False] or [True]đánh giá [False][False] and [True]đánh giá [True].
Rob Watts

16

Ví dụ 1:

Đây là cách toán tử hoạt động.

xy => nếu x sai, thì x , khác y

Vì vậy, nói cách khác, vì mylist1không phải False, kết quả của biểu thức là mylist2. (Chỉ các danh sách trống ước tính False.)

Ví dụ 2:

Các &nhà điều hành là một Bitwise và, như bạn đề cập. Hoạt động bitwise chỉ hoạt động trên số. Kết quả của a & b là một số gồm 1s trong các bit là 1 trong cả ab . Ví dụ:

>>> 3 & 1
1

Dễ dàng hơn để xem những gì đang xảy ra bằng cách sử dụng một chữ nhị phân (cùng số như trên):

>>> 0b0011 & 0b0001
0b0001

Các hoạt động của bitwise tương tự như khái niệm với các hoạt động boolean (sự thật), nhưng chúng chỉ hoạt động trên các bit.

Vì vậy, đưa ra một vài tuyên bố về chiếc xe của tôi

  1. Xe của tôi màu đỏ
  2. Xe của tôi có bánh xe

Logic "và" của hai câu lệnh này là:

(xe của tôi có màu đỏ không?) và (xe có bánh xe không?) => logic đúng với giá trị sai

Cả hai đều đúng, ít nhất là cho chiếc xe của tôi. Vì vậy, giá trị của báo cáo kết quả như một toàn thể là một cách logic đúng.

Bitwise "và" của hai câu lệnh này hơi mơ hồ hơn:

(giá trị số của câu lệnh 'xe của tôi màu đỏ') & (giá trị số của câu lệnh 'xe của tôi có bánh xe') => số

Nếu python biết cách chuyển đổi các câu lệnh thành các giá trị số, thì nó sẽ làm như vậy và tính toán theo bitwise - và của hai giá trị. Điều này có thể khiến bạn tin rằng &có thể hoán đổi cho nhau and, nhưng như với ví dụ trên, chúng là những thứ khác nhau. Ngoài ra, đối với các đối tượng không thể chuyển đổi, bạn sẽ chỉ nhận được một TypeError.

Ví dụ 3 và 4:

Numpy thực hiện các phép toán số học cho các mảng:

Các phép toán số học và so sánh trên ndarrays được định nghĩa là các phép toán phần tử và thường mang lại các đối tượng ndarray làm kết quả.

Nhưng không triển khai các hoạt động logic cho mảng, bởi vì bạn không thể quá tải các toán tử logic trong python . Đó là lý do tại sao ví dụ ba không hoạt động, nhưng ví dụ bốn thì không.

Vì vậy, để trả lời andvs &câu hỏi của bạn : Sử dụng and.

Các hoạt động bitwise được sử dụng để kiểm tra cấu trúc của một số (bit nào được đặt, bit nào không được đặt). Loại thông tin này chủ yếu được sử dụng trong các giao diện hệ điều hành cấp thấp ( ví dụ các bit cho phép unix ). Hầu hết các chương trình python sẽ không cần phải biết điều đó.

Các phép toán logic ( and, or, not), tuy nhiên, được sử dụng tất cả các thời gian.


14
  1. Trong Python một biểu thức X and Ytrả về Y, cho rằng bool(X) == Truehoặc bất kỳ Xhoặc Yđánh giá nào thành Sai, ví dụ:

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
    
  2. Toán tử bitwise đơn giản là không được xác định cho danh sách. Nhưng nó được định nghĩa cho các số nguyên - hoạt động trên biểu diễn nhị phân của các số. Xem xét 16 (01000) và 31 (11111):

    16 & 31
    >>> 16
    
  3. NumPy không phải là một nhà ngoại cảm, nó không biết, liệu bạn có nghĩa là ví dụ đó [False, False]phải bằng Truetrong một biểu thức logic. Trong phần này, nó ghi đè một hành vi Python chuẩn, đó là: "Bất kỳ bộ sưu tập trống nào len(collection) == 0False".

  4. Có lẽ là một hành vi dự kiến ​​của nhà điều hành & mảng của NumPy.


Sai và 20 trả về Sai
Rahul

4

Đối với ví dụ đầu tiên và dựa trên tài liệu của django
Nó sẽ luôn trả về danh sách thứ hai, thực sự danh sách không trống được xem là giá trị True cho Python do đó python trả về giá trị True 'cuối cùng' vì vậy danh sách thứ hai

In [74]: mylist1 = [False]
In [75]: mylist2 = [False, True, False,  True, False]
In [76]: mylist1 and mylist2
Out[76]: [False, True, False, True, False]
In [77]: mylist2 and mylist1
Out[77]: [False]

4

Các hoạt động với danh sách Python hoạt động trên danh sách . list1 and list2sẽ kiểm tra xem list1có trống không và trả về list1nếu có và list2nếu không. list1 + list2sẽ nối list2vào list1, để bạn có được một danh sách mới với len(list1) + len(list2)các yếu tố.

Các toán tử chỉ có ý nghĩa khi áp dụng phần tử khôn ngoan, chẳng hạn như &, nâng cao TypeError, vì các hoạt động khôn ngoan của phần tử không được hỗ trợ mà không lặp qua các phần tử.

Mảng Numpy hỗ trợ các hoạt động yếu tố khôn ngoan . array1 & array2sẽ tính toán bitwise hoặc cho từng phần tử tương ứng trong array1array2. array1 + array2sẽ tính tổng cho từng phần tử tương ứng trong array1array2.

Điều này không làm việc cho andor.

array1 and array2 về cơ bản là một cách viết tắt cho đoạn mã sau:

if bool(array1):
    return array2
else:
    return array1

Đối với điều này, bạn cần một định nghĩa tốt về bool(array1). Đối với các hoạt động toàn cầu như được sử dụng trong danh sách Python, định nghĩa là bool(list) == Truenếu listkhông trống và Falsenếu nó trống. Đối với các hoạt động khôn ngoan của phần tử, có một sự không rõ ràng về việc kiểm tra xem có bất kỳ yếu tố nào đánh giá Truehay tất cả các yếu tố đánh giá True. Bởi vì cả hai đều được cho là chính xác, numpy không đoán và tăng ValueErrorthời điểm bool()(gián tiếp) được gọi trên một mảng.


0

Câu hỏi hay. Tương tự như quan sát bạn có về các ví dụ 1 và 4 (hoặc tôi nên nói 1 & 4 :)) trên các andtoán &tử bitwise logic , tôi đã trải nghiệm về sumtoán tử. Numpy sumvà py sumhành xử khác nhau là tốt. Ví dụ:

Giả sử "mat" là một mảng 5x5 2d gọn gàng, chẳng hạn như:

array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20],
       [21, 22, 23, 24, 25]])

Sau đó numpy.sum (mat) cho tổng tổng của toàn bộ ma trận. Trong khi đó tổng tích hợp từ Python, chẳng hạn như tổng (mat) chỉ dọc theo trục. Xem bên dưới:

np.sum(mat)  ## --> gives 325
sum(mat)     ## --> gives array([55, 60, 65, 70, 75])
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.