Конструктор класса — метод __init__. Урок 3

Большинство классов имеют специальный метод, который автоматически при создании объекта создает ему атрибуты. Т.е. вызывать данный метод не нужно, т.к. он сам запускается при вызове класса. (Вызов класса происходит, когда создается объект.) Такой метод называется конструктором класса и в языке программирования Python носит имя __init__. (В начале и конце по два знака подчеркивания.)

Первым параметром, как и у любого другого метода, у __init__ является self, на место которого подставляется объект в момент его создания. Второй и последующие (если есть) параметры заменяются аргументами, переданными в конструктор при вызове класса.

Рассмотрим два класса: в одном будет использоваться конструктор, а в другом нет. Требуется создать два атрибута объекта.

class YesInit:
     def __init__(self,one,two):
          self.fname = one
          self.sname = two
 
obj1 = YesInit("Peter","Ok")
 
print (obj1.fname, obj1.sname)
class NoInit:
     def names(self,one,two):
          self.fname = one
          self.sname = two
 
obj1 = NoInit()
obj1.names("Peter","Ok")
 
print (obj1.fname, obj1.sname) 

Вывод интерпретатора в обоих случаях:

Peter Ok

В обоих программах у объекта появляются два атрибута: fname и sname. Однако в первом случае они инициализируются при создании объекта и должны передаваться в скобках при вызове класса. Если какие-то атрибуты должны присутствовать у объектов класса обязательно, то использование метода __init__ - идеальный вариант. Во второй программе (без использования конструктора) атрибуты создаются путем вызова метода names после создания объекта. В данном случае вызов метода names необязателен, поэтому объекты могут существовать без атрибутов fname и sname.

Обычно метод __init__ предполагает передачу аргументов при создании объектов, однако аргумент может не быть передан. Например, если в примере выше создать объект так: obj1 = YesInit(), т.е. не передать классу аргументы, то произойдет ошибка. Чтобы избежать подобных ситуаций, можно в методе __init__ присваивать параметрам значения по умолчанию. Если при вызове класса были заданы аргументы для данных параметров, то хорошо — они и будут использоваться, если нет — еще лучше — в теле метода будут использованы значения по умолчанию. Пример:

class YesInit:
     def __init__(self,one="noname",two="nonametoo"):
          self.fname = one
          self.sname = two
 
obj1 = YesInit("Sasha","Tu")
obj2 = YesInit()
obj3 = YesInit("Spartak")
obj4 = YesInit(two="Harry")
 
print (obj1.fname, obj1.sname)
print (obj2.fname, obj2.sname)
print (obj3.fname, obj3.sname)
print (obj4.fname, obj4.sname)

Вывод интерпретатора:

Sasha Tu
noname nonametoo
Spartak nonametoo
noname Harry

В данном случае, второй объект создается без передачи аргументов, поэтому в методе __init__используются значения по умолчанию ("noname" и "nonametoo"). При создании третьего и четвертого объектов передаются по одному аргументу. Если указывается значение не первого аргумента, то следует явно указать имя параметра (четвертый объект).

Метод __init__ может содержать параметры как без значений по умолчанию, так и со значениями по умолчанию. В таком случае, параметры, аргументы которых должны быть обязательно указаны при создании объектов, указываются первыми, а параметры со значениями по умолчанию — после. Например, ниже вторая программа с ошибкой:

class fruits:
     def __init__(self,w,n=0):
          self.what = w
          self.numbers = n
 
f1 = fruits("apple",150)
f2 = fruits("pineapple")
 
print (f1.what,f1.numbers)
print (f2.what,f2.numbers)
class fruits:
     def __init__(self,n=0,w): #ERROR
          self.what = w
          self.numbers = n
 
f1 = fruits(150,"apple")
f2 = fruits("pineapple")
 
print (f1.what,f1.numbers)
print (f2.what,f2.numbers)

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

class Building:
     def __init__(self,w,c,n=0):
          self.what = w
          self.color = c
          self.numbers = n
          self.mwhere(n)
 
     def mwhere(self,n):
          if n <= 0:
               self.where = "отсутствуют"
          elif 0 < n < 100:
               self.where = "малый склад"
          else:
               self.where = "основной склад"
 
     def plus(self,p):
          self.numbers = self.numbers + p
          self.mwhere(self.numbers)
     def minus(self,m):
          self.numbers = self.numbers - m
          self.mwhere(self.numbers)
 
m1 = Building("доски", "белые",50)
m2 = Building("доски", "коричневые", 300)
m3 = Building("кирпичи","белые")
 
print (m1.what,m1.color,m1.where)
print (m2.what,m2.color,m2.where)
print (m3.what,m3.color,m3.where)
 
m1.plus(500)
print (m1.numbers, m1.where)

В данном примере значение атрибута where объекта зависит от значения атрибута numbers.

Практическая работа:

  1. Спишите представленные выше скрипт с классом Building. Запустите программу, объясните как она работает. В какой момент создается атрибут where объектов? Зачем потребовалось конструкцию if-elif-else вынести в отдельную функцию, а не оставить ее в методе __init__?
  2. Самостоятельно придумайте класс, содержащий конструктор. Создайте на его основе несколько объектов.

Создано