Hướng dẫn python override compare method - ghi đè lên phương pháp so sánh python

Giả sử bạn có một lớp đơn giản như A dưới đây. Các phương pháp so sánh đều gần như giống nhau ngoại trừ chính so sánh. Có một lối tắt nào xung quanh việc khai báo sáu phương pháp trong một phương pháp sao cho tất cả các so sánh được hỗ trợ, một cái gì đó như

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
0?

Show

Tôi hỏi chủ yếu vì

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
0 có vẻ như Pythonic đối với tôi và tôi ngạc nhiên khi việc tìm kiếm của tôi không tìm thấy một tuyến đường như vậy.

class A:
    def __init__(self, number: float, metadata: str):
        self.number = number
        self.metadata = metadata

    def __lt__(self, other):
        return self.number < other.number

    def __le__(self, other):
        return self.number <= other.number

    def __gt__(self, other):
        return self.number > other.number

    def __ge__(self, other):
        return self.number >= other.number

    def __eq__(self, other):
        return self.number == other.number

    def __ne__(self, other):
        return self.number != other.number


class B:
    def __init__(self, number: float, metadata: str):
        self.number = number
        self.metadata = metadata

    def __compare__(self, other, comparison):
        return self.number.__compare__(other.number, comparison)

Trong Python 3, tham số
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5 đã được xóa và chỉ có thể sử dụng
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return ((self.last, self.first) != (other.last, other.first))

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __le__(self, other):
        return ((self.last, self.first) <= (other.last, other.first))

    def __gt__(self, other):
        return ((self.last, self.first) > (other.last, other.first))

    def __ge__(self, other):
        return ((self.last, self.first) >= (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
2 (hoặc không có đối số nào).

Không có người sửa lỗi cho sự thay đổi này. Tuy nhiên, việc phát hiện ra nó rất đơn giản: cuộc gọi

class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return ((self.last, self.first) != (other.last, other.first))

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __le__(self, other):
        return ((self.last, self.first) <= (other.last, other.first))

    def __gt__(self, other):
        return ((self.last, self.first) > (other.last, other.first))

    def __ge__(self, other):
        return ((self.last, self.first) >= (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5 với đối số
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5 làm tăng kiểu loại trong Python 3. Mỗi hàm CMP phải được thay thế bằng một hàm chính. Có hai cách để làm điều này:

Nếu chức năng thực hiện một hoạt động chung trên cả hai đối số và sau đó so sánh kết quả, hãy thay thế nó chỉ bằng hoạt động chung. Nói cách khác, class Person(object): def __init__(self, firstname, lastname): self.first = firstname self.last = lastname def __eq__(self, other): return ((self.last, self.first) == (other.last, other.first)) def __ne__(self, other): return ((self.last, self.first) != (other.last, other.first)) def __lt__(self, other): return ((self.last, self.first) < (other.last, other.first)) def __le__(self, other): return ((self.last, self.first) <= (other.last, other.first)) def __gt__(self, other): return ((self.last, self.first) > (other.last, other.first)) def __ge__(self, other): return ((self.last, self.first) >= (other.last, other.first)) def __repr__(self): return "%s %s" % (self.first, self.last) 7 nên được thay thế bằng class Person(object): def __init__(self, firstname, lastname): self.first = firstname self.last = lastname def __eq__(self, other): return ((self.last, self.first) == (other.last, other.first)) def __ne__(self, other): return ((self.last, self.first) != (other.last, other.first)) def __lt__(self, other): return ((self.last, self.first) < (other.last, other.first)) def __le__(self, other): return ((self.last, self.first) <= (other.last, other.first)) def __gt__(self, other): return ((self.last, self.first) > (other.last, other.first)) def __ge__(self, other): return ((self.last, self.first) >= (other.last, other.first)) def __repr__(self): return "%s %s" % (self.first, self.last) 8

Nếu ở trên không áp dụng, hãy bọc chức năng kiểu CMP với

class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return ((self.last, self.first) != (other.last, other.first))

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __le__(self, other):
        return ((self.last, self.first) <= (other.last, other.first))

    def __gt__(self, other):
        return ((self.last, self.first) > (other.last, other.first))

    def __ge__(self, other):
        return ((self.last, self.first) >= (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
9. Xem tài liệu của nó để biết chi tiết.

Hàm

def cmp(x, y):
    """
    Replacement for built-in function cmp that was removed in Python 3

    Compare the two objects x and y and return an integer according to
    the outcome. The return value is negative if x < y, zero if x == y
    and strictly positive if x > y.
    """

    return (x > y) - (x < y)
0 không có sẵn trong Python 2.6, vì vậy nếu bạn cần hỗ trợ phiên bản đó, bạn sẽ cần sao chép nó từ các nguồn Python

Hướng dẫn chuyển Python 3 bảo thủ

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()

Python 3 là nghiêm ngặt khi so sánh các đối tượng của các loại khác nhau. Nó cũng giảm so sánh dựa trên CMP và phân loại có lợi cho các so sánh phong phú và phân loại chính, các lựa chọn thay thế hiện đại đã có sẵn ít nhất kể từ Python 2.4. Chi tiết và chiến lược chuyển tiếp theo.

Các loại không thể đặt hàng

Cách tiếp cận nghiêm ngặt để so sánh trong Python 3 khiến thường không thể so sánh các loại đối tượng khác nhau.

  • FIXER: Không có: None
  • Tỷ lệ lưu hành: chung

Ví dụ, trong Python 2, so sánh

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
2 và
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
3 hoạt động (với kết quả không thể đoán trước được trong quá trình triển khai Python):

Nhưng trong Python 3, nó thất bại với một thông báo lỗi được mô tả tốt:

Sự thay đổi thường thể hiện trong danh sách sắp xếp: trong Python 3, danh sách với các mục thuộc loại khác nhau thường không thể sắp xếp.

Nếu bạn cần sắp xếp các danh sách không đồng nhất hoặc so sánh các loại đối tượng khác nhau, hãy thực hiện một chức năng chính để mô tả đầy đủ cách các loại khác nhau nên được đặt hàng.

So sánh phong phúPhương pháp đặc biệt
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
4 không còn được tôn vinh trong Python 3.
Trong Python 2,
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
5 đã thực hiện so sánh giữa hai đối tượng, trả về giá trị âm nếu
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
6, dương nếu
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
7 và không nếu chúng bằng nhau.
Cách tiếp cận này thể hiện kết quả so sánh là phổ biến trong các ngôn ngữ kiểu C. Nhưng, sớm trong sự phát triển của Python 2, rõ ràng là chỉ cho phép ba trường hợp cho thứ tự tương đối của các đối tượng là quá hạn chế.
Điều này dẫn đến việc giới thiệu các phương pháp so sánh phong phú, gán một phương pháp đặc biệt cho mỗi toán tử:Nhà điều hành
Phương pháp==
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
8
! =
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
9
<
class Person(object):
    def __init__(self, firstname, lastname):
         self.first = firstname
         self.last = lastname

    def __cmp__(self, other):
        return cmp((self.last, self.first), (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
0
class Person(object):
    def __init__(self, firstname, lastname):
         self.first = firstname
         self.last = lastname

    def __cmp__(self, other):
        return cmp((self.last, self.first), (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
3

class Person(object):
    def __init__(self, firstname, lastname):
         self.first = firstname
         self.last = lastname

    def __cmp__(self, other):
        return cmp((self.last, self.first), (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
1

>

Để tránh rắc rối trong việc cung cấp tất cả sáu chức năng, bạn có thể thực hiện

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
8,
>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
9 và chỉ một trong các nhà khai thác đặt hàng và sử dụng trình trang trí
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
0 để điền vào phần còn lại. Lưu ý rằng người trang trí không có sẵn trong Python 2.6. Nếu bạn cần hỗ trợ phiên bản đó, bạn sẽ cần cung cấp tất cả sáu phương pháp.

Bộ trang trí

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
1 đi kèm với chi phí thực hiện chậm hơn và dấu vết ngăn xếp phức tạp hơn cho các phương pháp so sánh dẫn xuất, do đó, việc xác định tất cả sáu có thể cần thiết rõ ràng trong một số trường hợp ngay cả khi hỗ trợ Python 2.6 bị giảm.

Ví dụ, giả sử rằng bạn có một lớp để đại diện cho một người có

>>> 2 < '2'
Traceback (most recent call last):
File "", line 1, in 
TypeError: unorderable types: int() < str()
4 được thực hiện:

class Person(object):
    def __init__(self, firstname, lastname):
         self.first = firstname
         self.last = lastname

    def __cmp__(self, other):
        return cmp((self.last, self.first), (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)

Với

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
3, lớp sẽ trở thành:

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)

Nếu

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
3 không thể được sử dụng hoặc nếu hiệu quả là quan trọng, tất cả các phương pháp có thể được đưa ra rõ ràng:

class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return ((self.last, self.first) != (other.last, other.first))

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __le__(self, other):
        return ((self.last, self.first) <= (other.last, other.first))

    def __gt__(self, other):
        return ((self.last, self.first) > (other.last, other.first))

    def __ge__(self, other):
        return ((self.last, self.first) >= (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)

Hàm from functools import total_ordering @total_ordering class Person(object): def __init__(self, firstname, lastname): self.first = firstname self.last = lastname def __eq__(self, other): return ((self.last, self.first) == (other.last, other.first)) def __ne__(self, other): return not (self == other) def __lt__(self, other): return ((self.last, self.first) < (other.last, other.first)) def __repr__(self): return "%s %s" % (self.first, self.last) 5

  • FIXER: Không có: None
  • Tỷ lệ lưu hành: chung

Là một phần của việc di chuyển khỏi các so sánh theo kiểu CMP, hàm

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
6 đã được loại bỏ trong Python 3.

Nếu nó là cần thiết (thường là phù hợp với API bên ngoài), bạn có thể cung cấp cho mã này:

def cmp(x, y):
    """
    Replacement for built-in function cmp that was removed in Python 3

    Compare the two objects x and y and return an integer according to
    the outcome. The return value is negative if x < y, zero if x == y
    and strictly positive if x > y.
    """

    return (x > y) - (x < y)

Biểu thức được sử dụng không đơn giản, vì vậy nếu bạn cần chức năng, chúng tôi khuyên bạn nên thêm chức năng đầy đủ, được ghi lại vào thư viện tiện ích dự án của bạn.

Đối số from functools import total_ordering @total_ordering class Person(object): def __init__(self, firstname, lastname): self.first = firstname self.last = lastname def __eq__(self, other): return ((self.last, self.first) == (other.last, other.first)) def __ne__(self, other): return not (self == other) def __lt__(self, other): return ((self.last, self.first) < (other.last, other.first)) def __repr__(self): return "%s %s" % (self.first, self.last) 5

  • FIXER: Không có: None
  • Tỷ lệ lưu hành: chung

Là một phần của việc di chuyển khỏi các so sánh theo kiểu CMP, hàm

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
6 đã được loại bỏ trong Python 3.

Nếu nó là cần thiết (thường là phù hợp với API bên ngoài), bạn có thể cung cấp cho mã này:

>>> actors = [Person('Eric', 'Idle'),
...           Person('John', 'Cleese'),
...           Person('Michael', 'Palin'),
...           Person('Terry', 'Gilliam'),
...           Person('Terry', 'Jones')]
...

Biểu thức được sử dụng không đơn giản, vì vậy nếu bạn cần chức năng, chúng tôi khuyên bạn nên thêm chức năng đầy đủ, được ghi lại vào thư viện tiện ích dự án của bạn.

>>> def cmp_last_name(a, b):
...     """ Compare names by last name"""
...     return cmp(a.last, b.last)
...
>>> sorted(actors, cmp=cmp_last_name)
['John Cleese', 'Terry Gilliam', 'Eric Idle', 'Terry Jones', 'Michael Palin']

Đối số

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5

Tỷ lệ: Không phổ biến

>>> def keyfunction(item):
...     """Key for comparison by last name"""
...     return item.last
...
>>> sorted(actors, key=keyfunction)
['John Cleese', 'Terry Gilliam', 'Eric Idle', 'Terry Jones', 'Michael Palin']

Trong các hàm Python 2,

from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
8 hoặc
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
9 có tham số
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5, xác định thứ tự sắp xếp. Đối số cho
from functools import total_ordering

@total_ordering
class Person(object):

    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname

    def __eq__(self, other):
        return ((self.last, self.first) == (other.last, other.first))

    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return ((self.last, self.first) < (other.last, other.first))

    def __repr__(self):
        return "%s %s" % (self.first, self.last)
5 là một hàm, giống như tất cả các hàm kiểu CMP, trả về kết quả âm, bằng không hoặc kết quả dương tính tùy thuộc vào thứ tự của hai đối số của nó.

Ví dụ: đưa ra một danh sách các phiên bản của một lớp một người (được xác định ở trên):

Một cách để sắp xếp nó theo họ trong Python 2 sẽ là:

  • Hàm này được gọi là nhiều lần - O (n log n) - trong quá trình so sánh.

  • Thay thế cho CMP, việc sắp xếp các chức năng có thể lấy tham số

    class Person(object):
    
        def __init__(self, firstname, lastname):
            self.first = firstname
            self.last = lastname
    
        def __eq__(self, other):
            return ((self.last, self.first) == (other.last, other.first))
    
        def __ne__(self, other):
            return ((self.last, self.first) != (other.last, other.first))
    
        def __lt__(self, other):
            return ((self.last, self.first) < (other.last, other.first))
    
        def __le__(self, other):
            return ((self.last, self.first) <= (other.last, other.first))
    
        def __gt__(self, other):
            return ((self.last, self.first) > (other.last, other.first))
    
        def __ge__(self, other):
            return ((self.last, self.first) >= (other.last, other.first))
    
        def __repr__(self):
            return "%s %s" % (self.first, self.last)
    
    2 chỉ có từ khóa, một hàm trả về khóa theo đó để sắp xếp:

    Ưu điểm của phương pháp này là chức năng này chỉ được gọi một lần cho mỗi mục. Khi các loại đơn giản như bộ dữ liệu, chuỗi và số được sử dụng cho các khóa, nhiều so sánh sau đó được xử lý bằng mã C được tối ưu hóa. Ngoài ra, trong hầu hết các trường hợp, các chức năng chính dễ đọc hơn CMP: Thông thường, mọi người nghĩ đến việc sắp xếp theo một số khía cạnh của một đối tượng (chẳng hạn như tên cuối cùng), thay vì so sánh các đối tượng riêng lẻ. Nhược điểm chính là kiểu CMP cũ thường được sử dụng trong các API ngôn ngữ C, do đó các thư viện bên ngoài có khả năng cung cấp các chức năng tương tự.