Làm cho một lớp do người dùng xác định trong python có thể sắp xếp, có thể băm


82

Những phương thức nào cần được ghi đè / triển khai khi làm cho các lớp do người dùng xác định có thể sắp xếp và / hoặc có thể băm trong python?

Những điều cần chú ý là gì?

Tôi nhập dir({})vào trình thông dịch của mình để nhận danh sách các phương thức trên các phương thức cài sẵn. Trong số đó, tôi cho rằng tôi cần triển khai một số tập hợp con của

['__cmp__', '__eq__', '__ge__', '__gt__', '__hash__', '__le__', '__lt__', '__ne__']

Có sự khác biệt nào trong các phương thức phải được triển khai cho Python3 so với Python2 không?


3
Thảo luận hay ở đây: stackoverflow.com/q/1061283/641766 . Sự khác biệt giữa Python 2.x và 3.x __cmp__đã bị loại bỏ.
zeekay

Câu trả lời:


88

Tôi gần như đã đăng điều này như một bình luận cho các câu trả lời khác nhưng nó thực sự là một câu trả lời trong và của chính nó.

Để làm cho các mục của bạn có thể phân loại được, chúng chỉ cần triển khai __lt__. Đó là phương pháp duy nhất được sử dụng bởi loại có sẵn.

Các phép so sánh khác hoặc functools.total_orderingchỉ cần thiết nếu bạn thực sự muốn sử dụng các toán tử so sánh với lớp của mình.

Để làm cho các mục của bạn có thể được băm, bạn triển khai __hash__như những người khác đã lưu ý. Bạn cũng nên triển khai __eq__theo cách tương thích - các mục tương đương nên băm giống nhau.


vì vậy việc triển khai kém __lt__có thể khiến python sắp xếp không thể đoán trước? (ví dụ: nếu x .__ lt __ (y) và y .__ lt __ (x))
Matt Fenwick

3
Tôi không biết về "không thể đoán trước", nó sẽ nhất quán nếu được cung cấp cùng một đầu vào chính xác, nhưng một thứ tự đầu vào khác có thể khiến các mặt hàng khác nhau theo một thứ tự khác. Có, nếu bạn triển khai không đúng cách so sánh được sử dụng để sắp xếp, Python sẽ sắp xếp không đúng cách. Tôi muốn giới thiệu một __key__hàm biến thể hiện thành một tuple, sau đó chỉ cần sử dụng cả __lt__( self.__key__() < other.__key__()) và __hash__( hash(self.__key__())).
agf

19

Không có bất kỳ sự khác biệt nào giữa Python 2 và 3.

Để có thể sắp xếp:

Bạn nên xác định các phương pháp so sánh. Điều này làm cho các mặt hàng của bạn có thể phân loại được. Nói chung, bạn không nên thích __cmp__().

Tôi thường sử dụng trình trang trí functools.total_ordering.

functools.total_ordering (cls) Cho một lớp xác định một hoặc nhiều phương thức sắp xếp so sánh phong phú, trình trang trí lớp này cung cấp phần còn lại. Điều này đơn giản hóa nỗ lực liên quan đến việc xác định tất cả các hoạt động so sánh phong phú có thể có:

Các lớp phải xác định một trong những __lt__(), __le__(), __gt__(), hoặc __ge__(). Ngoài ra, lớp nên cung cấp một __eq__()phương thức.

Bạn nên cẩn thận rằng các phương pháp so sánh của bạn không có bất kỳ tác dụng phụ nào. (thay đổi bất kỳ giá trị nào của đối tượng)

Đối với băm:

Bạn nên thực hiện __hash__()phương pháp. Tôi nghĩ rằng cách tốt nhất là quay trở lại hash(repr(self)), vì vậy hàm băm của bạn sẽ là duy nhất.


Để biết ví dụ functools.total_orderingtừ tài liệu, xem tại đây .
Evgeni Sergeev

3

Có một số cách đánh dấu đối tượng của bạn có thể sắp xếp được. So sánh thứ nhất - đa dạng thức, được xác định bởi một tập hợp các hàm:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

Ngoài ra, chỉ có thể xác định một chức năng:

object.__cmp__(self, other)

Và cuối cùng sẽ được xác định nếu bạn muốn xác định __hash__chức năng tùy chỉnh . Xem tài liệu .


6
Trong Python 3, "[...] __cmp__()phương thức đặc biệt không còn được hỗ trợ", hãy xem phần liên quan tại đây .
Evgeni Sergeev

-3

__lt__(self,other)Phương thức triển khai là câu trả lời để làm cho lớp của bạn có thể sắp xếp được.
Nó có thể được sử dụng không chỉ cho phương thức tích hợp sorted(iterable)mà còn cho hàng đợi ưu tiên qua heapqmô-đun.

Ngoài ra, tôi không thích thiết kế của python, rất nhiều '__ge__', '__gt__', '__le__', '__lt__', '__ne__'phương pháp không trực quan chút nào !
Ngược lại, Java's Interface Comparable<T> (xem java doc ) trả về một số nguyên âm, 0 hoặc một số nguyên dương vì đối tượng này nhỏ hơn, bằng hoặc lớn hơn đối tượng được chỉ định, đối tượng này trực tiếp và thân thiện !


9
Hầu hết câu trả lời của bạn bao hàm ý kiến ​​của bạn (không nên là một phần của câu trả lời).
skyking

@skyking ... trong khi tôi không đồng ý với ý kiến ​​trong câu trả lời cụ thể này, tôi tin rằng ý kiến ​​đó (với dữ liệu hữu ích và được nghiên cứu có liên quan đến câu hỏi) là rất có giá trị. Lỗi trong câu trả lời ý kiến ​​này là không hỗ trợ ý kiến ​​với dữ liệu liên quan. Nhưng hiểu được sở thích giúp mọi người đưa ra quyết định và do đó rất hữu ích. Ở đây, câu trả lời là không chính xác vì không có mã python hữu ích và có thể hành động để minh họa một cách pythonic để viết mã dựa trên sở thích của tác giả.
Andrew
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.