const-references 的 Pythonic 等价物是什么?
What is the Pythonic equivalent of const-references?
关于const
-正确性和 Python 中缺少真正的 private
成员的争论(至少在 SO 上)有很多。我正在努力适应 Pythonic 的思维方式。
假设我想实现一个油箱。它有容量,可以重新装满,也可以从中消耗燃料。所以我会按如下方式实现它:
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
但请使用 consume
或 refill
方法来更改它。
现在,我实现了一个 Car
,它有一个 FuelTank
:
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
中除 consume
之外的所有方法。这显然不是一个可扩展的解决方案。所以,我可以选择将以下 @property
添加到 Car
@property
def tank(self):
return self._tank
但现在客户端无法理解 consume
方法不应被调用。事实上,这个实现只比将 tank
设置为 public 属性稍微安全一点:
def __init__(self, consumption):
self._consumption = consumption
self.tank = FuelTank(60.0)
并节省额外的代码行。
所以,总而言之,我有三个选择:
- 在
Car
中为 Car
的客户端允许使用的每个 FuelTank
方法编写一个包装器方法(不可扩展且难以维护)。
- 保持
_tank
(名义上)私有并允许客户端作为 getter-only 属性 访问它。这只会保护我免受可能试图将 tank
设置为完全不同的对象的过度 'idiot' 客户端的影响。但是,除此之外就和制作 tank
public. 一样好
- 制作
tank
public,询问客户"please do not call Car.tank.consume
"
我想知道哪个选项被认为是 Pythonic 世界中的最佳实践?
注意在 C++ 中我会在 Tank
class 中创建 level
和 capacity
方法 const
并声明 tank
为私有Car
的成员,使用 get_tank()
方法 returns 对 tank
的 const
引用。这样,我只需要 refill
的一个包装器方法,并且我授予客户端对 Tank
的任何 const
成员的完全访问权限(未来维护成本为零)。就品味而言,我发现这是 Python 所缺少的重要功能。
澄清。
我知道在 C++ 中可以实现的东西在 Python 中几乎肯定是不可能实现的(由于它们的根本差异)。我主要想弄清楚的是三种选择中哪一种是最 Pythonic 的?选项 (2) 是否比选项 (3) 有任何特殊优势?有没有办法使选项 (1) 可扩展?
由于 Python 没有任何标准方法来 标记 方法 const
,因此无法 构建-in 提供限制访问它们的值(即,一个对象)的方式。然而,有两个习语可用于提供类似的东西,Python 的动态属性和反射功能使它们变得更容易。
如果要设计一个class来支持这个用例,你可以拆分它的接口:在“real”类型上只提供读接口,然后提供一个包装器,该包装器提供写入接口并将任何未知调用转发到 reader:
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 不是为此设计的,您可以 反转 包装:
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)
这显然需要更多的工作,因为您必须制作一个 完整的 黑名单(或白名单,尽管这样制作有用的错误消息会有点困难)。但是,如果 class 是合作的,您可以将此方法与 tags(例如,一个函数属性)一起使用,以避免显式列出并有两个 classes.
关于const
-正确性和 Python 中缺少真正的 private
成员的争论(至少在 SO 上)有很多。我正在努力适应 Pythonic 的思维方式。
假设我想实现一个油箱。它有容量,可以重新装满,也可以从中消耗燃料。所以我会按如下方式实现它:
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
但请使用 consume
或 refill
方法来更改它。
现在,我实现了一个 Car
,它有一个 FuelTank
:
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
中除 consume
之外的所有方法。这显然不是一个可扩展的解决方案。所以,我可以选择将以下 @property
添加到 Car
@property
def tank(self):
return self._tank
但现在客户端无法理解 consume
方法不应被调用。事实上,这个实现只比将 tank
设置为 public 属性稍微安全一点:
def __init__(self, consumption):
self._consumption = consumption
self.tank = FuelTank(60.0)
并节省额外的代码行。
所以,总而言之,我有三个选择:
- 在
Car
中为Car
的客户端允许使用的每个FuelTank
方法编写一个包装器方法(不可扩展且难以维护)。 - 保持
_tank
(名义上)私有并允许客户端作为 getter-only 属性 访问它。这只会保护我免受可能试图将tank
设置为完全不同的对象的过度 'idiot' 客户端的影响。但是,除此之外就和制作tank
public. 一样好
- 制作
tank
public,询问客户"please do not callCar.tank.consume
"
我想知道哪个选项被认为是 Pythonic 世界中的最佳实践?
注意在 C++ 中我会在 Tank
class 中创建 level
和 capacity
方法 const
并声明 tank
为私有Car
的成员,使用 get_tank()
方法 returns 对 tank
的 const
引用。这样,我只需要 refill
的一个包装器方法,并且我授予客户端对 Tank
的任何 const
成员的完全访问权限(未来维护成本为零)。就品味而言,我发现这是 Python 所缺少的重要功能。
澄清。
我知道在 C++ 中可以实现的东西在 Python 中几乎肯定是不可能实现的(由于它们的根本差异)。我主要想弄清楚的是三种选择中哪一种是最 Pythonic 的?选项 (2) 是否比选项 (3) 有任何特殊优势?有没有办法使选项 (1) 可扩展?
由于 Python 没有任何标准方法来 标记 方法 const
,因此无法 构建-in 提供限制访问它们的值(即,一个对象)的方式。然而,有两个习语可用于提供类似的东西,Python 的动态属性和反射功能使它们变得更容易。
如果要设计一个class来支持这个用例,你可以拆分它的接口:在“real”类型上只提供读接口,然后提供一个包装器,该包装器提供写入接口并将任何未知调用转发到 reader:
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 不是为此设计的,您可以 反转 包装:
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)
这显然需要更多的工作,因为您必须制作一个 完整的 黑名单(或白名单,尽管这样制作有用的错误消息会有点困难)。但是,如果 class 是合作的,您可以将此方法与 tags(例如,一个函数属性)一起使用,以避免显式列出并有两个 classes.