Объектно-ориентированное программирование

Классы и объекты

Последнее обновление: 03.02.2024

Python имеет множество встроенных типов, например, int, str и так далее, которые мы можем использовать в программе. Но также Python позволяет определять собственные типы с помощью классов. Класс представляет некоторую сущность. Конкретным воплощением класса является объект.

Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке, у которого есть имя, возраст, какие-то другие характеристики. Человек может выполнять некоторые действия - ходить, бегать, думать и т.д. То есть это представление, которое включает набор характеристик и действий, можно назвать классом. Конкретное воплощение этого шаблона может отличаться, например, одни люди имеют одно имя, другие - другое имя. И реально существующий человек будет представлять объект этого класса.

В языке Python класс определяется с помощью ключевого слова class:

class название_класса:
    атрибуты_класса
    методы_класса

Внутри класса определяются его атрибуты, которые хранят различные характеристики класса, и методы - функции класса.

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

class Person:
    pass

В данном случае определен класс Person, который условно представляет человека. В данном случае в классе не определяется никаких методов или атрибутов. Однако поскольку в нем должно быть что-то определено, то в качестве заменителя функционала класса применяется оператор pass. Этот оператор применяется, когда синтаксически необходимо определить некоторый код, однако исходя из задачи код нам не нужен, и вместо конкретного кода вставляем оператор pass.

После создания класса можно определить объекты этого класса. Например:

class Person:
    pass

tom = Person()      # определение объекта tom
bob = Person()      # определение объекта bob

После определения класса Person создаются два объекта класса Person - tom и bob. Для создания объекта применяется специальная функция - конструктор, которая называется по имени класса и которая возвращает объект класса. То есть в данном случае вызов Person() представляет вызов конструктора. Каждый класс по умолчанию имеет конструктор без параметров:

tom = Person()      # Person() - вызов конструктора, который возвращает объект класса Person

Конструкторы

Итак, для создания объекта класса используется конструктор. Так, выше когда мы создавали объекты класса Person, мы использовали конструктор по умолчанию, который не принимает параметров и который неявно имеют все классы. Однако мы можем явным образом определить в классах конструктор с помощью специального метода, который называется __init__() (по два прочерка с каждой стороны). К примеру, изменим класс Person, добавив в него конструктор:

class Person:
    # конструктор
    def __init__(self):
        print("Создание объекта Person")

tom = Person()      # Создание объекта Person

Итак, здесь в коде класса Person определен конструктор - функция __init__. Конструктор должен принимать как минимум один параметр ссылку на текущий объект - self. Обычно конструкторы применяются для определения действий, которые должны производиться при создании объекта.

Теперь при создании объекта:

tom = Person()

выполняется вызов конструктора __init__() из класса Person, который выведет на консоль строку "Создание объекта Person".

Стоит отметить, что конструктор фактически представляет обычную функцию, только для вызовы конструктора используется не __init__, а название класса. Кроме того, при вызове конструктора параметру self явным образом не передается никакого значения. При выполнении программы Python динамически будет определять self.

Атрибуты объекта

Атрибуты хранят состояние объекта. Для определения и установки атрибутов внутри класса можно применять слово self. Например, определим следующий класс Person:

class Person:

    def __init__(self, name, age):
        self.name = name    # имя человека
        self.age = age        # возраст человека


tom = Person("Tom", 22)

# обращение к атрибутам
# получение значений
print(tom.name)     # Tom
print(tom.age)      # 22
# изменение значения
tom.age = 37
print(tom.age)      # 37

Теперь конструктор класса Person принимает еще два параметра - name и age. Через эти параметры в конструктор будут передаваться имя и возраст создаваемого человека.

Внутри конструктора устанавливаются два атрибута - name и age (условно имя и возраст человека):

def __init__(self, name, age):
    self.name = name
    self.age = age

Атрибуту self.name присваивается значение переменной name. Атрибут age получает значение параметра age. Название атрибутов не обязательно должно соответствовать названиям параметров.

Если мы определили в классе конструктор __init__ с параметрами (кроме self), то при вызове конструктора этим параметрам надо передать значения:

tom = Person("Tom", 22)

То есть в данном случае параметру name передается строка "Tom", а параметру age - число 22.

Далее по имени объекта мы можем обращаться к атрибутам объекта - получать и изменять их значения:

print(tom.name)     # получение значения атрибута name
tom.age = 37        # изменение значения атрибута age

Подобным образом мы можем создавать разные объекты класса Person с разным значением для атрибутов:

class Person:

    def __init__(self, name, age):
        self.name = name    # имя человека
        self.age = age        # возраст человека


tom = Person("Tom", 22)
bob = Person("Bob", 43)

print(tom.name)         # Tom
print(bob.name)         # Bob

