Ответ 1
Эта методика оптимизации называется постоянной сгибанием.
Причина постоянного сгибания, происходящего в последнем коде, но не в первом, заключается в том, что Python имеет динамическую типизацию, а в математике произведение действительных чисел является коммутативным и свободно ассоциативным, это не так в Python в общем случае, так как все переменные не содержат действительных чисел, и не могут заранее знать типы.
Умножение в Python left-associative - 24 * 60 * 60 * n
ведет себя как (((24 * 60) * 60) * n)
, что в свою очередь неявно выполняется подобно
(24).__mul__(60).__mul__(60).__mul__(n)
или
(n).__rmul_((24).__mul__(60).__mul__(60))
тогда как n * 24 * 60 * 60
, который (((n * 24) * 60) * 60)
может вести себя как
n.__mul__(24).__mul__(60).__mul__(60)
или
(24).__rmul__(n).__mul__(60).__mul__(60)
Поскольку мы не можем заранее знать поведение n.__mul__
, мы не можем сгинуть константу во втором случае. Рассмотрим этот пример смешного класса, который возвращает подкласс int
, который определяет __mul__
/__rmul__
как возвращающий сумму операндов вместо продукта:
class MultiplyAsAdd(int):
def __mul__(self, other):
return MultiplyAsAdd(self + other)
def __rmul__(self, other):
return MultiplyAsAdd(other + self)
Тогда
>>> (lambda n: 24*60*60*n)(MultiplyAsAdd(5))
86405
>>> (lambda n: n*24*60*60)(MultiplyAsAdd(5))
149
Очевидно, было бы неправильно, если бы Python заключил в скобки продукт в качестве n*(24*60*60)
в последнем случае.