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

2 投票
7 回答
663 浏览
提问于 2025-04-17 02:52
some_var = my_current_favorite.range
some_var = my_current_favorite.range()

假设我有一个类:

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

我整理了一份我正在关注的汽车列表:

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

如果我给我现在最喜欢的车起个名字(可以理解为指向列表中的一个“指针”):

my_current_fav = cars[1]

我可以很方便地访问我现在最喜欢的车的属性:

my_current_fav.name       # Returns "Ford"
my_current_fav.tank_size  # Returns 15
my_current_fav.mpg        # Returns 12

这时候我有点迷糊了。我想为我现在最喜欢的车提供一些额外的“计算”属性(假设这些属性存储在原始列表中太“昂贵”,而计算起来更简单):

my_current_fav.range      # Would return tank_size * mpg = 180
                          # (if pointing to "Ford")

在我的情况下,我最终妥协了,把'范围'作为Car()的一个属性。但如果在每个Car()实例中存储'范围'很昂贵,而计算它却很便宜呢?

我考虑过把'my_current_fav'做成Car()的一个子类,但我找不到办法让它同时保持简单地“指向”'cars'列表中的一个条目。

我也考虑过使用装饰器来计算和返回'range',但我不知道怎么才能同时访问'名称'、'mpg'等属性。

有没有优雅的方法可以指向'cars'列表中的任何项目,提供对所指向实例的属性的访问,同时提供在Car类中找不到的额外属性?

补充信息:
在阅读了你们的许多回答后,我意识到我应该在原始问题中提供一些背景信息。与其单独评论许多答案,我把额外的信息放在这里。

这个问题是一个更复杂问题的简化版。原始问题涉及对现有库的修改。虽然把范围作为方法调用而不是属性是个不错的选择,但在许多现有用户脚本中进行这样的更改会很昂贵。实际上,追踪那些用户脚本也是一项昂贵的工作。

同样,我目前为每辆车计算范围的方法在Python的术语中并不“昂贵”,但在运行时却是昂贵的,因为现实世界的类比需要慢速调用(嵌入式)操作系统。虽然Python本身并不慢,但这些调用确实很慢,所以我希望尽量减少它们。

7 个回答

0

如果你能访问这个类(听起来你是可以的),那就直接在这个类里面创建一个函数吧。

def range(self):
    return self.tank_size * self.mpg
0

你提到 my_current_fav 是一个“指针”——我只是想确保你明白,实际上,

my_current_fav = cars[1]

是把一个名字绑定到 cars[1] 上——换句话说,my_current_fav is cars[1] == True。这里并没有真正的指向。如果你真的想要一种指针的风格,你可以这样做:

class Favorite(object):
    def __init__(self, car_list, index):
        self.car_list = car_list
        self.index = index
    def __getattr__(self, attr):
        return getattr(self.car_list[self.index], attr)
    def __index__(self):
        return self.index
    @property
    def range(self):
        return self.mpg * self.tank_size

my_current_fav = Favorite(cars, 1)

print my_current_fav.name
print my_current_fav.range
print cars[my_current_fav]
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

如果在 Favorite 的实例中找不到某个属性,程序会去 Favorite 实例的 car 属性中查找,这个 car 属性是在你创建 Favorite 时设置的。

你提到的 range 其实不太适合放在 Favorite 里,因为它应该只是 car 的一个 property(属性),我用它只是为了简单说明。

补充说明:这个方法的一个好处是,如果你换了你最喜欢的车,而在 Favorite 中没有存储任何与车相关的特定信息,你可以用下面的代码把现有的最爱车换成另一辆车:

my_current_fav.car = cars[0] # or Car('whatever')

撰写回答