Làm cách nào để một bộ Python ([]) kiểm tra xem hai đối tượng có bằng nhau không? Đối tượng cần xác định những phương thức nào để tùy chỉnh điều này?


83

Tôi cần tạo một đối tượng hoặc lớp 'vùng chứa' bằng Python, nó lưu giữ bản ghi của các đối tượng khác mà tôi cũng xác định. Một yêu cầu của vùng chứa này là nếu hai đối tượng được coi là giống hệt nhau, một (một trong hai) sẽ bị loại bỏ. Suy nghĩ đầu tiên của tôi là sử dụng a set([])làm đối tượng chứa, để hoàn thành yêu cầu này.

Tuy nhiên, tập hợp không loại bỏ một trong hai trường hợp đối tượng giống hệt nhau. Tôi phải xác định những gì để tạo một?

Đây là mã Python.

class Item(object):
  def __init__(self, foo, bar):
    self.foo = foo
    self.bar = bar
  def __repr__(self):
    return "Item(%s, %s)" % (self.foo, self.bar)
  def __eq__(self, other):
    if isinstance(other, Item):
      return ((self.foo == other.foo) and (self.bar == other.bar))
    else:
      return False
  def __ne__(self, other):
    return (not self.__eq__(other))

Thông dịch viên

>>> set([Item(1,2), Item(1,2)])
set([Item(1, 2), Item(1, 2)])

Rõ ràng rằng __eq__(), x == yphương thức được gọi bởi , không phải là phương thức được gọi bởi tập hợp. Được gọi là gì? Tôi phải xác định phương pháp nào khác?

Lưu ý: Các Items phải vẫn có thể thay đổi và có thể thay đổi, vì vậy tôi không thể cung cấp một __hash__()phương thức. Nếu đây là cách duy nhất để làm điều đó, thì tôi sẽ viết lại để sử dụng các bất biến Items.


1
Gặp phải vấn đề tương tự. Tôi giả sử bạn đang thao túng một lượng nhỏ dữ liệu bên trong mã của mình. Đây có lẽ không phải là một ứng cử viên tốt cho việc sử dụng cơ sở dữ liệu. Tôi nhớ có thể tạo một tập hợp và xác định một hàm so sánh trong C ++ và tôi tin rằng Java cũng vậy, tuy nhiên có vẻ như bạn không thể làm điều này với các đối tượng từ điển trong Python. Có vẻ như ai đó có thể đã viết một thư viện "set" bằng Python có thể làm điều này, nhưng tôi không biết về một thư viện.
Chris Dutrow

Câu trả lời:


32

Tôi sợ bạn sẽ phải cung cấp một __hash__()phương pháp. Nhưng bạn có thể mã hóa nó theo cách này, điều đó không phụ thuộc vào các thuộc tính có thể thay đổi của bạn Item.


3
< docs.python.org/reference/… > Trong đoạn thứ hai ở đây, nó chỉ ra rằng __hash__()chỉ nên được định nghĩa cho các đối tượng bất biến.
Ada

1
@Nathanael: nếu đối tượng có thể phải thay đổi, bạn có thể tạo một bản sao bất biến của đối tượng, như frozenset () và set ().
Lie Ryan

2
@Nathanael - bạn muốn gọi __eq__như thế nào? So sánh các thuộc tính (1,2) đó? Sau đó, bạn phải trả lại một số băm của (1,2) cũng trong __hash__phương thức của bạn .
eumiro

Hoặc, vì foo và bar đều không thay đổi, __hash __ () có thể trả về tổng các băm của foo và bar? Không ... tổng quá không đáng tin cậy ... nếu foo là 1 và thanh 2, đó sẽ là thanh bằng 1 với foo là 2, điều này là sai. Hàm toán học nào có thể được sử dụng cho mục đích này? Modulo hoặc bộ phận sẽ hoạt động.
Ada

1
Nathanael: Chỉ cần sử dụng hashdựng sẵn: hash((self.foo, self.bar)). Điều này sử dụng băm của tuple, phù hợp với nhu cầu của bạn. (Của bạn __eq__cũng có thể được viết dưới dạng tupleso sánh.)
Pi Delport

73

Có, bạn cần một __hash__()-method VÀ toán tử so sánh mà bạn đã cung cấp.

class Item(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
    def __repr__(self):
        return "Item(%s, %s)" % (self.foo, self.bar)
    def __eq__(self, other):
        if isinstance(other, Item):
            return ((self.foo == other.foo) and (self.bar == other.bar))
        else:
            return False
    def __ne__(self, other):
        return (not self.__eq__(other))
    def __hash__(self):
        return hash(self.__repr__())

3
Trên Python 3, bạn không cần __ne__, và thậm chí trong 2.x, bạn không nên định nghĩa __ne__về mặt __eq__. Thấy chưa, stackoverflow.com/a/30676267/5337834
John Strood
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.