X [x <2] = 0 có nghĩa là gì trong Python?


85

Tôi đã xem qua một số mã có dòng tương tự như

x[x<2]=0

Chơi với các biến thể, tôi vẫn còn bị mắc kẹt về những gì cú pháp này làm.

Ví dụ:

>>> x = [1,2,3,4,5]
>>> x[x<2]
1
>>> x[x<3]
1
>>> x[x>2]
2
>>> x[x<2]=0
>>> x
[0, 2, 3, 4, 5]

7
không bao giờ có ý nghĩa khi làm điều này với một danh sách.
dbliss

12
Điều này chỉ có ý nghĩa với mảng NumPy hoặc các đối tượng tương tự, hoạt động hoàn toàn khác với hành vi trong thử nghiệm của bạn hoặc hành vi dựa trên danh sách được giải thích trong một trong hai câu trả lời.
user2357112 hỗ trợ Monica

11
Lưu ý, điều này không hoạt động trong Python 3. Các loại chỉ có thể được so sánh khi so sánh có ý nghĩa. Trong Python 3, ví dụ này ném TypeError: unorderable types: list() < int().
Morgan Thrapp

2
Quá ít thông tin. Nên đã đề cập rằng mảng này là một mảng không rõ ràng.
lmaooooo

3
Tôi bị sốc vì điều này có rất nhiều phiếu tán thành (mặc dù đó thực sự là một câu hỏi hay cho định dạng SO).
PascalVKooten

Câu trả lời:


120

Điều này chỉ có ý nghĩa với mảng NumPy . Hành vi với danh sách là vô dụng và dành riêng cho Python 2 (không phải Python 3). Bạn có thể muốn kiểm tra lại xem đối tượng ban đầu có thực sự là một mảng NumPy (xem thêm bên dưới) và không phải là một danh sách.

Nhưng trong mã của bạn ở đây, x là một danh sách đơn giản.

Từ

x < 2

là Sai tức là 0, do đó

x[x<2]x[0]

x[0] được thay đổi.

Ngược lại, x[x>2]x[True]hoặcx[1]

Vì vậy, x[1]được thay đổi.

Lý do tại sao điều này xảy ra?

Các quy tắc để so sánh là:

  1. Khi bạn đặt hàng hai chuỗi hoặc hai kiểu số, thứ tự được thực hiện theo cách mong đợi (thứ tự từ vựng cho chuỗi, thứ tự số cho số nguyên).

  2. Khi bạn đặt hàng kiểu số và không phải kiểu số, kiểu số sẽ xuất hiện trước.

  3. Khi bạn đặt hàng hai loại không tương thích mà không phải là số, chúng được sắp xếp theo thứ tự bảng chữ cái của tên loại của chúng:

Vì vậy, chúng tôi có đơn đặt hàng sau

số <danh sách <chuỗi <tuple

Xem câu trả lời được chấp nhận cho Python so sánh chuỗi và int như thế nào? .

Nếu x là một mảng NumPy , thì cú pháp có ý nghĩa hơn vì lập chỉ mục mảng boolean . Trong trường hợp đó, x < 2không phải là boolean; đó là một mảng boolean đại diện cho việc mỗi phần tử xcó nhỏ hơn 2. x[x < 2] = 0sau đó chọn các phần tử xnhỏ hơn 2 và đặt các ô đó thành 0. Xem Lập chỉ mục .

>>> x = np.array([1., -1., -2., 3])
>>> x < 0
array([False,  True,  True, False], dtype=bool)
>>> x[x < 0] += 20   # All elements < 0 get increased by 20
>>> x
array([  1.,  19.,  18.,   3.]) # Only elements < 0 are affected

11
Cho rằng OP đặc biệt nói rằng "Tôi đã xem một số mã như thế này ...", tôi nghĩ câu trả lời của bạn mô tả lập chỉ mục boolean numpy rất hữu ích - có thể đáng để chỉ ra rằng nếu OP cuộn lên mã mà họ đã xem, họ ' Tôi gần như chắc chắn sẽ thấy một importcho numpy.
J Richard Snape

2
Vẫn là một cách làm quá thông minh phải không? (So ​​sánh với, nói [0 if i < 2 else i for i in x],.) Hay đây là phong cách được khuyến khích trong Numpy?
Tim Pederick

6
@TimPederick: Sử dụng khả năng hiểu danh sách với NumPy là một ý tưởng khá tồi. Nó chậm hơn hàng chục đến hàng trăm lần, nó không hoạt động với các mảng có chiều tùy ý, dễ dàng hơn để vặn các loại phần tử và nó tạo ra một danh sách thay vì một mảng. Việc lập chỉ mục mảng Boolean là hoàn toàn bình thường và được mong đợi trong NumPy.
user2357112 hỗ trợ Monica

