Suppose I have a class:
class Car(object):
def __init__(self, name, tank_size=10, mpg=30):
self.name = name
self.tank_size = tank_size
self.mpg = mpg
I put together a list of the cars I’m looking at:
cars = []
cars.append(Car("Toyota", 11, 29))
cars.append(Car("Ford", 15, 12))
cars.append(Car("Honda", 12, 25))
If I assign a name to my current favorite (a “pointer” into the list, if you will):
my_current_fav = cars[1]
I can easily access the attributes of my current favorite:
my_current_fav.name # Returns "Ford"
my_current_fav.tank_size # Returns 15
my_current_fav.mpg # Returns 12
Here’s where I start getting foggy. I would like to provide additional “computed” attributes only for my current favorite (let’s assume these attributes are too “expensive” to store in the original list and are easier to just compute):
my_current_fav.range # Would return tank_size * mpg = 180
# (if pointing to "Ford")
In my case, I just capitulated and added ‘range’ as an attribute of Car(). But what if storing ‘range’ in each Car() instance was expensive but calculating it was cheap?
I considered making ‘my_current_fav’ a sub-class of Car(), but I couldn’t figure out a way to do that and still maintain my ability to simply “point” ‘my_current_favorite’ to an entry in the ‘cars’ list.
I also considered using decorators to compute and return ‘range’, but couldn’t figure out a way to also provide access to the attributes ‘name’, ‘mpg’, etc.
Is there an elegant way to point to any item in the list ‘cars’, provide access to the attributes of the instance being pointed to as well as provide additional attributes not found in the class Car?
Additional information:
After reading many of your answers, I see there is background information I should have put into the original question. Rather than comment on many answers individually, I’ll put the additional info here.
This question is a simplification of a more complicated issue. The original problem involves modifications to an existing library. While making range a method call rather than an attribute is a good way to go, changing
some_var = my_current_favorite.range
to
some_var = my_current_favorite.range()
in many existing user scripts would be expensive. Heck, tracking down those user scripts would be expensive.
Likewise, my current approach of computing range for every car isn’t “expensive” in Python terms, but is expensive in run-time because the real-world analog requires slow calls to the (embedded) OS. While Python itself isn’t slow, those calls are, so I am seeking to minimize them.
This is easiest to do for your example, without changing
Car, and changing as little else as possible, with__getattr__:Any attribute not found on an instance of
Favoritewill be looked up on theFavoriteinstancescarattribute, which you set when you make theFavorite.Your example of
rangeisn’t a particularly good one for something to add toFavorite, because it should just be apropertyof car, but I used it for simplicity.Edit: Note that a benefit of this method is if you change your favorite car, and you’ve not stored anything car-specific on
Favorite, you can change the existing favorite to a different car with: