Недавно я писал о том, как работает некоторая магия в Python и как вы можете использовать эту магию, реализуя так называемые методы двойного подчеркивания. Чтобы проиллюстрировать это, я построил игрушечный пример структуры данных Set. Как и любая хорошая структура данных, набор имеет размер — если бы мы собирались протестировать его (а мы должны это сделать, потому что тесты покупают вам свободу и гибкость для легкого изменения вашего кода по мере его развития), он мог бы выглядеть так: :
def test_size(): empty = Set() has_stuff = Set() has_stuff.add("waffles") has_stuff.add("cheese") assert empty.size() == 0 assert has_stuff.size() == 2
Теперь, когда у нас есть провальный тест, давайте сделаем так, чтобы он прошел
class Set: def __init__(self): self.__values = [] self.__size = 0 def add(self, value): if value in self.__values: return self.__size += 1 self.__values.append(value) def size(self): return self.__size
В этот момент я почти слышу, как какой-то благонамеренный питонист кричит:
"Привет! Это питон, нам весь этот багаж не нужен. Давайте просто воспользуемся переменной экземпляра и избежим лишнего кода. Это проще!»
def test_size(): empty = ListSet() has_stuff = Set() has_stuff.add("waffles") has_stuff.add("cheese") assert empty.__size == 0 assert has_stuff.__size == 2
Вздох. Питон, я люблю тебя, но иногда ты меня действительно ранишь.
Проблема с раскрытием переменных экземпляра
Теперь нет ничего принципиально неправильного в раскрытии переменных экземпляра. Однако это выбор дизайна, и каждый дизайн решает одну проблему, создавая другие. Давайте посмотрим на связанный пример, определяющий размер структуры данных LinkedList:
class Node: def __init__(self, val): self.__value = val self.__next = None def add(self, next_): self.__next = next_ def value(self): return self.__value def next(self): return self.__next def size(self): # size must be at least the current node size = 1 next_ = self.next() # continue to iterate while we have more nodes…