指向另一个对象的属性并添加自己的属性

JS.*_*JS. 2 python oop design-patterns

假设我有一个班级:

class Car(object):
    def __init__(self, name, tank_size=10, mpg=30):
        self.name = name
        self.tank_size = tank_size
        self.mpg = mpg
Run Code Online (Sandbox Code Playgroud)

我把我正在看的车列表放在一起:

cars = []
cars.append(Car("Toyota", 11, 29))
cars.append(Car("Ford", 15, 12))
cars.append(Car("Honda", 12, 25))
Run Code Online (Sandbox Code Playgroud)

如果我为当前收藏夹指定了一个名称(如果你愿意的话,将一个"指针"放入列表中):

my_current_fav = cars[1]
Run Code Online (Sandbox Code Playgroud)

我可以轻松访问我当前最喜欢的属性:

my_current_fav.name       # Returns "Ford"
my_current_fav.tank_size  # Returns 15
my_current_fav.mpg        # Returns 12
Run Code Online (Sandbox Code Playgroud)

这是我开始变得模糊的地方.我想仅为我当前的收藏提供额外的"计算"属性(让我们假设这些属性太"昂贵",无法存储在原始列表中,并且更容易计算):

my_current_fav.range      # Would return tank_size * mpg = 180
                          # (if pointing to "Ford")
Run Code Online (Sandbox Code Playgroud)

在我的情况下,我只是投降并添加'范围'作为Car()的属性.但是如果在每个Car()实例中存储'range'是昂贵但计算它很便宜怎么办?

我考虑让'my_current_fav'成为Car()的子类,但是我无法找到一种方法来做到这一点,并且仍然能够将'my_current_favorite'简单地"指向"'汽车'列表中的条目.

我还考虑使用装饰器来计算和返回'范围',但是无法找到一种方法来提供对属性'name','mpg'等的访问.

是否有一种优雅的方式指向列表'cars'中的任何项目,提供对所指向实例的属性的访问,以及提供Car类中没有的其他属性?

附加信息:
在阅读了许多答案之后,我看到我应该在原始问题中加入背景信息.我不会单独评论很多答案,而是在这里添加其他信息.

这个问题是对更复杂问题的简化.最初的问题涉及对现有库的修改.虽然使方法调用而不是属性是一个很好的方法,但改变

some_var = my_current_favorite.range
Run Code Online (Sandbox Code Playgroud)

some_var = my_current_favorite.range()
Run Code Online (Sandbox Code Playgroud)

在许多现有的用户脚本中会很昂贵.哎呀,追踪这些用户脚本将是昂贵的.

同样,我目前对每辆车的计算范围的方法在Python方面并不"昂贵",但在运行时昂贵的,因为真实世界的模拟需要对(嵌入式)OS的慢速调用.虽然Python本身并不慢,但这些调用是,所以我正在寻求最小化它们.

agf*_*agf 6

对于您的示例而言,这是最容易做到的,无需更改Car,并尽可能少地更改,使用__getattr__:

class Car(object):
    def __init__(self, name, tank_size=10, mpg=30):
        self.name = name
        self.tank_size = tank_size
        self.mpg = mpg

class Favorite(object):
    def __init__(self, car):
        self.car = car
    def __getattr__(self, attr):
        return getattr(self.car, attr)
    @property
    def range(self):
        return self.mpg * self.tank_size

cars = []
cars.append(Car("Toyota", 11, 29))
cars.append(Car("Ford", 15, 12))
cars.append(Car("Honda", 12, 25))

my_current_fav = Favorite(cars[1])

print my_current_fav.range
Run Code Online (Sandbox Code Playgroud)

在实例上找不到的任何属性都Favorite将在Favorite实例car属性上查找,该属性是在您创建时设置的Favorite.

你的例子range对于要添加的东西来说并不是特别好的例子Favorite,因为它应该只是一个property汽车,但我简单地使用它.

编辑:请注意,此方法的一个好处是,如果您更改自己喜欢的汽车,并且您没有存储任何汽车特定的汽车Favorite,您可以将现有的收藏夹更改为另一辆汽车:

my_current_fav.car = cars[0] # or Car('whatever')
Run Code Online (Sandbox Code Playgroud)