2016-04-11 15 views
7

Trong Python, tôi có thể làm quá tải phương thức __add__ của đối tượng (hoặc phương thức "gạch dưới kép" khác). Điều này cho phép tôi xác định hành vi tùy chỉnh cho các đối tượng của tôi khi sử dụng toán tử Python.Biết nếu + hoặc __add__ được gọi trên một đối tượng

Có thể biết, từ bên trong phương pháp dunder, nếu phương pháp được gọi qua + hoặc qua __add__? Ví dụ: giả sử tôi muốn tạo đối tượng in "+" hoặc "__add__" tùy thuộc vào việc + đã được sử dụng hay chưa hoặc trực tiếp __add__ được gọi trực tiếp hay không.

class MyAdder(object): 
    def __add__(self, other): 
     print method_how_created() 
     return 0 


MyAdder() + 7 
# prints "+", returns 0 

MyAdder().__add__(7) 
# prints "__add__", returns 0 

Chặn sự tồn tại của một số ma thuật như method_how_created, là có ánh xạ biểu tượng chuẩn cho các phương pháp sai? Tôi biết có các danh sách, chẳng hạn như http://www.python-course.eu/python3_magic_methods.php hoặc các giải pháp dựa trên phân tích cú pháp tài liệu của mô-đun operator, như đã đề cập ở đây: Access operator functions by symbol. Có cách nào để tìm một bản đồ giữa các tên hàm và các ký hiệu ít bị tấn công hơn là phân tích các tài liệu hoặc tạo danh sách theo cách thủ công không?

+8

Tại sao bạn lại muốn một cái gì đó như thế này? – wnnmaw

+1

Hãy sửa tôi nếu tôi sai, nhưng không phải '+' chỉ gọi '__add__'? –

+1

@ OrangeFlash81. Và đối với các lệnh 'NotImplemented'' __radd__' – Bharel

Trả lời

5

Có, nhưng có thể bạn không muốn làm điều này, vì đó là một ý tưởng tồi. Bạn phải kiểm tra ngăn xếp thông dịch viên. Vì lý do rõ ràng, điều này sẽ không hoạt động nếu mã của bạn được gọi là từ C. Dưới đây là các mã:

import inspect 
import dis 

class A(object): 
    def __add__(self, other): 
     fr = inspect.getouterframes(
      inspect.currentframe(), 1)[1][0] 
     opcode = fr.f_code.co_code[fr.f_lasti] 
     # opcode = ord(opcode) # Uncomment for Python 2 
     is_op = opcode == dis.opmap['BINARY_ADD'] 
     if is_op: 
      print('Called with +') 
     else: 
      print('Called with __add__') 

A() + 1 
A().__add__(1) 

này được thử nghiệm và hoạt động trên Python 3, và đòi hỏi phải sửa đổi chỉ nhẹ cho Python 2.

+0

Bạn có thể giải thích lý do tại sao điều này sẽ là một ý tưởng tồi? Có phải vì điều này sẽ không hoạt động trong các triển khai python không phải C, như Jython hay PyPy? Luôn luôn là một ý tưởng tồi để kiểm tra ngăn xếp thông dịch viên, và nếu có, tại sao? – cjrieds

+1

Đây sẽ là một ý tưởng tồi bởi vì (1) nó tạo ra một hàm hoạt động khác nhau tùy thuộc vào cách nó được gọi, điều bất thường và đáng ngạc nhiên, và vì (2) phương thức '__add __()' được cho là giống như ' + ', và làm cho nó hoạt động khác nhau chỉ là sẽ gây nhầm lẫn và tức giận mọi người. Đúng vậy, ai đó sử dụng mã này có thể tức giận * với bạn * để tạo ra một con quái vật như vậy. –

+0

Câu hỏi bổ sung-- lời xin lỗi của tôi nhưng không rõ ràng với tôi tại sao điều này sẽ không làm việc từ C. Bạn có thể giải thích, hoặc chỉ cho tôi một liên kết với một lời giải thích? – cjrieds

4

Trong cpython, bạn có một số khả năng introspection bằng cách tháo khung giới thiệu. Ví dụ:

import dis 
import inspect 

def method_how_created(): 
    return dis.dis(inspect.currentframe().f_back.f_back.f_code) 

class MyAdder(object): 
    def __add__(self, other): 
     print method_how_created() 

x = MyAdder() 

Kiểm tra trường hợp x + 7, bạn sẽ thấy một cái gì đó như thế này trong tháo gỡ:

  64 LOAD_NAME    5 (x) 
     67 LOAD_CONST    5 (7) 
     70 BINARY_ADD   
     71 POP_TOP    
     72 LOAD_CONST    1 (None) 
     75 RETURN_VALUE   

Sử dụng phương pháp kỳ diệu x.__add__(7), bạn sẽ thấy một cái gì đó như thế này trong tháo gỡ:

  64 LOAD_NAME    5 (x) 
     67 LOAD_ATTR    6 (__add__) 
     70 LOAD_CONST    5 (7) 
     73 CALL_FUNCTION   1 
     76 POP_TOP    
     77 LOAD_CONST    1 (None) 
     80 RETURN_VALUE   
Các vấn đề liên quan