Упрощенный код (без кеширования)
Сначала фрагмент упрощенного кода, который я буду использовать для объяснения проблемы.
def integrate(self, function, range):
# this is just a naive integration function to show that
# function needs to be called many times
sum = 0
for x in range(range):
sum += function(x) * 1
return sum
class Engine:
def __init__(self, capacity):
self.capacity = capacity
class Chasis:
def __init__(self, weigth):
self.weight = weight
class Car:
def __init__(self, engine, chassis):
self.engine = engine
self.chassis = chassis
def average_acceleration(self):
# !!! this calculations are actually very time consuming
return self.engine.capacity / self.chassis.weight
def velocity(self, time):
# here calculations are very simple
return time * self.average_acceleration()
def distance(self, time):
2 + 2 # some calcs
integrate(velocity, 2000)
2 + 2 # some calcs
engine = Engine(1.6)
chassis = Chassis(500)
car = Car(engine, chassis)
car.distance(2000)
chassis.weight = 600
car.distance(2000)
Задача
Car
является основным классом. Он имеет Engine
и Chassis
.
average_acceleration()
использует атрибуты Engine и Chassis и выполняет очень трудоемкие вычисления.
velocity()
, с другой стороны, выполняет очень простые вычисления, но использует значение, вычисленное как average_acceleration()
distance()
передает velocity
функцию на integrate()
Теперь integrate()
звонит много раз velocity()
, который звонит каждый раз average_acceleration()
. Учитывая, что значение, возвращаемое average_acceleration()
, зависит только от Engine и Chassis, было бы желательно кэшировать значение, возвращаемое average_acceleration()
.
Мои идеи
Первая попытка (не работает)
Fist I об использовании memoize decorator следующим образом:
@memoize
def average_acceleration(self, engine=self.engine, chassis=self.chassis):
# !!! this calculations are actually very time consuming
return engine.capacity / chassis.weight
Но это не будет работать так, как я хочу, потому что Engine и Chassis изменчивы. Таким образом, если сделать:
chassis.weight = new_value
average_acceleration () вернет неправильное (ранее кэшированное) значение при следующем вызове.
Вторая попытка
Наконец я изменил код следующим образом:
def velocity(self, time, acceleration=None):
if acceleration is None:
acceleration = self.average_acceleration()
# here calculations are very simple
return time * acceleration
def distance(self, time):
acceleration = self.average_acceleration()
def velocity_withcache(time):
return self.velocity(time, acceleration)
2 + 2 # some calcs
integrate(velocity_withcache, 2000)
2 + 2 # some calcs
Я добавил параметр acceleration
в метод velocity()
. Добавив эту опцию, я вычисляю acceleration
только один раз в методе distance()
, где я знаю, что объекты шасси и двигателя не изменены, и я передаю это значение скорости.
Итог
Код, который я написал, делает то, что мне нужно, но мне интересно, можете ли вы придумать что-нибудь лучше / чище?