Python - должны быть инициализированы все переменные-члены в __init__
Возможно, это скорее вопрос стиля, чем технический, но у меня есть класс python с несколькими переменными-членами, и я хочу, чтобы он работал так, что некоторые из переменных-членов инициализируются, когда пользователь сначала создает экземпляр class (т.е. в функции __init__
), и я хочу, чтобы другие переменные-члены были определены из аргументов функций-членов, которые будут вызываться позже. Поэтому мой вопрос заключается в том, должен ли я инициализировать все переменные-члены в функции __init__
(и установить те, которые будут определены позже, на фиктивные значения) или инициализировать некоторые из функций __init__
, а некоторые - в более поздних функциях. Я понимаю, что это может быть трудно понять, поэтому вот несколько примеров.
В этом примере var3
сначала устанавливается на 0 в функции __init__
, а затем позже в функции my_funct задано значение.
class myClass(object):
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
self.var3=0
def my_funct(self,var3):
self.var3=var3
и в этом примере var3
вообще не определяется функцией __init__
class myClass(object):
def __init__(self,var1,var2):
self.var1=var1
self.var2=var2
def my_funct(self,var3):
self.var3=var3
Я не думаю, что любой из способов имел бы большое значение (может быть, небольшая разница в использовании памяти). Но мне было интересно, почему по какой-то причине одна из них предпочтительнее других.
Ответы
Ответ 1
В объектно-ориентированном программировании это зависит от разработчика, чтобы гарантировать, что объект всегда находится в согласованном состоянии после создания экземпляра и после завершения метода. Помимо этого, вы можете свободно развивать класс по своему усмотрению (имея в виду определенные принципы с подклассом/переопределением и т.д.).
Инструмент, такой как Pylint, будет предупреждать, когда вы устанавливаете переменные экземпляра вне __init__
. Можно утверждать, что установка всех переменных экземпляра в __init__
более чистая, но это не правило, которое должно выполняться в любое время.
Ответ 2
Я бы фактически препятствовал инициализации переменных, которые вам не всегда нужны в __init__
, для произвольного значения по умолчанию.
Я делаю вопрос о вашем использовании OO, если это так, но я уверен, что существует допустимый и понятный случай, когда __init__
не будет делать все, и класс захочет дополнительно модифицировать себя, добавив дополнительные атрибуты с другими методами.
Правильный способ, по моему мнению, проверить, была ли указана переменная при запуске метода, который может захотеть использовать, будет использовать hasattr
. Это в том случае, если это допустимый способ использования метода, и тест просто переключает поведение разумным способом.
Другой способ - попытаться использовать его и обработать исключение и предоставить некоторую удобную для пользователя информацию о том, что делает пользователь вашего класса неправильным. Это в том случае, когда метод нуждается в атрибуте, который должен быть установлен перед запуском.
то есть. Привет, человек, вы инициализировали класс, но вам нужно убедиться, что атрибут z
существует, вызывая метод z_init
перед запуском метода z_run
.
Другим, возможно, более путинским способом было бы просто документировать, как использовать метод в docstring, а затем позволить исключению летать, когда он используется неправильно. Это достаточно хорошо для первой реализации чего-то, и вы можете сосредоточиться на следующей задаче. Это в той же ситуации, что и выше, для этого метода должен быть установлен атрибут.
Причина, по которой мне не нравится идея инициализации переменных для произвольных значений по умолчанию, может быть путаницей (потому что она произвольная) и является линейным шумом.
Если значение не произвольно и просто значение по умолчанию, которое можно изменить, вы должны использовать значение по умолчанию в методе __init__
, который можно переопределить. Он также может быть действительным начальным состоянием, которое также не произвольно, и вы должны установить его в методе __init__
.
Итак, реальный ответ зависит от этого, и вам, вероятно, следует избегать его и подвергать сомнению использование OO, если вы это делаете, добавляя атрибуты в другие методы или инициализируя атрибуты произвольными значениями.
В то время как Симеон Виссер говорит, чтобы сохранить ваш объект в постоянном состоянии, у него нет оснований для того, какая последовательность основана на вашем абстрактном примере. В то время как Pylint предупреждает об этом, предупреждения из программ lint просто так, что рецензент высокого уровня может быть предупрежден о вещах, которые обычно указывают на запах кода. Я говорю рецензент высокого уровня, потому что реальный рецензент должен читать и понимать весь ваш код и, следовательно, не нужен Pylint.
Пример, который нарушает правило:
class Mutant(object):
"""A mutant!"""
def __init__(self):
"""A mutant is born with only 1 eye and 1 mouth"""
self.eyes = 1
self.mouth = 1
self.location = 'Montana'
def roll_to(self, location):
"""If they have limbs, running is less dangerous"""
if hasattr(self, 'limbs'):
print 'Your mutant broke its limbs off!!'
del self.limbs
self.location = location
def run_to(self, location):
"""If they don't have limbs, running is not effective"""
if not hasattr(self, 'limbs'):
print 'Your mutant tries to run but he has no limbs.'
else:
self.location = location
def grow_limbs(self, number_of_limbs):
"""Ah, evolution!"""
assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...'
if hasattr(self, 'limbs'):
self.limbs += number_of_limbs
else:
self.limbs = number_of_limbs