@TimPederick Ngoài hiệu suất đạt được, có khả năng ai đã viết mã có ý định tiếp tục sử dụng một mảng numpy. x[x<2]sẽ trả về một mảng numpy, trong khi [0 if i<2 else i for i in x]trả về một danh sách. Điều này là do x[x<2]là một hoạt động lập chỉ mục (được gọi trong numpy / scipy / pandas là một hoạt động cắt do khả năng che giấu dữ liệu), trong khi khả năng hiểu danh sách là một định nghĩa đối tượng mới. Xem lập chỉ mục NumPy
Michael Delgado

45
>>> x = [1,2,3,4,5]
>>> x<2
False
>>> x[False]
1
>>> x[True]
2

Bool chỉ được chuyển đổi thành một số nguyên. Chỉ số là 0 hoặc 1.


7
Bạn có thể đề cập đến điều đó x2được " sắp xếp nhất quán nhưng tùy ý " và thứ tự có thể thay đổi trong các triển khai Python khác nhau.
Robᵩ

2
Tôi cũng muốn nói thêm rằng đây là một cách làm thông minh và theo tôi nên tránh. Hãy làm điều đó một cách rõ ràng - việc OP phải hỏi câu hỏi này ủng hộ quan điểm của tôi.
kratenko

11
bạn có thể bổ sung thêm chi tiết, tại sao x<2 == false?
Iłya Bursov

15
boolkhông được chuyển đổi sang một số nguyên, một booltrong Python là một số nguyên
Antti Haapala

2
Chỉ để làm rõ tuyên bố của @ AnttiHaapala cho bất kỳ ai khác đi cùng, bool là một lớp con của int.
porglezomp

14

Mã gốc trong câu hỏi của bạn chỉ hoạt động trong Python 2. Nếu xlà một listtrong Python 2, thì so sánh x < yFalsenếu ylà một integer. Điều này là do việc so sánh một danh sách với một số nguyên không có ý nghĩa. Tuy nhiên trong Python 2, nếu các toán hạng không thể so sánh được, thì việc so sánh dựa trên CPython dựa trên thứ tự bảng chữ cái của tên các loại ; Ngoài ra, tất cả các số đều đứng đầu trong các so sánh kiểu hỗn hợp . Điều này thậm chí còn không được nêu trong tài liệu của CPython 2 và các triển khai Python 2 khác nhau có thể cho các kết quả khác nhau. Điều đó được [1, 2, 3, 4, 5] < 2đánh giá là False2là một số và do đó "nhỏ hơn" so với một listtrong CPython. Sự so sánh hỗn hợp này cuối cùng đãđược coi là một tính năng quá tối nghĩa và đã bị loại bỏ trong Python 3.0.


Bây giờ, kết quả <là a bool; và boollà một lớp con củaint :

>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1
True
>>> False + 5
5
>>> True + 5
6

Vì vậy, về cơ bản bạn đang lấy phần tử 0 hoặc 1 tùy thuộc vào việc so sánh là đúng hay sai.


Nếu bạn thử mã ở trên bằng Python 3, bạn sẽ nhận được TypeError: unorderable types: list() < int()do sự thay đổi trong Python 3.0 :

So sánh đơn hàng

Python 3.0 đã đơn giản hóa các quy tắc để so sánh thứ tự:

Các nhà khai thác đặt hàng so sánh ( <, <=, >=, >) nâng cao một TypeErrorngoại lệ khi các toán hạng không có một trật tự tự nhiên có ý nghĩa. Do đó, các biểu thức như 1 < '', 0 > Nonehoặc len <= lenkhông còn hợp lệ, và ví dụ như None < Nonetăng lên TypeErrorthay vì trả về False. Một hệ quả tất yếu là việc sắp xếp một danh sách không đồng nhất không còn hợp lý nữa - tất cả các phần tử phải được so sánh với nhau. Lưu ý rằng điều này không áp dụng cho toán tử ==and !=: các đối tượng thuộc các kiểu không thể so sánh khác nhau luôn so sánh không bình đẳng với nhau.


Có nhiều kiểu dữ liệu làm quá tải các toán tử so sánh để thực hiện một điều gì đó khác biệt (khung dữ liệu từ gấu trúc, mảng của numpy). Nếu mã mà bạn đang sử dụng đã làm cái gì khác, đó là vì xđã không phải là mộtlist , nhưng một thể hiện của một số lớp khác với nhà điều hành <ghi đè để trả về một giá trị mà không phải là một bool; và giá trị này sau đó đã được xử lý đặc biệt bởi x[](aka __getitem__/ __setitem__)


6
+FalseXin chào Perl, xin chào JavaScript, bạn khỏe không?
cat

@cat trong Javascript, Perl, nó chuyển đổi giá trị dưới dạng số. Trong Python nó là dành cho UNARY_POSITIVEopcode mà các cuộc gọi__pos__
Antti Haapala

