2016-04-11 11 views
7

W Pythonie mogę przeciążyć metodę obiektu __add__ (lub inne podwójne podkreślenie, inaczej metody "dunder"). To pozwala mi zdefiniować niestandardowe zachowanie dla moich obiektów podczas korzystania z operatorów Pythona.Wiem, czy + lub __add__ wywołał obiekt

Czy można dowiedzieć się, z metody dunder, czy metoda została wywołana przez + lub przez __add__?

Na przykład, załóżmy, że chcę stworzyć przedmiot, który drukuje "+" lub "__add__" zależności czy + użyto czy __add__ nazwano bezpośrednio.

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 

zakazu istnienia jakiejś magii jak method_how_created, czy istnieje kanoniczny odwzorowanie symboli Dunder metod? Wiem, że istnieją listy, takie jak http://www.python-course.eu/python3_magic_methods.php lub rozwiązania oparte na analizie docletów modułu operator, jak wspomniano tutaj: Access operator functions by symbol. Czy istnieje sposób na znalezienie mapy między nazwami funkcji i symbolami, która jest trochę mniej odstręczająca niż analiza docstrukcji lub ręczne tworzenie listy?

+8

Dlaczego miałbyś chcieć czegoś takiego? – wnnmaw

+1

Proszę mnie poprawić, jeśli się mylę, ale czy '+' nie wystarczy zadzwonić '__add__'? –

+1

@ OrangeFlash81 to robi. A w przypadku 'NotImplemented' wywołania' __radd__' – Bharel

Odpowiedz

5

Tak, ale prawdopodobnie nie chcesz tego robić, ponieważ jest to zły pomysł. Musisz przejrzeć stos tłumacza. Z oczywistych powodów, to nie będzie działać, jeśli Twój kod jest wywoływana z C. Oto kod:

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) 

ten został przetestowany i działa na Python 3 i wymaga jedynie niewielkich modyfikacji dla Pythona 2.

+0

Czy mógłbyś wyjaśnić, dlaczego byłby to zły pomysł? Czy to nie działa w implementacjach Pythona innych niż C, takich jak Jython czy PyPy? Czy sprawdzanie stosu interpretera jest zawsze złym pomysłem, a jeśli tak, dlaczego? – cjrieds

+1

To byłby zły pomysł, ponieważ (1) tworzy on funkcję, która zachowuje się inaczej w zależności od tego, jak się ją nazywa, co jest niezwykłe i zaskakujące, i ponieważ (2) metoda '__add __()' powinna być taka sama jak ' + "i sprawianie, by zachowywał się inaczej, po prostu będzie mylić i wściekać ludzi. Zgadza się, ktoś używający tego kodu może się na ciebie wkurzyć * za stworzenie takiego potwora. –

+0

Dodatkowe pytanie - moje przeprosiny, ale nie jest dla mnie jasne, dlaczego to nie zadziała z C. Czy mógłbyś wyjaśnić, lub wskazać mi link z wyjaśnieniem? – cjrieds

4

W cpython, masz trochę introspekcji poprzez demontaż ramki strony odsyłającej. Na przykład:

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() 

Sprawdzanie sprawę x + 7, widać coś takiego w demontażu:

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

Korzystanie magiczną metodę x.__add__(7), widać coś takiego w demontażu:

  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   
Powiązane problemy