const引用的Pythonic等价物是什么?

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")

关于Python中缺少
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
    设置为一个完全不同的对象的“白痴”客户端的攻击。但是,否则就等同于公开坦克
  • 坦克
    公之于众,并询问客户“请不要打电话给
    汽车.坦克.消费
  • 我想知道在Pythonic世界中,哪一个选项被认为是最佳实践

    < C++ >我在C++中做了<代码> >级别>代码>代码>容量<代码>方法>代码> const <代码> <代码>坦克> /Cuff>类,并声明为代码>坦克>代码>作为<代码> > <代码>的私有成员,使用<代码> GETY-TANK()/<代码>方法返回<代码> const >参考<代码>坦克< /代码>。这样,我只需要一个包装器方法来
    重新加注
    ,并且我允许客户完全访问
    储罐
    的任何
    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)