Конструктор класса – метод __init__()

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

В Python наличие пар знаков подчеркивания спереди и сзади в имени метода говорит о том, что он принадлежит к группе методов перегрузки операторов. Если подобные методы определены в классе, то объекты могут участвовать в таких операциях как сложение, вычитание, вызываться как функции и др.

При этом методы перегрузки операторов не надо вызывать по имени. Вызовом для них является сам факт участия объекта в определенной операции. В случае конструктора класса – это операция создания объекта. Так как объект создается в момент вызова класса по имени, то в этот момент вызывается метод __init__(), если он определен в классе.

Необходимость конструкторов связана с тем, что нередко объекты должны иметь собственные свойства сразу. Пусть имеется класс Person, объекты которого обязательно должны иметь имя и фамилию. Если класс будет описан подобным образом

class Person:
    def setName(self, n, s):
        self.name = n
        self.surname = s

то создание объекта возможно без полей. Для установки имени и фамилии метод setName() нужно вызывать отдельно:

>>> from test import Person 
>>> p1 = Person()
>>> p1.setName("Bill", "Ross")
>>> p1.name, p1.surname
('Bill', 'Ross')

В свою очередь, конструктор класса не позволит создать объект без обязательных полей:

class Person:
    def __init__(self, n, s):
        self.name = n
        self.surname = s
 
p1 = Person("Sam", "Baker")
print(p1.name, p1.surname)

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

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

>>> p1 = Person()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 2 required positional arguments: 'n' and 's'

Однако бывает, что надо допустить создание объекта, даже если никакие данные в конструктор не передаются. В таком случае параметрам конструктора класса задаются значения по умолчанию:

class Rectangle:
    def __init__(self, w = 0.5, h = 1):
        self.width = w
        self.height = h
    def square(self):
        return self.width * self.height
 
rec1 = Rectangle(5, 2)
rec2 = Rectangle()
rec3 = Rectangle(3)
rec4 = Rectangle(h = 4)
print(rec1.square())
print(rec2.square())
print(rec3.square())
print(rec4.square())

Вывод:

10
0.5
3
2.0

Если класс вызывается без значений в скобках, то для параметров будут использованы их значения по умолчанию. Однако поля width и height будут у всех объектов.

 

 

Кроме того, конструктору вовсе не обязательно принимать какие-либо параметры, не считая self. Значения полям могут назначаться как угодно. Также не обязательно, чтобы в конструкторе происходила установка атрибутов объекта. Там может быть, например, код, который порождает создание объектов других классов.

Практическая работа. Конструктор и деструктор

Помимо конструктора объектов в языках программирования есть обратный ему метод – деструктор. Он вызывается, когда объект не создается, а уничтожается.

В языке программирования Python объект уничтожается, когда исчезают все связанные с ним переменные или им присваивается другое значение, в результате чего связь со старым объектом теряется. Удалить переменную можно с помощью команды языка del.

В классах Python функцию деструктора выполняет метод __del__().

Напишите программу по следующему описанию:

  1. Есть класс Person, конструктор которого принимает три параметра (не учитывая self) – имя, фамилию и квалификацию специалиста. Квалификация имеет значение заданное по умолчанию, равное единице.

  2. У класса Person есть метод, который возвращает строку, включающую в себя всю информацию о сотруднике.

  3. Класс Person содержит деструктор, который выводит на экран фразу "До свидания, мистер …" (вместо троеточия должны выводиться имя и фамилия объекта).

  4. В основной ветке программы создайте три объекта класса Person. Посмотрите информацию о сотрудниках и увольте самое слабое звено.

  5. В конце программы добавьте функцию input(), чтобы скрипт не завершился сам, пока не будет нажат Enter. Иначе вы сразу увидите как удаляются все объекты при завершении работы программы.

В Python деструктор используется редко, так как интерпретатор и без него хорошо убирает "мусор".

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Комментарии

Ответ на от plustilino

Добрый день! Такое впечатление что у меня конструктор почему то не работает.
>>> class Rectangle:
	width = 0
	height = 0
	def square(self):
		return self.width*self.height
 
 
>>> rect = Rectangle()
>>> print(rect.width)
0
Автоматом перестали делаться отступы в блоках
>>> class Rectangle:
	  def __init__(self, width, height):
	  self.width = width
 
SyntaxError: expected an indented block
>>>
Получается ошибка,делаю рестарт shell, все равно нет отступов автоматом и ошибка та же. Закрываю Python 3.7.0 Shell отступы работают, но конструктор нет
>>> class Rectangle:
	def __init__(self, width, height):
		self.width = width
		self.height = height
		def square(self):
			return self.width*self.height
 
 
