Пример
Допустим, мы Мы собираемся создать структуру данных multiset (неупорядоченную коллекцию, допускающую повторяющиеся значения), которую мы реализуем с помощью dict, чтобы отслеживать количество раз появления каждого элемента. Нам нужен конструктор, который будет инициализировать набор списком значений, который может выглядеть примерно так:
Код: Выделить всё
class Multiset():
def __init__(self, iterable):
self._counts = {}
self._size = 0
for item in iterable:
self._counts[item] = self._counts.get(item, 0) + 1
self._size += 1
Код: Выделить всё
def __add__(self, other):
combined_counts = {item : self._counts.get(item, 0) + other._counts.get(item, 0)
for item in self._counts.keys() | other._counts.keys()}
combined_size = self._size + other._size
#return a new Multiset whose _counts is combined_counts and _size is combined_size
1. Открытый конструктор, частный фабричный метод
Мы могли бы оставить конструктор как есть и создать частный фабричный метод, который создает новый объект, а затем напрямую устанавливает атрибуты:
Код: Выделить всё
@classmethod
def _from_counts(cls, counts, size):
result = cls([])
result._counts = counts
result._size = size
return result
2. Частный конструктор, общедоступный фабричный метод
Вместо этого мы могли бы изменить метод __init__ так, чтобы он напрямую принимал внутренние данные, и предоставить фабричный метод для внешнего использования.
Код: Выделить всё
# this is only intended to be called from inside our class
def __init__(self, counts, size):
self._counts = counts
self._size = size
# this is part of the public API
@classmethod
def of(cls, iterable):
...
3. Конструктор с ключевыми аргументами
Мы могли бы добавить в метод __init__ функциональность для создания объекта любым способом в зависимости от того, какие аргументы ему переданы:
Код: Выделить всё
def __init__(self, iterable=None, counts=None, size=None):
if counts is not None:
self._counts = counts
self._size = size
else:
#initialize with the items in iterable
4. Закрытый фабричный метод, который вызывает __new__ напрямую
Как и вариант 1, но мы можем создать объект без его инициализации, а затем напрямую заполнить его данными:
Код: Выделить всё
@classmethod
def _from_counts(cls, counts, size):
result = cls.__new__(cls)
result._counts = counts
result._size = size
return result
Вопрос
Лучше ли какой-либо из этих методов, чем остальные, или есть другой способ, который я не рассмотрел? Кажется, это достаточно распространенный вариант использования, и другие уже разработали решение, но я не смог найти никакой информации о нем в Интернете.
Подробнее здесь: https://stackoverflow.com/questions/791 ... -in-python
Мобильная версия