Иногда я хочу написать класс с переменными экземпляра, которые, с одной стороны, должны быть инициализированы внутри __init__
, но, с другой стороны, позже могут быть обновлены с помощью других функций (function_1
- function_3
) либо из внутри после какого-либо события или снаружи.
Все функции обновления зависят от одного и того же входного параметра, но работают одинаково при инициализации и последующем обновлении. Они могут быть либо членами класса (@staticmethod
или нет), либо служебными функциями, импортированными из некоторого пакета.
Для более позднего обновления, функция обновления «meta» (update_member_variables
) явно должна быть процедурой т.е. ничего не возвращать и только изменять переменные-члены как побочный эффект. Однако для инициализации лучше использовать чистую функцию и возвращать значения переменных, чтобы их можно было присвоить переменным внутри __init__
.
Этот конфликт всегда приводит меня к следующему циклу дублированного кода: объявления вне __init__
и None
-инициализации, но никогда не приводят к удовлетворительному решению:
from some_utils import function_1, function_2, function_3
# A: duplicate code in update_member_variables
class Class:
def __init__(self, parameter):
self._variable_1 = function_1(parameter)
self._variable_2 = function_2(parameter)
self._variable_3 = function_3(parameter)
def update_member_variables(self, parameter):
self._variable_1 = function_1(parameter)
self._variable_2 = function_2(parameter)
self._variable_3 = function_3(parameter)
# B: instance variables declared outside __init__
class Class:
def __init__(self, parameter):
self.update_member_variables(parameter)
def update_member_variables(self, parameter):
self._variable_1 = function_1(parameter)
self._variable_2 = function_2(parameter)
self._variable_3 = function_3(parameter)
# C: boilerplate None-assignments
class Class:
def __init__(self, parameter):
self._variable_1 = None
self._variable_2 = None
self._variable_3 = None
self.update_member_variables(parameter)
def update_member_variables(self, parameter):
self._variable_1 = function_1(parameter)
self._variable_2 = function_2(parameter)
self._variable_3 = function_3(parameter)
# D: back to duplicated code in update_member_variables
class Class:
def __init__(self, parameter):
(
self._variable_1,
self._variable_2,
self._variable_3
) = self._derive_values(parameter)
def _derive_values(self, parameter):
return (
function_1(parameter),
function_2(parameter),
function_3(parameter),
)
def update_member_variables(self, parameter):
(
self._variable_1,
self._variable_2,
self._variable_3
) = self._derive_values(parameter)
Заманчиво выбрать B, но из-за всех предупреждений против объявлений переменных-членов вне __init__
, Я обычно придерживаюсь C или D, хотя они кажутся раздутыми и громоздкими.
Нет ли лучшего способа решить эту ситуацию? Может быть, нестандартно? Или самый «элегантный» или чистый из уже существующих в нашей эры?
Похожие вопросы:
В { ссылка } на аналогичный вопрос был дан ответ, но update_number
есть подходит только для инициализации. Код принятого ответа похож на мой D, но без update_member_variables
.
В { ссылка } был дан ответ на другой связанный вопрос. В целом, Симеон Виссер заявляет, что ответственность за обеспечение целостности объекта после инициализации лежит на разработчике, но также не обязательно придерживаться этого правила, несмотря ни на что. Является ли мой случай таким, в котором можно выбрать B? Инстанцированные объекты B будут согласованы по крайней мере.