指向另一个对象的属性并添加自己的属性
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 个回答
如果你能访问这个类(听起来你是可以的),那就直接在这个类里面创建一个函数吧。
def range(self):
return self.tank_size * self.mpg
你提到 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]
对于你的例子,最简单的方法是不改变 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')