假设我有一个班级:
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")
在我的情况下,我只是投降并添加'range'作为Car()的属性。但是,如果在每个Car()实例中存储'range'是昂贵但计算它很便宜呢?
我考虑过让'my_current_fav'成为Car()的一个子类,但我无法找到办法做到这一点,并且仍然能够将'my_current_favorite'简单地“指向”'汽车中的一个条目'list。
我还考虑使用装饰器来计算和返回'范围',但是无法找到一种方法来提供对属性'name','mpg'等的访问。
是否有一种优雅的方式指向列表'cars'中的任何项目,提供对所指向实例的属性的访问,以及提供在Car类中找不到的其他属性?
其他信息:
在阅读了许多答案之后,我看到我应该在原始问题中加入背景信息。我不会单独评论许多答案,而是将其他信息放在这里。
这个问题是对更复杂问题的简化。最初的问题涉及对现有库的修改。虽然使用方法调用而不是属性是一个很好的方法,但是改变
some_var = my_current_favorite.range
到
some_var = my_current_favorite.range()
在许多现有的用户脚本中都很昂贵。哎呀,追踪这些用户脚本会很贵。
同样,我目前对每辆车的计算范围的方法在Python术语中并不“昂贵”,但在运行时非常昂贵,因为真实世界的模拟需要慢速调用(嵌入式)OS。虽然Python本身并不慢,但这些调用是,所以我正在寻求最小化它们。
答案 0 :(得分: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
属性中查找,您在创建Favorite
时设置该属性。
range
的示例对于添加到Favorite
的内容并不是特别好,因为它应该只是汽车的property
,但为了简单起见我使用了它。
修改:请注意,此方法的一个好处是,如果您更改自己喜爱的汽车,并且未在Favorite
上存储任何特定于汽车的汽车,则可以更改现有的收藏与另一辆车:
my_current_fav.car = cars[0] # or Car('whatever')
答案 1 :(得分:0)
如果你有权访问该类(听起来像你这样做),只需在类中创建一个函数。
def range(self):
return self.tank_size * self.mpg
答案 2 :(得分:0)
关于您的示例,您可以使range
成为可以按需计算的类Car
的只读property。不需要额外的课程。
答案 3 :(得分:0)
为什么不创建一个方法:
class Car(object):
def __init__(self, name, tank_size=10, mpg=30):
self.name = name
self.tank_size = tank_size
self.mpg = mpg
def range(self):
return self.tank_size * self.mpg
答案 4 :(得分:0)
range()
之类的声音应该是班级的一种方法。方法非常便宜 - 对象不存储它们。缺点是每次访问range
的值时都会计算出来。
class Car(object):
def __init__(self, name, tank_size=10, mpg=30):
[AS ABOVE]
def range(self):
return self.tank_size * self.mpg
如果您希望它的行为类似于字段,即只计算一次,则可以在range
方法中将值存储在对象中:
def range(self):
if not hasattr(self,'_rangeval'):
self._rangeval = self.tank_size * self.mpg
return self._rangeval
这利用了您可以在对象中动态创建字段的事实。
我不明白为什么当默认的计算值为300时,默认值为180.如果这种奇怪的行为很重要,则需要设置另一个标志以查看其他参数是否已初始化为默认值或不。
答案 5 :(得分:0)
我不确定我理解你要做什么,所以我将对你正在思考的内容进行一些不同的理解。
在我的情况下,我只是投降并添加'range'作为Car()的属性。但是,如果在每个Car()实例中存储'range'是昂贵但计算它很便宜呢?
首先,担心“昂贵”的事情通常不是Pythonic。如果真的很重要,那么大部分时间你都会使用低级语言。但一般来说,如果你想要计算而不是存储某些东西,那么自然的方法就是使用一种方法。将它添加到Car类。每个对象不花费,除非您基于每个对象显式替换该方法。
以下是它的工作原理:当您拨打a_car.range()
时,首先会在range
中查找a_car
属性。如果在那里找不到,那么(在这里跳过许多详细信息!)a_car
的类被标识为Car
,并在那里查找属性。您将range
定义为Car
方法,因此可以在那里找到它,并确定它是实际可调用的东西,因此它被调用。作为一种特殊的语法规则,a_car
作为方法的第一个参数传递(所有这些都部分解释了为什么你需要有一个显式参数 - 按惯例命名为self
- 对于Python中的方法,不同于许多其他语言具有隐式this
)。
我考虑过让'my_current_fav'成为Car()的一个子类,但我无法找到办法做到这一点,并且仍然能够将'my_current_favorite'简单地“指向”'汽车中的一个条目'list。
您绝对可以将Car
的子类存储在与一堆普通Car
相同的列表中。没问题。哎呀,如果你愿意的话,你可以将Banana
存储在同一个列表中,作为一堆Car
s; Python是动态类型的,并不关心。它将确定什么样的对象在它变得相关的确切时刻。
您不能轻易做到的是使现有Car
成为MyFavouriteCar
。如果你创建了MyFavouriteCar
引用的新my_favourite_car
(请不要说“指向”,请对象引用是更高级别的抽象,与Java不同,没有“空指针”) Python - None
是一个对象),然后你可以用它替换列表中的现有汽车,但它仍然是一个不同的对象(即使它以某种方式基于它替换的原始Car
)。你可以这样设计,这无关紧要;或者你可以诉诸邪恶的hackery(我不会在这里解释);或者你可以(在你的情况下好多了)只为所有汽车提供功能,因为它真的是免费的。
来自Python的禅宗:特殊情况不够特别。
答案 6 :(得分:0)
你谈到my_current_fav
是一个'指针' - 我只是想确保你意识到这一点,实际上,
my_current_fav = cars[1]
将名称绑定到cars[1]
- 换句话说,my_current_fav is cars[1] == True
。没有任何指点。如果你真的想要一个指针stlye,你可以做这样的事情:
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]