>>> r = Rectangle(100, 50)
>>> print(r.height)
50
>>> print(r.square())
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    print(r.square())
AttributeError: 'Rectangle' object has no attribute 'square'
>>> 
Метод square не виден в подсказке когда пишу print(r.....) и появляется эта ошибка
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    print(r.square())
AttributeError: 'Rectangle' object has no attribute 'square'
Подскажите может shell как то криво встал и как проверить есть эта библиотека модуль не знаю что в оболочке как проверить?

Ответ на от Олег

Шел и не будет делать отступы автоматом. 

Не надо класть метод в конструктор. Делают так:

>>> class Rectangle:
...     def __init__(self, width, height):
...             self.width = width
...             self.height = height
...     def square(self):
...             return self.width * self.height
... 
>>> r = Rectangle(100, 50)
>>> r.square()
5000

Или так:

>>> class Rectangle:
...     def __init__(self, width, height):
...             self.width = width
...             self.height = height
...             self.square = self.square()
...     def square(self):
...             return self.width * self.height
... 
>>> r = Rectangle(100, 50)
>>> r.square
5000

Ответ на от plustilino

>>> class Rectangle:
	def __init__(self, w, h):
		self.w = w
		self.h = h
		def square(self):
			return self.w * self.h
 
 
>>> r = Rectangle(100, 50)
>>> r.square()
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    r.square()
AttributeError: 'Rectangle' object has no attribute 'square'
>>> 
>>> class Rectangle:
	w = 100
	h = 50
	def square(self):
		return self.w * self.h
 
 
>>> r = Rectangle()
>>> r.square()
5000
>>> 

Ответ на от Олег

Разница в том, что у вас def square() на разных уровнях вложенности. В первом случае внутри __init__(), что неправильно. В теле конструктора размещают поля (переменные) и вызовы методов, но не определения методов.

Ответ на от plustilino

