如何缓存方法返回的值,这取决于其他类的属性?

Pat*_*ick 5 python oop

简化代码(无缓存)

首先是一段简化的代码,我将用它来解释这个问题.

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)
Run Code Online (Sandbox Code Playgroud)

问题

Car是主要的班级.它有一个Engine和一个Chassis.

average_acceleration() 使用来自引擎和机箱的属性,并执行非常耗时的计算.

velocity()另一方面,执行非常简单的计算,但使用的计算值 average_acceleration()

distance()velocity功能传递给integrate()

现在,integrate()多次velocity()打电话,每次打电话average_acceleration().考虑到返回的average_acceleration()值仅取决于Engine和Chassis,因此需要缓存返回的值average_acceleration().

我的想法

第一次尝试(不工作)

我想以下列方式使用memoize装饰器:

    @memoize
    def average_acceleration(self, engine=self.engine, chassis=self.chassis):
        # !!! this calculations are actually very time consuming
        return engine.capacity / chassis.weight
Run Code Online (Sandbox Code Playgroud)

但它不会像我想的那样工作,因为发动机和底盘是可变的.因此,如果做:

chassis.weight = new_value
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我将参数添加accelerationvelocity()方法中.添加了该选项后,我acceleration只计算一次distance()方法,我知道底盘和引擎对象没有改变,我把这个值传递给了速度.

底线

我写的代码完成了我需要它做的事情,但是我很好奇你是否能想出更好/更干净的东西?