Было много дискуссий (по крайней мере, о SO) об отсутствии const
-корректности и отсутствии истинных private
членов в Python. Я пытаюсь привыкнуть к мышлению Pythoni c.
Предположим, я хочу реализовать топливный бак. Он имеет емкость, может быть пополнен или из него можно потреблять топливо. Поэтому я реализовал бы это следующим образом:
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
, я сообщаю клиенту, что 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
publi c:
def __init__(self, consumption):
self._consumption = consumption
self.tank = FuelTank(60.0)
и сохранить дополнительные строки кода.
Итак, в заключение, я ' у нас есть три варианта:
- Запись метода-обертки в
Car
для каждого метода FuelTank
, который разрешено использовать клиенту Car
(не масштабируемый и трудно поддерживаемый). - Сохранение
_tank
(номинально) закрытого типа и предоставление клиенту доступа к нему в качестве свойства только для получения. Это только защищает меня от чрезмерно «идиотского» клиента, который может попытаться установить tank
на совершенно другой объект. Но в остальном все равно, что сделать tank
publi c. - Сделать
tank
publi c и попросить клиента "пожалуйста, не звоните Car.tank.consume
"
Мне было интересно, какой вариант считается лучшим в мире Pythoni c?
Примечание в C ++ Я бы сделал level
и capacity
методы const
в Tank
класс и объявил tank
как закрытый член Car
с методом get_tank()
, который возвращает const
-ссылку на tank
. Таким образом, мне понадобится только один метод-обертка для refill
, и я предоставлю клиенту полный доступ ко всем const
членам Tank
(с нулевыми затратами на обслуживание в будущем). По вкусу я нахожу это важной особенностью, которой не хватает Python.
Разъяснение.
Я понимаю, чего можно достичь в C ++ почти наверняка невозможно достичь в Python (из-за их фундаментальных отличий). В основном я пытаюсь выяснить, какая из трех альтернатив является самой Pythoni c? Есть ли у варианта (2) какое-либо конкретное преимущество перед вариантом (3)? Есть ли способ сделать вариант (1) масштабируемым?