Подтвердить что ты не робот

Почему Python не оценивает арифметику с постоянными номерами до компиляции в байт-код?

В следующем коде почему Python не компилирует f2 в тот же байт-код, что и f1?

Есть ли причина не?

>>> 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
4b9b3361

Ответ 1

Это связано с тем, что x может иметь метод __mul__ с побочными эффектами. x * 10 * 10 дважды вызывает __mul__, а x * 100 вызывает его только один раз:

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

Автоматическое сложение констант и только вызов __mul__ один раз могут изменить поведение.

Вы можете получить нужную оптимизацию, переупорядочив операцию таким образом, чтобы константы сначала умножались (или, как указано в комментариях, с помощью круглых скобок, чтобы группировать их так, чтобы они просто работали вместе, независимо от положения), таким образом делая явное ваше стремление к свертыванию:

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

Ответ 2

Python оценивает выражения из слева направо. Для f2() это означает, что он сначала оценит x*10, а затем умножит результат на 10. Попробуйте:

Try:

def f2(x):
    10*10*x

Это должно быть оптимизировано.