python - const 引用在 Python 中的等价物是什么?

标签 python python-3.x const-correctness information-hiding

关于 Python 中缺乏 const 正确性和缺乏真正的 private 成员,存在很多争论(至少在 SO 上)。我正在努力适应Python式的思维方式。

假设我想实现一个油箱。它有容量,可以重新填充,也可以消耗燃料。所以我会按如下方式实现它:

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

到目前为止,我已经在代码中加入了一定程度的 const 正确性:通过不实现 capacity 的属性 setter ,我通知客户端 capacity 对象构造后就无法更改。 (尽管从技术上讲,通过直接访问 _capacity 总是可以实现这一点。)同样,我告诉客户端您可以读取 level 但请使用 consumerefill 方法来更改它。

现在,我实现了一辆带有 FuelTankCar:

class Car:
    def __init__(self, consumption):
        self._consumption = consumption
        self._tank = FuelTank(60.0)


    @property
    def consumption(self):
        return self._consumption

    def run(self, kms):

        required_fuel = kms * self._consumption / 100

        if required_fuel > self._tank.level:
            raise ValueError("Insufficient fuel to drive %f kilometers" %
                             kms)

        self._tank.consume(required_fuel)

    def refill_tank(self, amount):

        self._tank.refill(amount)

我再次暗示客户端不应该直接访问_tank。他唯一能做的就是refill_tank

一段时间后,我的客户提示说他需要一种方法来知道油箱中还剩下多少燃料。因此,我决定添加第二种方法,名为 tank_level

    def tank_level(self):
        return self._tank.level

由于担心 tank_capacity 很快就会成为必需的,我开始在 Car 中添加包装方法来访问 FuelTank 的所有方法,除了消费。这显然不是一个可扩展的解决方案。因此,我也可以将以下 @property 添加到 Car

    @property
    def tank(self):
        return self._tank

但是现在客户端无法理解不应调用 consume 方法。事实上,此实现仅比仅将 tank 设为公共(public)属性稍微安全一些:

    def __init__(self, consumption):
        self._consumption = consumption
        self.tank = FuelTank(60.0)

并节省额外的代码行。

总而言之,我有三个选择:

  1. Car 中为允许 Car 客户端使用的 FuelTank 的每个方法编写一个包装方法(不可扩展且难以扩展)维持)。
  2. 保持 _tank (名义上)私有(private),并允许客户端将其作为仅 getter 属性进行访问。这只能保护我免受过度“白痴”客户端的侵害,该客户端可能会尝试将 tank 设置为完全不同的对象。但是,否则就和公开tank一样好。
  3. 公开 tank,并询问客户“请不要调用 Car.tank.consume

我想知道哪个选项被认为是 Pythonic 世界中的最佳实践?

注意,在 C++ 中,我会在 Tank 类中创建 levelcapacity 方法 const 并声明 tank 作为 Car 的私有(private)成员,使用 get_tank() 方法返回对 tank 的 const 引用。这样,我只需要一个用于 refill 的包装方法,并且我为客户端提供了对 Tank 的任何 const 成员的完全访问权限(零 future 的维护费用)。就个人喜好而言,我发现这是 Python 所缺乏的一个重要功能。


澄清。

我知道在 C++ 中可以实现的东西在 Python 中几乎肯定是不可能实现的(由于它们的根本差异)。我主要想弄清楚这三种选择中哪一种是最 Pythonic 的?选项(2)比选项(3)有什么特别的优势吗?有没有办法使选项(1)可扩展?

最佳答案

由于 Python 没有任何标记方法的标准方式 const ,不可能有一种内置方式来提供限制对它们的访问的值(,一个对象)。然而,有两种习惯用法可以用来提供类似的东西,这两种习惯用法都可以通过 Python 的动态属性和反射工具变得更容易。

如果要设计一个类来支持此用例,您可以拆分其接口(interface):仅提供“真实”类型的读取接口(interface),然后提供提供写入接口(interface)的包装器并将任何未知调用转发给读者:

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=derived; ,所以我们必须使用一个单独的对象。它也不能从一个构建:客户端不能认为他们应该这样做以获得写访问权。

如果该类不是为此设计的,您可以反转包装:

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)

这显然需要更多工作,因为您必须创建一个完整黑名单(或白名单,尽管这样会更难制作有用的错误消息)。不过,如果类是协作的,您可以将此方法与标签(例如,函数属性)结合使用,以避免显式列表和拥有两个类。

关于python - const 引用在 Python 中的等价物是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60778937/

相关文章:

python - 在一个 python 文件上运行 Sphinx 的最简单方法

python-3.x - 可以多次运行同一个模型吗?

具有 volatile 和外部数据访问的 C++ 常量正确性

python - PySide (Qt) - 布局不起作用

python - 用 python igraph 标记图中的边

python - Urllib 返回 html 但没有结束段落标记

python - 关于数据结构和算法书中代码的问题

c++ - "just-in-time calculation"是否适合用于可变?

c++ - 如何处理const对象中非const引用成员的初始化?

python - 系统/pip readline 版本不匹配