Class

> *Эта статья про классы! Если вас больше интересуют библиотеки, то перейдите в статью.*
>
>

**Классы** - Одна из нужных вещей не только в Python, но и во всём программировании! Благодаря им можно значительно облегчить код, а также они используются почти во всех библиотеках! Но перед тем как перейти к самим классам, надо разобраться из чего состоит ООП.

## Из чего состоит ООП?

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

* **Класс** - Чертёж робота.
* **Объект** - Это и есть робот которого создали по чертежу.
* **Наследование** - Добавление новых опций в чертёж робота. Можно сказать что наследование - это подклассы.
* **Полиморфизм** - Изменение функционала в классах-наследниках. Предположим, что в чертеже *(классе)* было прописано что все роботы могут ходить и видеть. В классе-наследнике можно будет указать что там роботы уже имеют возможность прыгать и иметь возможность к ночному зрению.
* **Инкапсуляция** - Броня робота. Робот конечно сможет жить без брони, но провода можно и поломать без проблем.

Также стоит упомянуть про то что переменные в классах называются полями, а функции - модулями.

## Создание

### Класс

Создадим простой класс:

```
# Класс
class Building:
    city = None
    year = None

    def data(self, city, year):
        self.city = city
        self.year = year

    def getdata(self):
        print('Строение было создано в', self.year, 'году и было построено в городе', self.city)

# Объект
shop = Building()
shop.data('Санкт-Петербург', 1933)
shop.getdata()

```
В нём мы получаем информацию про год постройки здания и город где он был построен. Рассмотрим также **\_\_init\_\_**.

```
class Building:
    city = None
    year = None

    def \_\_init\_\_(self, city, year):
        self.data(city, year)
        self.getdata()

    def data(self, city, year):
        self.city = city
        self.year = year

    def getdata(self):
        print('Строение было создано в', self.year, 'году и было построено в городе', self.city)

```
Благодаря этому можно из трёх строки *(создание переменной, заполнение данных, вывод)* сделать одну **Building(значение1, значение2)**.

#### Зачем нужен self?

self нужен для того чтобы обращаться к полям. Если же его не написать, то код начнёт искать переменную вне функции. Это можно проверить с помощью редактирования такого класса:

```
number = 4
class PrintNumber:
    number = 5
    def \_\_init\_\_(self):
        print(self.number)  # Вывод: 5
        print(number)  # Вывод: 4

```

#### Как сделать так чтобы заполнялись лишь определённые значения?

Добавляем к значениям изначальное значение и отныне не будет вылезать ошибка при недостаче значений!

```
def \_\_init\_\_(self, city=None, year=None):
    self.data(city, year)
    self.getdata()

```

```
Building('Москва')

### Наследование

Если вам хватило примера из **Из чего состоит ООП?**, то можете пролистать до черты.

Наследовательские классы или же подклассы могут быть очень полезны. Пример: Строятся два здания: Магазин и Школа. Есть два бланка, где в обоих написаны одинаковые пункты:

* Город
* Сколько денег для построения
* Начало работы
* Предполагаемая дата завершения
* Цель

И в Школе надо будет ещё указать количество кабинетов, сколько там будет учителей, школьные правила, программа обучения и т.д... И для этого надо будет создать отдельный бланк который будет иметь свойства первоначального.

Записываются наслед. классы как **Название подкласса(Основной класс)**. Пример:

* Обычный
* "Главный" класс компактнее

```
class Building:
    city = None
    money = None
    start = None
    end = None

    def \_\_init\_\_(self, city='из России', money='некоторое количество', start='какое-то время', end='какое-то время'):
        self.data(city,money,start,end)
        self.getdata()

    def data(self, city, money, start, end):
        self.city = city
        self.money = money
        self.start = start
        self.end = end

    def getdata(self):
        print('Здание строится в городе', self.city, 'и на него было выделено', self.money, 'денег.')
        print('Началась стройка в', self.start, 'и планируется закончиться в', self.end)

Building()
print()

class School(Building):
    rooms = None
    teachers = None

    def \_\_init\_\_(self, city, money, start, end, rooms, teachers):
        super(School, self).\_\_init\_\_(city, money, start, end) # super достаёт данные полей из Building и засовывает их в модуль
        self.rooms = rooms
        self.teachers = teachers

    def data\_school(self):
        print(f'Город: {self.city}', f'Деньги: {self.money}', f'Начало работы: {self.start}', f'Предпологаемый конец: {self.end}', f'Комнаты: {self.rooms}', f'Учителя: {self.teachers}', sep='\n')

a = School('Москва', 500000, '12.04.1994', '4.02.1995', '40', '10')
print()
a.data\_school()

# Учителя: 10

```

```
class Building:
    city = None;money = None;start = None;end = None
    def \_\_init\_\_(self, city='из России', money='некоторое количество', start='какое-то время', end='какое-то время'):
        self.data(city,money,start,end); self.getdata()
    def data(self, city, money, start, end): self.city = city;self.money = money;self.start = start;self.end = end
    def getdata(self):
        print('Здание строится в городе', self.city, 'и на него было выделено', self.money, 'денег.');print('Началась стройка в', self.start, 'и планируется закончиться в', self.end)

Building()
print()

class School(Building):
    rooms = None
    teachers = None

    def \_\_init\_\_(self, city, money, start, end, rooms, teachers):
        super(School, self).\_\_init\_\_(city, money, start, end) # super достаёт данные полей из Building и засовывает их в модуль
        self.rooms = rooms
        self.teachers = teachers

    def data\_school(self):
        print(f'Город: {self.city}', f'Деньги: {self.money}', f'Начало работы: {self.start}', f'Предпологаемый конец: {self.end}', f'Комнаты: {self.rooms}', f'Учителя: {self.teachers}', sep='\n')

a = School('Москва', 500000, '12.04.1994', '4.02.1995', '40', '10')
print()
a.data\_school()

# Комнаты: 40

### Полиморфизм

В наследовании добавляются новые опции, но что делать со старыми? С помощью Полиморфизма можно заменить значения из главного класса в наследовательном классе. Возьмём пример с getdata. Для того чтобы обновить, надо из основного класса убрать "self.getdata()" чтобы корректно работало в наследовательском классе.

```
def \_\_init\_\_(self, city='из России', money='некоторое количество', start='какое-то время', end='какое-то время'):
    self.data(city,money,start,end);

```

```
def \_\_init\_\_(self, city, money, start, end, rooms, teachers):
    super(School, self).\_\_init\_\_(city, money, start, end) # super достаёт данные полей из Building и засовывает их в модуль
    self.rooms = rooms
    self.teachers = teachers
    self.getdata() # Не забудьте здесь добавить эту строку!

def getdata(self):
    super().getdata()
    print('В школе будет', self.rooms, 'комнат а также', self.teachers, 'учителей.')

School('Москва', 500000, '12.04.1994', '4.02.1995', '40', '10')

# Началась стройка в 12.04.1994 и планируется закончиться в 4.02.1995

### Инкапсуляция

В Python есть 3 вида защиты:

* Публичная: a
* Защищённая: \_a
* Приватная: \_\_a

Однако к полям всё равно можно будет обращаться и с этим ничего не поделаешь.