Большое спасибо!
Но если честно я разочарован питоном.
Такая мелочь как отступ лишний так сильно на все влияет.
Вложенность не пробелами или новой строкой должна определяться а символами понятными, а не как здесь __ поди догадайся что это 2 _
В php, html да где угодно хоть все в строчку пиши, и блочность чисто условная для удобства восприятия, а тут прямо догма:-(((((
Уже не помню как в Visual Basic было, но вроде там тоже на синтакисис не особо заморачивались, может я не прав конечно и это же ооп! Там всегда так:-((((
Вот беда то

Ответ на от Олег

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

class Person:
 
    def __init__(self, name, surname, position=1):
        self.name = name
        self.surname = surname
        self.position = position
 
    def display(self):
        return self.name, self.surname, self.position
 
    def __del__(self):
        print "Goodbye Mr. {} {}".format(self.name, self.surname)
 
p1 = Person('big', 'dude', 3)
print p1.display()
 
p2 = Person('small', 'croon', 4)
print p2.display()
 
p3 = Person('neutral', 'guy', 5)
print p3.display()
 
del p2
 
try:
    input("Press Enter and exit")
except SyntaxError:
    exit()

Спасибо, после вашей статьи понял тему про __init__ На остальных ресурсах менее понятно. Сенкс
print ("Спасибо за пост")

class Person:
    def __init__(self, name, lastname, qual=1):
        self.name = name
        self.lastname = lastname
        self.qual = qual
 
    def __repr__(self):
        return "" % (self.name, self.lastname, self.qual)
 
    def info(self):
        print(f"Сотрудник {self.name} {self.lastname} имеет квалификацию {self.qual}")
 
    def __del__(self):
        print(f"До свидания, мистер {self.name} {self.lastname}")
 
def byQual_key(person):
    return person.qual
 
persons = []
persons.append(Person("Иван", "Иванов", 2))
persons.append(Person("Петр", "Петров"))
persons.append(Person("Сидр", "Сидоров", 3))
 
p = sorted(persons, key = byQual_key, reverse=True)
 
for i in p:
    i.info()
 
p[-1].__del__()
 
print("Press ENTER to exit")
input()

Ответ на от Алексей

Для чего использовали i.info()

Ответ на от Нурлыбай

Фраза про увольнение появляется, но физически из списка он не удаляется. При проверке списка после увольнения, он отражается в сотрудниках.

В задании сказано, что квалификация задана в классе Person и равна 1. А потом говорится уволить самое слабое звено. Но они же все имеют квалификацию = 1. ? Зарание спасибо.

class Person():
    def __init__(self, n, sn, sk=1 ):
        self.name=n
        self.surname=sn
        self.skill=sk              
    def presentation(self):
        print("Меня зовут ",self.surname, self.name,", моя квалификация",self.skill)
    def dismissal (self):
        print("До свидания, мистер", self.surname, "!")
        del self.name
        del self.surname
        del self.skill
 
employees=[] #список сотрудников
skills=[] #список квалификаций
empl1=Person("Сергей", "Иванов", 7)
empl2=Person("Андрей", "Петров")
empl3=Person("Бармалей", "Сидоровов", 2)
employees.append(empl1)
employees.append(empl2)
employees.append(empl3)
for empl in employees:
    empl.presentation() 
    skills.append(empl.skill)
employees[skills.index(min(skills))].dismissal()#увольнение сотрудника
employees.remove(employees[skills.index(min(skills))])#удаление из списка сотрудников
skills.remove(skills[skills.index(min(skills))])# удаление сотрудника из списка квалификаций
 
input("Нажмите Enter")

Подскажите советом, как правильней организовать список персонажей, что бы не ручками и глазками высматривать "самое слабое звено", а найти программно и его удалить? Решение задачи:
class person:
    def __init__(self, fName, lName, qual=1, gender='M'):
        self.fName=fName
        self.lName=lName
        self.qual=qual
        self.gender='Male' if gender=='M' else 'Female' if gender=='F' else 'WHO ARE YOU?'
 
    def __del__(self):
        return f'Goodbye {str(self)}'
 
    def __repr__(self):
        return f'{self.lName[0].upper()}{self.lName[1:]} {self.fName[0].upper()}'
 
    def getInfo(self):
        return f'Person: [{str(self)}]\n - Qualification: [{self.qual}]\n - Gender: [{self.gender}]'
 
anton=person('anton','ukupnik')
vasya=person('vasya','pupkin',qual=3)
anna=person('anna','ivanova',gender='F', qual=4)
 
print(anton.getInfo())
print(anna.getInfo())
print(vasya.getInfo())
 
print(anton.__del__())
 
input('press enter to exit')

Ответ на от plustilino

Класс дополнил методами: ...
    def __lt__(self, other):
        return self.qual < other.qual
 
    def __gt__(self, other):
        return self.qual > other.qual
...
pList=[person('anton','ukupnik'),
    person('igor','nikolaev'), 
    person('vasya','pupkin',qual=3),
    person('anna','ivanova',gender='F', qual=4)]
 
 
print('remove outsiders:')
for pers in list(filter(lambda x: x.qual==min(pList).qual, pList)):
    pList.remove(pers)
    print(pers.__del__())

Метод __del__() выполняется перед уничтожением объекта, если вызвать его на объекте, то он просто выполнит свой блок.

#!/bin/env python36
class Person:
    def __init__(self, n, s, q = 1):
        self.name = n
        self.surname = s
        self.qualification = int(q)
    def getInfo(self):
       return  self.name, self.surname, self.qualification
    def __del__(self):
        print('Goodbye Mr.', self.name, self.surname)
 
one = Person('Ivan', 'Drago', 2)
two = Person('John', 'Smith', 12)
three = Person('Natalia', 'Lazareva', 3)
four = Person('Simon', 'Bull', 5)
print(one.getInfo())
print(two.getInfo())
print(three.getInfo())
print(four.getInfo())
skill_list = []
for skill in one.qualification, two.qualification, three.qualification, four.qualification:
    skill_list.append(skill)
weak = min(skill_list)
for per in one, two, three, four:
    if per.qualification == weak:
        per.__del__()
input()

А почему нельзя сделать нужные поля в Class A: , вместо __init__ ? Или при создании объекта учитывается только метод __init__, а указанные поля в самом классе не идут в использование(при создании объекта)?

class Person():
 
    def __init__(self, name, surname, kvala = 1):
        self.name = name
        self.surname = surname
        self.kvala = kvala
 
    def vozv(self):
        return self.name, self.surname, self.kvala
 
    def __del__(self):
        print ('Goodbye', self.name, self.surname)
 
Alba = Person("Alba", "Bakker", "13")
Samanta = Person("Samanta", "Bekker", "14")
Lila = Person("Lila", "Arhib", "33")
 
print (Alba.vozv(), Samanta.vozv(), Lila.vozv())
 
a = input()
#Вот что получилось
Есть несколько вопросов: 1) Можно ли как-то вывести все созданные объекты по данному классу, не чередуя их через запятую в print ? 2) Почему у меня после моего Input'а выводится "Goodbye name, surname" по всем трем объектам ?хотя даже команды нету 3) Не понял как использовать деструктор для удаления определенного объекта? Пример: Alba.__del__() - так ?

Ответ на от And

2) потому что при завершении программы удаляются все объекты автоматически, input() тут не причем.

3) можно удалять del Alba.

1) только если вы поместите объекты в список. Хотя возможно есть какой-то специфический вариант нахождения в программе всех объектов одного класса. Мне неизвестно.