2017-03-08 23 views
6

Nói rằng tôi đã xây dựng một thư viện chứa một lớp Foo, với sự hỗ trợ đối với một số phương pháp kỳ diệu, nói __add__()__radd__():Có phương pháp tốt nhất cho các phương pháp ma thuật mở rộng trong python?

>>> class Foo(object): 
...  def __add__(self, rhs): 
...   print("Foo.__add__", rhs) 
...  def __radd__(self, lhs): 
...   print("Foo.__radd__", lhs) 
... 
>>> foo = Foo() 
>>> foo + 3 
Foo.__add__ 3 
>>> 3 + foo 
Foo.__radd__ 3 

Khi tính toán 3 + foo, trăn đầu tiên gọi type(3).__add__(3, foo), nhưng như thế này trả về NotImplemented, nó rơi trở lại để type(foo).__radd__(foo, 3):

>>> type(3).__add__(3, foo) 
NotImplemented 

tôi muốn các nhà phát triển có thể xây dựng các thư viện trên đầu trang của thư viện của tôi, nói một thư viện chứa một lớp Bar và tôi muốn họ có toàn quyền kiểm soát. Cụ thể, tôi muốn triển khai một số cơ chế cho phép thư viện khác quyết định xem foo + bar có nên gọi số foo.__add__(bar) hoặc bar.__radd__(foo) hay không.

Tôi thấy rằng NumPy đã giải quyết vấn đề này bằng cách sử dụng sơ đồ __array_priority__. Nhưng điều này dường như gây ra một số nhức đầu (xem xét số lượng câu hỏi và các vấn đề mở ra về điều này). Có các phương pháp hay nhất khác không?

+1

Bạn có muốn tính năng này hoạt động với các lớp hiện có không? –

+0

Mặc dù câu hỏi của bạn khá thú vị nhưng thiếu chi tiết. Bạn có thể descrbe nhiều khía cạnh của hành vi mong muốn? –

Trả lời

1

Một lựa chọn phổ biến là để duy trì một danh sách các loại được hỗ trợ bởi các LHS, và nếu loại của RHS không có trong danh sách, sau đó trở về NotImplemented:

class Foo(object): 
    SUPPORTED_TYPES = (int, Foo) 
    def __add__(self, rhs): 
     if isinstance(type(rhs), SUPPORTED_TYPES): 
      [...] # compute self + rhs 
     else: 
      return NotImplemented 

này hoạt động tốt trừ khi rhs là một subtype thông minh của một trong số SUPPORTED_TYPES: không có cách nào nó sẽ được kiểm soát. Hơn nữa, các loại danh sách theo cách này không phải là rất linh hoạt. Nó có thể là tốt hơn để dựa vào gõ vịt hơn vào một danh sách các loại mã hóa cứng.

1

Một lựa chọn đơn giản là cố gắng để cho các LHS làm bất cứ điều gì nó cần phải làm (trong ví dụ dưới đây nó gọi phương pháp value() của RHS) và trong trường hợp nó đặt ra một ngoại lệ, bắt nó và trở NotImplemented:

class Foo(object): 
    [...] 
    def __add__(self, rhs): 
     try: 
      return self._value + rhs.value() 
     except AttributeError: 
      return NotImplemented 

Đơn giản như có thể, không cần duy trì danh sách SUPPORTED_TYPES. Tuy nhiên, RHS thực hiện một phương pháp value() không liên quan gì đến nhiệm vụ này, do đó có thể có một chút rủi ro. Hơn nữa, không có cách nào dễ dàng cho các rhs để có được toàn quyền kiểm soát kết quả.

Trong Python, nó thường là tốt hơn để cầu xin sự tha thứ chứ không phải xin phép, như trên, nhưng bạn có thể thích để kiểm tra xem rhsvalue() phương pháp:

class Foo(object): 
    def __add__(self, rhs): 
     rhs_value_func = getattr(rhs, "value", None) 
     if rhs_value_func is None: 
      return NotImplemented 
     else: 
      return self._value + rhs_value_func() 
0

Tuy nhiên, tùy chọn khác là để sử dụng một thuộc tính như __foo_priority__, phần nào giống như NumPy làm với __array_priority__ của nó:

class Foo(object): 
    __foo_priority__ = 0 
    def __add__(self, rhs): 
     delegate = True 
     try: 
      rhs_prio = type(rhs).__foo_priority__ 
      delegate = (self.__foo_priority__ < rhs_prio) 
     except AttributeError: 
      delegate = True 
     if delegate: 
      return NotImplemented 
     else: 
      return self.value_ + rhs.value() 

tùy chọn này hơi phức tạp, nhưng khá linh hoạt. Vấn đề duy nhất (nhỏ) với tùy chọn này là nó yêu cầu loại rhs để có thêm thuộc tính, vì vậy không có cách nào để kiểm soát rhs nếu nó là kiểu hiện có mà không có thuộc tính này.

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