Здесь создаются два объекта класса Person: tom и bob. Они соответствуют определению класса Person, имеют одинаковый набор атрибутов, однако их состояние будет отличаться. И в каждом случае Python будет динамически определять объект self. Так, в следующем случае

tom = Person("Tom", 22)

Это будет объект tom

А при вызове

bob = Person("Bob", 43)

Это будет объект bob

В принципе нам необязательно определять атрибуты внутри класса - Python позволяет сделать это динамически вне кода:

class Person:

    def __init__(self, name, age):
        self.name = name        # имя человека
        self.age = age          # возраст человека


tom = Person("Tom", 22)

tom.company = "Microsoft"
print(tom.company)  # Microsoft

Здесь динамически устанавливается атрибут company, который хранит место работы человека. И после установки мы также можем получить его значение. В то же время подобное определение чревато ошибками. Например, если мы попытаемся обратиться к атрибуту до его определения, то программа сгенерирует ошибку:

tom = Person("Tom", 22)
print(tom.company)  # ! Ошибка - AttributeError: Person object has no attribute company

Методы классов

Методы класса фактически представляют функции, которые определенны внутри класса и которые определяют его поведение. Например, определим класс Person с одним методом:

class Person:       # определение класса Person
     def say_hello(self):
        print("Hello")

tom = Person()
tom.say_hello()    # Hello

Здесь определен метод say_hello(), который условно выполняет приветствие - выводит строку на консоль. При определении методов любого класса, как и конструктора, первый параметр метода представляет ссылку на текущий объект, который согласно условностям называется self. Через эту ссылку внутри класса мы можем обратиться к функциональности текущего объекта. Но при самом вызове метода этот параметр не учитывается.

Используя имя объекта, мы можем обратиться к его методам. Для обращения к методам применяется нотация точки - после имени объекта ставится точка и после нее идет вызов метода:

объект.метод([параметры метода])

Например, обращение к методу say_hello() для вывода приветствия на консоль:

tom.say_hello()    # Hello

В итоге данная программа выведет на консоль строку "Hello".

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

class Person:       # определение класса Person
    def say(self, message):     # метод 
        print(message)

tom = Person()
tom.say("Hello METANIT.COM")    # Hello METANIT.COM

Здесь определен метод say(). Он принимает два параметра: self и message. И для второго параметра - message при вызове метода необходимо передать значение.

Для обращения к атрибутам и методам объекта внутри класса в его методах также применяется слово self:

self.атрибут    # обращение к атрибуту
self.метод      # обращение к методу

Например, следующий класс Person:

class Person:

    def __init__(self, name, age):
        self.name = name        # имя человека
        self.age = age          # возраст человека
    
    def display_info(self):
        print(f"Name: {self.name}  Age: {self.age}")


tom = Person("Tom", 22)
tom.display_info()      # Name: Tom  Age: 22

bob = Person("Bob", 43)
bob.display_info()      # Name: Bob  Age: 43

Здесь определяется метод display_info(), который выводит информацию на консоль. И для обращения в методе к атрибутам объекта применяется слово self: self.name и self.age

В итоге мы получим следующий консольный вывод:

Name: Tom  Age: 22
Name: Bob  Age: 43

Деструкторы

Кроме конструкторов классы в Python также могут определять специальные методы - деструкторы, которые вызываются при удалении объекта. Деструктор представляет собой метод __del__(self), в который, как и в конструктор, передается ссылка на текущий объект. В деструкторе определяются действия, которые надо выполнить при удалении объекта, например, освобождение или удаление каких-то ресурсов, которые использовал объект.

Деструктор вызывается автоматически интерпретатором, нам не нужно его явным образом вызывать. Простейший пример:

class Person:
 
    def __init__(self, name):
        self.name = name
        print("Создан человек с именем", self.name)
    
    def __del__(self):
        print("Удален человек с именем", self.name)
 
tom = Person("Tom")

Здесь в деструкторе просто выведится уведомление об удалении объекта Person. Программа создает один объект Person и хранит ссылку на него в переменной tom. Создание объекта вызовет выполнение конструктора. При завершении программы автоматически будет выполняться деструктор объекта tom. В итоге консольный вывод программы будет следующим:

Создан человек с именем Tom
Удален человек с именем Tom

Другой пример:

class Person:
 
    def __init__(self, name):
        self.name = name
        print("Создан человек с именем", self.name)
    
    def __del__(self):
        print("Удален человек с именем", self.name)
 

def create_person():
    tom = Person("Tom")
    
create_person()
print("Конец программы")

Здесь объект Person создается и используется внутри функции create_person, поэтому жизнь создаваемого объекта Person ограничена областью этой функции. Соответственно, когда функция завершит свое выполнение, у объекта Person будет вызываться деструктор. В итоге мы получим следующий консольный вывод:

Создан человек с именем Tom
Удален человек с именем Tom
Конец программы
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850