2017-11-30 18 views
5

Tôi có một chút lớp helper:Tại sao Python không có phương thức "__req__" (phản ánh bình đẳng)?

class AnyOf(object): 
    def __init__(self, *args): 
     self.elements = args 
    def __eq__(self, other): 
     return other in self.elements 

này cho phép tôi làm điều kỳ diệu ngọt ngào như:

>>> arr = np.array([1,2,3,4,5]) 
>>> arr == AnyOf(2,3) 
np.array([False, True, True, False, False]) 

mà không cần phải sử dụng một danh sách hiểu biết (như trong np.array(x in (2,3) for x in arr).

(Tôi duy trì một giao diện người dùng duy nhất cho phép() sử dụng tin cậy gõ vào đoạn mã tùy ý, và a == AnyOf(1,2,3) là rất nhiều ngon miệng hơn một danh sách hiểu biết cho người dùng phi kỹ thuật am hiểu.)

Tuy nhiên!

Điều này chỉ hoạt động một chiều! Ví dụ, nếu tôi đã làm AnyOf(2,3) == arr thì phương thức __eq__ của lớp không bao giờ được gọi: thay vào đó, phương thức __eq__ của mảng NumPy được gọi, mà nội bộ (tôi sẽ đoán) gọi phương thức __eq__ của tất cả các phần tử của nó.

Điều này khiến tôi tự hỏi: tại sao Python không cho phép một mặt tương đương với __eq__? (Tương đương với phương pháp như __radd__, __rmul__, vân vân.)

+2

"Tại sao Python không có X?" không phải là một câu hỏi rất hiệu quả. Nếu bạn muốn Python có X, cách hiệu quả để thực hiện điều đó xảy ra là tuân theo [quy trình trong PEP-1] (https://www.python.org/dev/peps/pep-0001/#start-with- một ý tưởng-cho-python) để thực sự đề xuất ý tưởng và nhận phản hồi từ những người có tiếng nói về những gì được thêm vào ngôn ngữ. Nếu không, "Có cách nào để có được hiệu ứng hữu ích của X trong Python không?" có nhiều khả năng thực sự có kết quả hữu ích ngay lập tức (tạo ra ánh sáng thay vì nhiệt). –

+1

Liên quan: https://stackoverflow.com/questions/3588776/how-is-eq-handled-in-python-and-in-what-order – Craig

+0

Thực ra tôi không thể nhận ra nhu cầu cấp bách cho một lớp trong ví dụ ở trên. Tại sao không sử dụng một hàm đơn giản, trả về một danh sách? – guidot

Trả lời

5

Một __req__ không phải là một ý tưởng tốt trong ngôn ngữ, bởi vì nếu lớp Left định nghĩa __eq__ và lớp Right định nghĩa __req__, sau đó Python có nghĩa vụ đưa ra quyết định phù hợp về những người được gọi đầu tiên trong Left() == Right(). Cả hai đều không thể thắng.

Tuy nhiên, mô hình datamodel Python cho phép bạn thực hiện những gì bạn muốn ở đây. Từ cả hai bên, bạn có thể kiểm soát so sánh này, nhưng bạn sẽ cần xác định AnyOf chính xác. Nếu bạn muốn AnyOf để kiểm soát __eq__ từ phía bên tay phải, bạn phải xác định nó là một phân lớp của np.ndarray.

nếu tôi được làm AnyOf(2,3) == arr sau đó phương pháp __eq__AnyOf lớp của tôi không bao giờ được gọi

Không, bạn có một sự hiểu lầm cơ bản ở đây. Phía bên tay trái luôn luôn được thử đầu tiên tại sự so sánh bình đẳng, trừ khi phía bên phải là một phân lớp của loại bên tay trái.

arr == AnyOf(2,3) 

Trong trường hợp trên, tùy chỉnh của bạn __eq__ được gọi, bởi vì các mảng NumPy gọi nó! Vì vậy, np.ndarray thắng và quyết định kiểm tra một lần cho mỗi phần tử. Nó theo nghĩa đen có thể làm bất cứ điều gì khác, bao gồm cả không gọi AnyOf.__eq__ của bạn cả.

AnyOf(2,3) == arr 

Trong trường hợp trên, lớp học của bạn không nhận được lần thử đầu tiên tại sự so sánh, và nó không thành công vì cách bạn sử dụng in (kiểm tra nếu một mảng là trong một tuple).

+0

Về "An' __req__' không phải là một ý tưởng tốt trong ngôn ngữ "và đối số sau, tôi không nghĩ rằng giữ, như từ cùng một đối số, phương pháp như' __radd__' cũng sẽ không phải là một ý tưởng tốt. Tuy nhiên, tôi thấy bây giờ, do mô hình được chọn ('Left .__ eq__' hoặc' Right .__ req__') nó sẽ thực sự làm cho nó để '__req__' có thể thực hiện đúng chức năng giống như' __eq__', làm cho phương pháp tay phải không cần thiết! – acdr

2

Các documentation về __rxx__ phương pháp như __radd__ trạng thái:

Những chức năng này chỉ được gọi nếu toán hạng trái không hỗ trợ hoạt động tương ứng và các toán hạng có nhiều loại khác nhau.

Trong khi các lớp học không có __add__ hoặc __sub__ phương pháp mỗi mặc định, họ làm__eq__:

>>> class A(object): 
...  pass 
>>> '__eq__' in dir(A) 
True 

Điều này có nghĩa __req__ sẽ không bao giờ được gọi là trừ khi bạn xóa một cách rõ ràng __eq__ từ lớp khác.

Bạn có thể giải quyết vấn đề cụ thể của bạn với np.in1d:

>>> np.in1d(arr, [2, 3]) 
array([False, True, True, False, False], dtype=bool) 
1

Đây là tài liệu hướng dẫn trên data model:

Không có phiên bản hoán đổi đối số của các phương pháp này (được sử dụng khi đối số bên trái không hỗ trợ thao tác nhưng đối số phải thực hiện); đúng hơn, __lt__()__gt__() là phản chiếu của nhau, __le__()__ge__() là phản ánh của nhau, và __eq__()__ne__() là phản xạ của riêng mình. Nếu toán hạng là các loại khác nhau và loại toán hạng phải là một lớp trực tiếp hoặc gián tiếp của loại toán hạng bên trái, phương pháp được phản ánh của toán hạng phù hợp có ưu tiên, nếu không phương thức của toán hạng trái có ưu tiên là . Phân lớp ảo không được xem xét.

Như đã trình bày trong các ý kiến ​​trên, những gì bạn muốn tác phẩm, và __eq__ cơ bản là sames như một tiềm năng __req__: nó được gọi là ở phía bên tay phải của == nếu đối tượng ở phía bên tay trái trả NotImplemented:

In [1]: class A: 
    ...:  def __eq__(self, other): 
    ...:   return NotImplemented 
    ...:  

In [2]: class B: 
    ...:  def __eq__(self, other): 
    ...:   print("B comparing") 
    ...:   return True 
    ...:  

In [3]: B() == A() 
B comparing 
Out[3]: True 

In [4]: A() == B() 
B comparing 
Out[4]: True 

In [5]: A() == A() 
Out[5]: False 

Vì nó đến, nó thậm chí làm việc với khác, thông thường, các đối tượng:

In [10]: 5 == B() 
B comparing 
Out[10]: True 

Tuy nhiên, một số đối tượng có thể mang lại TypeError trên __eq__ thay vì trả lại NotImplemented hoặc False và điều này làm cho điều này không đáng tin cậy đối với tất cả các loại đối tượng.

Điều gì xảy ra trong trường hợp của bạn, việc sử dụng sai số toán tử in với các mảng và bộ dữ liệu bên trong phương thức __eq__ của riêng bạn. (Cảm ơn @wim đã phát hiện điều này trong một câu trả lời ở đây).

+1

+1 đó là một "if" lớn - một số loại tiêu chuẩn sẽ chỉ tăng ngoại lệ thay vì trả về 'NotImplemented', vì vậy bạn không thể dựa vào điều này. – wim

+0

Tôi không chắc nó hoạt động với 'np.array' theo cách đó. Nhưng nếu nó không phải là một lỗi trong 'np.array', không phải là một cái gì đó thiếu từ ngôn ngữ. – doublep

+0

Tôi không nói người ta có thể dựa vào nó. – jsbueno

Các vấn đề liên quan