2012-02-22 15 views
42

W następującym kodzie, dlaczego Python nie kompiluje f2 tego samego kodu bajtowego co f1?Dlaczego Python nie ocenia arytmetyki liczb stałych przed kompilacją do kodu bajtowego?

Czy jest jakiś powód, aby tego nie robić?

>>> def f1(x): 
    x*100 

>>> dis.dis(f1) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_CONST    1 (100) 
       6 BINARY_MULTIPLY 
       7 POP_TOP 
       8 LOAD_CONST    0 (None) 
      11 RETURN_VALUE 
>>> def f2(x): 
     x*10*10 

>>> dis.dis(f2) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_CONST    1 (10) 
       6 BINARY_MULTIPLY 
       7 LOAD_CONST    1 (10) 
      10 BINARY_MULTIPLY 
      11 POP_TOP 
      12 LOAD_CONST    0 (None) 
      15 RETURN_VALUE 

Odpowiedz

70

To dlatego x może mieć __mul__ metodę z efektami ubocznymi. x * 10 * 10 wzywa __mul__ dwukrotnie, natomiast x * 100 nazywa go tylko raz:

>>> class Foo(object): 
...  def __init__ (self): 
...    self.val = 5 
...  def __mul__ (self, other): 
...    print "Called __mul__: %s" % (other) 
...    self.val = self.val * other 
...    return self 
... 
>>> a = Foo() 
>>> a * 10 * 10 
Called __mul__: 10 
Called __mul__: 10 
<__main__.Foo object at 0x1017c4990> 

Automatycznie składane stałe i tylko wywołanie __mul__ raz może zmienić zachowanie.

Możesz uzyskać żądaną optymalizację, zmieniając kolejność operacji w taki sposób, że stałe są mnożone jako pierwsze (lub, jak wspomniano w komentarzach, używając nawiasów, aby je pogrupować w taki sposób, że są używane tylko razem, niezależnie od pozycji), czyniąc wyraźną chęć do zwijania się zdarzyć:

>>> def f1(x): 
...  return 10 * 10 * x 
... 
>>> dis.dis(f1) 
    2   0 LOAD_CONST    2 (100) 
       3 LOAD_FAST    0 (x) 
       6 BINARY_MULTIPLY  
       7 RETURN_VALUE 
+0

Należy zauważyć, że nawet nic nie jest przekazywane do metody, wynik "dis" jest wciąż taki sam, tzn. Jeśli zawartość metody jest: 'x = 9; y = x * 10 * 10; ', wynik jest wciąż tym samym mianem. ładowanie const dwa razy. Wydaje się, że Python nie wykonuje optymalizacji całej metody? –

+5

@ SanjayT.Sharma: Kompilator wciąż nie wie, czym jest "x", więc musi go bezpiecznie odtworzyć. Wszechstronne funkcje introspekcji i dynamicznego modyfikowania środowiska wykonawczego umożliwiają zmianę typu 'x' w locale funkcji. –

+6

'x * (10 * 10)' powinno również działać, i jest nieco bardziej wyraźne. – WolframH

17

Python ocenia wyrażeń z left to right. Dla f2() oznacza to, będzie to pierwszy ocenić x*10 a następnie pomnożyć wynik przez 10. Spróbuj:

Spróbuj:

def f2(x): 
    10*10*x 

ta powinna być zoptymalizowana.

+0

To działało dla mnie w cpythonie 2.7.2. – jcollado

+1

Super! również potwierdzone na cpython 2.6.6 – Jonathan