2016-05-10 16 views
16

Python cho phép các biểu thức như x > y > z, theo tài liệu, tương đương với (x > y) and (y > z) ngoại trừ y chỉ được đánh giá một lần. (https://docs.python.org/3/reference/expressions.html)So sánh chuỗi tùy chỉnh

Tuy nhiên, điều này dường như bị hỏng nếu tôi tùy chỉnh các hàm so sánh. Ví dụ. giả sử tôi có các lớp sau: (. Xin lỗi cho khối lớn, nhưng một khi bạn đọc các phương pháp __eq__, phần còn lại là tầm thường)

class CompareList(list): 
    def __repr__(self): 
     return "CompareList([" + ",".join(str(x) for x in self) + "])" 

    def __eq__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] == other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x == other for x in self) 

    def __ne__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] != other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x != other for x in self) 

    def __gt__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] > other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x > other for x in self) 

    def __ge__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] >= other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x >= other for x in self) 

    def __lt__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] < other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x < other for x in self) 

    def __le__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] <= other[idx] for idx in xrange(len(self))) 
     else: 
      return CompareList(x <= other for x in self) 

Bây giờ tôi có thể làm công cụ thú vị như CompareList([10, 5]) > CompareList([5, 10]) và nó sẽ chính xác trở CompareList([True,False])

Tuy nhiên, dí các hoạt động này không hoạt động độc đáo:

low = CompareList([1]) 
high = CompareList([2]) 
print(low > high > low) # returns CompareList([True]) 

Tại sao không? Điều gì xảy ra dưới mui xe ở đây? Tôi biết nó không tương đương với (low > high) > low = (False > low) (vì điều đó sẽ trả về Sai). Nó có thể là low > (high > low) nhưng điều đó sẽ không có ý nghĩa về mặt ưu tiên của toán tử (thường là từ trái qua phải).

+1

Kiểm tra 'functools.total_ordering' - nếu tôi nhớ chính xác, bạn chỉ cần cung cấp một cmp ví dụ' __eq__' và một thứ tự, ví dụ '__lt__' và thêm' @ functools.total_ordering' vào lớp ** **, và nó sẽ điền vào phần còn lại cho bạn. – dwanderson

+0

@dwanderson Tôi nhớ đọc Python sẽ làm phần còn lại cho bạn. –

Trả lời

8

Python cho phép biểu như x > y > z, trong đó, theo các tài liệu, tương đương với (x > y) and (y > z) trừ y chỉ được đánh giá một lần.

Theo điều này, low > high > low sẽ tương đương với (low > high) and (high > low).

>>> x = low > high # CompareList([False]) 
>>> y = high > low # CompareList([True]) 
>>> x and y 
CompareList([True]) 

Thêm từ tài liệu trên x and y:

x and y: nếu x là sai, sau đó x, khác y

Trong trường hợp trên:

>>> x is False 
False 
>>> x if x is False else y  # x and y 
CompareList([True]) 

vì vậy khi bạn thực hiện x and y, nó trả về số yCompareList([True]).

+1

Đó ... là một câu trả lời đơn giản đáng ngạc nhiên! – acdr

+1

Không nên là "nếu' x' là giả "với mã là' if x' hoặc 'nếu bool (x) là Sai'? Ví dụ ''' và True' sẽ trả về' '' 'mặc dù' '' không phải là False'. Một phần của vấn đề ở đây là 'bool (custom_object)' sẽ trả về 'True' trừ khi' __nonzero__' bị ghi đè, nhưng lọc theo cách mà OP dự định là không thể, như đã lưu ý trong câu trả lời của @ iulian. –

+0

''' và True' trả về' '' 'vì [' bool ('') '] (https://docs.python.org/3/library/stdtypes.html#truth-value-testing) là' False '. vì vậy nếu bạn đi theo logic ''' if '' là Sai khác True' thì' '' là False' đánh giá thành 'True' và bạn nhận được' '' '. – AKS

1

Bạn nên trả về giá trị boolean từ phương pháp so sánh của mình.

Để trích dẫn documentation for "rich comparison" methods:

Theo quy ước, False và True được trả về cho so sánh thành công. Tuy nhiên, các phương thức này có thể trả về bất kỳ giá trị nào, vì vậy nếu toán tử so sánh được sử dụng trong ngữ cảnh Boolean (ví dụ: trong điều kiện của câu lệnh if), Python sẽ gọi bool() trên giá trị là xác định kết quả đúng hay sai.

Để phá vỡ nó xuống cho trường hợp này:

exp1 = low > high 
print(exp1) 
print(bool(exp1)) 
exp2 = high > low 
print(exp2) 
print(bool(exp2)) 

sẽ cung cấp cho bạn

CompareList([False]) 
True 
CompareList([True]) 
True 

Bây giờ chúng ta làm các hoạt động cuối cùng và in ra kết quả

print(exp1 and exp2) 

Vì cả hai giá trị đều được đánh giá là True bạn sẽ nhận được

CompareList([True]) 
+0

Tôi không thấy nó có liên quan như thế nào với câu hỏi về sự so sánh chuỗi. Tôi biết rằng nói 'nếu CompareList ([Đúng, Sai]): 'không có ý nghĩa. – acdr

+0

Tôi đã thêm một số giải thích khác. – Matthias

3

Những câu trả lời khác là đúng, nhưng tôi muốn giải quyết thực tế thiếu thi cho vấn đề này, bởi vì, như tôi tin rằng, những gì OP muốn có được kết quả là từ low > high > low là một CompareList([False]).

Thật vậy, low > high > low đánh giá để (low > high) and (high > low) và kể từ CompareList([False]) is False đánh giá để False (có nghĩa là nó là True), sau đó các toán hạng thứ hai của and điều hành được đánh giá và trả lại (vì nó cũng đánh giá để True).

Chìa khóa để triển khai so sánh chuỗi là ghi đè toán tử boolean and dọc theo __gt____lt__.

Thật không may, không có cách nào để thực hiện việc này và có thể sẽ không xảy ra. Các PEP 335 - Overloadable Boolean Operators proposal đã bị từ chối bởi Guido, nhưng ông có thể xem xét việc so sánh xích như một < b < c quá tải [1].

Trừ khi thời điểm đó, không có cách nào để ví dụ của bạn hoạt động như mong đợi khi sử dụng so sánh xích.

Cách duy nhất để đạt được kết quả chính xác là bằng cách ghi đè các phương pháp __and__ và viết so sánh của bạn như thế này:

def CompareList(list): 
    ... 
    def __and__(self, other): 
     if isinstance(other, list): 
      return CompareList(self[idx] and other[idx] for idx in range(len(self))) 
     else: 
      return CompareList(x and other for x in self) 

Sau đó, bằng cách viết vào mẫu dưới đây, bạn sẽ nhận được câu trả lời đúng:

low = CompareList([1, 2]) 
high = CompareList([2, 2]) 
print((low >= high) & (high >= low)) # returns CompareList([False, True]) 
+0

Không chắc chắn tại sao điều này không cao hơn, rất sâu sắc, đặc biệt là tham chiếu PEP. Tôi sẽ lưu ý rằng 'CompareList ([False]) là False' có lẽ nên được thay đổi thành' bool (CompareList ([False])) là False' vì tính trung thực của nó đang được kiểm tra, không phải là danh tính của nó. –

+0

@JaredGoguen Cảm ơn bạn! Tôi đã đăng nó 1 giờ sau khi các câu trả lời khác đã được đăng, vì vậy đó là lý do tại sao ... – iulian