Tôi nghĩ bạn muốn nói __setitem__thay vì __getitem__trong phần cuối cùng của bạn. Ngoài ra, tôi hy vọng bạn không phiền vì câu trả lời của tôi được lấy cảm hứng từ phần câu trả lời của bạn.
MSeifert

Không, tôi muốn nói và đã nghĩ đến việc __getitem__mặc dù không kém có thể là __setitem____delitem__
Antti Haapala

9

Cái này có một công dụng nữa: chơi gôn mã. Code golf là nghệ thuật viết các chương trình giải quyết một số vấn đề với càng ít byte mã nguồn càng tốt.

return(a,b)[c<d]

gần tương đương với

if c < d:
    return b
else:
    return a

ngoại trừ việc cả a và b đều được đánh giá trong phiên bản đầu tiên, nhưng không được đánh giá trong phiên bản thứ hai.

c<dđánh giá Truehoặc False.
(a, b)là một tuple.
Lập chỉ mục trên tuple hoạt động giống như lập chỉ mục trên danh sách: (3,5)[1]== 5.
Truelà bằng 1Falsebằng với 0.

  1. (a,b)[c<d]
  2. (a,b)[True]
  3. (a,b)[1]
  4. b

hoặc cho False:

  1. (a,b)[c<d]
  2. (a,b)[False]
  3. (a,b)[0]
  4. a

Có một danh sách tốt trên mạng trao đổi ngăn xếp gồm nhiều thứ khó chịu mà bạn có thể làm với python để tiết kiệm một vài byte. /codegolf/54/tips-for-golfing-in-python

Mặc dù trong mã thông thường, điều này không bao giờ nên được sử dụng và trong trường hợp của bạn, nó có nghĩa là nó xhoạt động như một thứ có thể được so sánh với một số nguyên và như một vùng chứa hỗ trợ cắt, đây là một sự kết hợp rất bất thường. Nó có thể là mã Numpy, như những người khác đã chỉ ra.


6
Code Golf is the art of writing programs: ')
cat

1
Nitpick nhỏ: Các bool không đúc đến một int, nó chỉ một (xem câu trả lời khác)
mèo

6

Nói chung nó có thể có nghĩa là bất cứ điều gì . Nó đã giải thích ý nghĩa của nó nếu xlà một listhay numpy.ndarraynhưng nói chung nó chỉ phụ thuộc vào cách các toán tử so sánh ( <, >, ...) và cũng là get / set-item (cách [...]-syntax) được thực hiện.

x.__getitem__(x.__lt__(2))      # this is what x[x < 2] means!
x.__setitem__(x.__lt__(2), 0)   # this is what x[x < 2] = 0 means!

Bởi vì:

  • x < value tương đương với x.__lt__(value)
  • x[value] tương đương với x.__getitem__(value)
  • x[value] = othervaluelà (cũng gần đúng) tương đương với x.__setitem__(value, othervalue).

Điều này có thể được tùy chỉnh để làm bất cứ điều gì bạn muốn. Chỉ là một ví dụ (bắt chước lập chỉ mục numpys-boolean một chút):

class Test:
    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        # You could do anything in here. For example create a new list indicating if that 
        # element is less than the other value
        res = [item < other for item in self.value]
        return self.__class__(res)

    def __repr__(self):
        return '{0} ({1})'.format(self.__class__.__name__, self.value)

    def __getitem__(self, item):
        # If you index with an instance of this class use "boolean-indexing"
        if isinstance(item, Test):
            res = self.__class__([i for i, index in zip(self.value, item) if index])
            return res
        # Something else was given just try to use it on the value
        return self.value[item]

    def __setitem__(self, item, value):
        if isinstance(item, Test):
            self.value = [i if not index else value for i, index in zip(self.value, item)]
        else:
            self.value[item] = value

Vì vậy, bây giờ hãy xem điều gì sẽ xảy ra nếu bạn sử dụng nó:

>>> a = Test([1,2,3])
>>> a
Test ([1, 2, 3])
>>> a < 2  # calls __lt__
Test ([True, False, False])
>>> a[Test([True, False, False])] # calls __getitem__
Test ([1])
>>> a[a < 2] # or short form
Test ([1])

>>> a[a < 2] = 0  # calls __setitem__
>>> a
Test ([0, 2, 3])

Lưu ý rằng đây chỉ là một khả năng. Bạn có thể tự do thực hiện hầu hết mọi thứ bạn muốn.


Tôi sẽ nói rằng việc sử dụng bất cứ thứ gì thực sự là quá chung chung đối với hành vi có thể giải thích một cách hợp lý như câu trả lời được chấp nhận.
PascalVKooten

@PascalvKooten Bạn không đồng ý với "bất cứ điều gì" hoặc với câu trả lời tổng quát? Tôi nghĩ rằng đó là một điểm quan trọng cần thực hiện vì hầu hết các hành vi hợp lý trong python chỉ là theo quy ước.
MSeifert
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.