Подтвердить что ты не робот

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

Я не думаю, что любой из способов имел бы большое значение (может быть, небольшая разница в использовании памяти). Но мне было интересно, почему по какой-то причине одна из них предпочтительнее других.

4b9b3361

Ответ 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