const引用的Pythonic等价物是什么?
关于Python中缺少const引用的Pythonic等价物是什么?,python,python-3.x,const-correctness,information-hiding,Python,Python 3.x,Const Correctness,Information Hiding,关于Python中缺少const-正确性和缺少真正的private成员,已经有很多争论(至少是这样)。我正在努力适应蟒蛇式的思维方式 假设我想要实现一个燃料箱。它有容量,可以重新加注,也可以消耗燃料。因此,我将按如下方式实施: class FuelTank: def __init__(self, capacity): if capacity < 0: raise ValueError("Negative capacity")
const
-正确性和缺少真正的private
成员,已经有很多争论(至少是这样)。我正在努力适应蟒蛇式的思维方式
假设我想要实现一个燃料箱。它有容量,可以重新加注,也可以消耗燃料。因此,我将按如下方式实施:
class FuelTank:
def __init__(self, capacity):
if capacity < 0:
raise ValueError("Negative capacity")
self._capacity = capacity
self._level = 0.0
@property
def capacity(self):
return self._capacity
@property
def level(self):
return self._level
def consume(self, amount):
if amount > self.level:
raise ValueError("amount higher than tank level")
self._level -= amount
def refill(self, amount):
if amount + self.level > self.capacity:
raise ValueError("overfilling the tank")
self._level += amount
我再次暗示,客户端不应该直接访问\u tank
。他能做的唯一一件事就是给油箱重新加油
过了一段时间,我的客户抱怨说他需要一种方法来知道油箱里还有多少燃油。因此,我决定添加第二种方法,称为tank\u level
def tank_level(self):
return self._tank.level
由于担心不久将需要油箱容量
,我开始在Car
中添加包装器方法,以访问油箱
的所有方法,除了消耗
。这显然不是一个可扩展的解决方案。因此,我也可以将以下@属性
添加到汽车
@property
def tank(self):
return self._tank
但是现在客户端无法理解不应该调用consume
方法。事实上,此实现仅比将tank
作为公共属性稍微安全一些:
def __init__(self, consumption):
self._consumption = consumption
self.tank = FuelTank(60.0)
并保存额外的代码行
总之,我有三个选择:
Car
中为FuelTank
中允许Car
的客户端使用的每个方法编写包装器方法(不可扩展且难以维护)\u tank
(名义上)私有,并允许客户端将其作为getter-only属性访问。这只会保护我免受一个可能试图将tank
设置为一个完全不同的对象的“白痴”客户端的攻击。但是,否则就等同于公开坦克坦克
公之于众,并询问客户“请不要打电话给汽车.坦克.消费
”重新加注
,并且我允许客户完全访问储罐
的任何const
成员(未来的维护成本为零)。就品味而言,我发现这是Python所缺乏的一个重要特性
澄清。
<>我明白在C++中可以实现的几乎是不可能在Python中实现的(因为它们的根本不同)。我主要想弄清楚的是,三个备选方案中哪一个是最具python风格的?方案(2)是否比方案(3)有任何特殊优势?有没有一种方法可以使选项(1)可伸缩?由于Python没有任何标准的方法来标记方法
const
,因此不能有一种内置的方法来提供限制访问它们的值(即对象)。然而,有两种习惯用法可以用来提供类似的东西,这两种习惯用法都通过Python的动态属性和反射功能变得更容易
如果一个类被设计为支持这个用例,您可以拆分它的接口:只提供“real”类型的读接口,然后提供一个提供写接口并将任何未知调用转发给读卡器的包装器:
class ReadFoo:
def __init__(self): self._bar=1
@property
def bar(self): return self._bar
class Foo:
def __init__(self): self._foo=ReadFoo()
def read(self): return self._foo
def more(self): self._foo._bar+=1
def __getattr__(self,n): return getattr(self._foo,n)
class Big:
def __init__(self): self._foo=Foo()
@property
def foo(self): return self._foo.read()
请注意,Foo
不会从ReadFoo
继承;Python和C++之间的另一个区别是Python不能表达<代码> Base&B=派生;<代码>,因此我们必须使用单独的对象。它也不能由一个来构造:客户端不能认为他们应该这样做是为了获得写访问权
如果类不是为此而设计的,则可以反转包装:
class ReadList:
def __init__(self,l): self._list=l
def __getattr__(self,n):
if n in ("append","extend","pop",…):
raise AttributeError("method would mutate: "+n)
return getattr(self._list,n)
这显然是更多的工作,因为您必须创建一个完整的黑名单(或白名单,尽管这样做会更难生成有用的错误消息)。如果类是协作的,那么可以使用这种方法来使用<强>标签> /St>(例如,函数属性)来避免显式列表并具有两个类。不太熟悉C++,声明为“代码> > const <代码>会使对象的方法不可访问吗?在Python中,您可能对此感兴趣:.@deceze返回const引用将阻止对返回的引用直接调用非const方法(例如消耗燃料,导致状态更改)。它是一种只允许在同时具有“setter”的类型上调用“getter”方法(标记为const)的方法。@PatrickArtner这是一种适用于小对象(如本玩具示例中的小对象)的完全有效的解决方案。但假设(智能)
燃油箱
存储了所有加油事件的日志。那么,在返回时复制所有这些日志将是非常低效的(特别是如果客户端在特定时刻访问日志只是为了读取级别);这里的重点是具体的、狭隘的问题和具体的答案;关于什么构成良好实践的问题,特别是当重点更多地放在设计上而不是代码上时,在那里更受欢迎。(请参见描述作用域的不同)。为什么不允许用户调用消费?运行汽车发动机并不是从油箱中取出燃油的唯一方法;例如,你可以用虹吸管把煤气抽出来。我认为您在决定是否向Car
添加更多方法是可伸缩的方面还为时过早。感谢您全面的回答。如果我的孩子
class ReadList:
def __init__(self,l): self._list=l
def __getattr__(self,n):
if n in ("append","extend","pop",…):
raise AttributeError("method would mutate: "+n)
return getattr(self._list,n)