Создание классов и объектов
Мы уже знаем, зачем нужно ООП. Теперь пора написать первый настоящий класс на Python. Разберёмся с синтаксисом, узнаем, что такое «экземпляр», и увидим, как Python хранит объекты в памяти.
🔑 Ключевое слово class
В прошлом уроке мы говорили о классе как о чертеже или форме для печенья. Пора наконец написать этот чертёж на Python.
В Python для создания класса используется ключевое слово class. Это такое же зарезервированное слово, как if, for или def — Python «знает» его заранее и понимает, что дальше будет описание класса.
Базовый синтаксис выглядит вот так:
class ИмяКласса:
# тело класса — здесь будет содержимое
Заметьте структуру: слово class, затем имя, затем двоеточие : — и дальше с отступом идёт тело класса. Точно так же, как у функции def или у блока if. Python всегда использует двоеточие + отступ для обозначения вложенных блоков.
💡 Важно: класс — это просто описание. Сам по себе он ничего не делает и не занимает много памяти. Он лишь говорит Python: «Запомни, вот как будут выглядеть объекты этого типа». Работа начнётся, когда мы создадим объект на основе этого класса.
📝 PascalCase — правило именования классов
В Python есть официальный документ с правилами оформления кода — он называется PEP 8 (Python Enhancement Proposal 8). Это что-то вроде свода правил дорожного движения для Python-программистов. Все профессиональные разработчики его соблюдают.
Согласно PEP 8, имена классов пишутся в стиле PascalCase — это когда каждое слово в названии начинается с заглавной буквы, и слова пишутся слитно, без подчёркиваний.
Правильно — PascalCase
BankAccountUserProfileShoppingCartHttpRequestНеправильно
bank_account — snake_caseuserprofile — всё строчнымSHOPPINGCART — всё заглавнымhttpRequest — camelCaseПочему это важно? Потому что в Python по стилю написания сразу видно, с чем работает программист. Если видишь bank_account — это переменная или функция. Если видишь BankAccount — это точно класс. Код становится читаемым с первого взгляда.
🎯 Запомните: функции и переменные → snake_case (слова через подчёркивание). Классы → PascalCase (каждое слово с заглавной). Это не Python требует технически — это договорённость сообщества, которую все соблюдают.
🪴 Минимальный класс: знакомство с pass
В Python тело класса не может быть пустым. Если написать class Dog: и ничего не добавить — Python выдаст ошибку SyntaxError. Он ожидает хоть что-то внутри.
Для таких случаев в Python есть специальное слово-заглушка — pass. Оно буквально означает «здесь ничего нет, пропусти». Это полноценная инструкция, которая ничего не делает — она нужна именно для того, чтобы занять место, когда тело блока должно быть, но нам пока нечего туда писать.
# Минимальный валидный класс
class Dog:
pass
# Вот и всё! Это полноценный класс Python.
# Пока он пустой, но это абсолютно рабочий код.
pass часто используют на этапе проектирования: сначала описывают структуру (какие классы будут), а потом заполняют их содержимым. Это нормальная практика — сначала наброски, потом детали.
🏗️ Создание объекта — вызов класса как функции
Класс у нас есть. Теперь нужно создать объект на его основе. И здесь Python делает всё очень просто: чтобы создать объект, нужно вызвать класс точно так же, как вы вызываете функцию — написать его имя и поставить скобки.
class Dog:
pass
# Создаём объект — вызываем класс как функцию
my_dog = Dog()
# my_dog теперь — это объект (экземпляр) класса Dog
print(my_dog) # <__main__.Dog object at 0x10a3f2b50>
Что произошло? Dog() — это вызов класса. Python «запустил» чертёж и по нему создал реальный объект в памяти компьютера. Этот объект был сохранён в переменную my_dog.
Когда вы распечатаете my_dog, увидите что-то вроде <__main__.Dog object at 0x10a3f2b50>. Это Python говорит вам: «Это объект класса Dog, и он живёт по такому-то адресу в памяти». Адрес (те числа в конце) — это физическое место в оперативной памяти вашего компьютера.
🔧 Под капотом: когда вы пишете Dog(), Python вызывает специальный метод __init__ этого класса. Сейчас у нашего класса нет своего __init__, поэтому Python использует базовый — он просто создаёт пустой объект. Об __init__ мы поговорим подробно в следующем уроке.
📌 Что такое «экземпляр» (instance)?
В программировании вы будете постоянно встречать два слова: объект и экземпляр. Хорошая новость — это синонимы. Оба слова означают одно и то же: конкретную «вещь», созданную на основе класса.
Класс
Это чертёж, описание, шаблон. Существует в одном экземпляре (хе-хе). Не занимает памяти «под данные».
Объект / Экземпляр
Конкретная «вещь», созданная по чертежу. Живёт в памяти. Таких может быть сколько угодно.
Аналогия: класс Dog — это описание собак как вида (четыре лапы, хвост, лает). Объект my_dog — это конкретная собака Шарик, которая живёт у вас дома.
Термин «экземпляр» (от английского instance) пришёл из объектно-ориентированного программирования. Когда говорят «создать экземпляр класса» — это просто другой способ сказать «создать объект». В документации Python, в Stack Overflow, в книгах — вы будете видеть оба слова вперемежку. Знайте, что это одно и то же.
🔍 Встроенные функции: type(), id(), isinstance()
Python даёт нам три очень полезные встроенные функции для работы с объектами. Давайте разберём каждую.
🏷️ type(obj) — узнать класс объекта
Возвращает класс, к которому принадлежит объект. Это как спросить: «Из какого теста сделано это печенье?» — и форма ответит тебе своё имя.
🔢 id(obj) — уникальный номер объекта в памяти
Возвращает целое число — адрес объекта в оперативной памяти. Это как номер квартиры: у каждого объекта свой уникальный адрес. Два разных объекта никогда не имеют одинаковый id в один момент времени.
✅ isinstance(obj, Class) — проверка принадлежности классу
Возвращает True или False: является ли объект экземпляром указанного класса. Это самый правильный способ проверить тип объекта в Python — предпочтительнее, чем сравнение через type().
Посмотрим, как всё это работает на практике:
class Dog:
pass
# Создаём объект
my_dog = Dog()
# type() — узнаём класс объекта
print(type(my_dog)) # <class '__main__.Dog'>
print(type(my_dog) == Dog) # True
# id() — уникальный адрес в памяти
print(id(my_dog)) # например: 4432156320 (число будет другим у вас!)
# isinstance() — проверяем принадлежность классу
print(isinstance(my_dog, Dog)) # True
print(isinstance(my_dog, str)) # False — это явно не строка
💬 Почему isinstance() лучше чем type() ==? Потому что isinstance учитывает наследование. Если класс Poodle является подклассом Dog, то isinstance(poodle_obj, Dog) вернёт True. А type(poodle_obj) == Dog вернёт False, ведь тип будет именно Poodle. Наследование мы изучим позже — просто запомните: для проверки типа используйте isinstance().
🧪 Демонстрация: три объекта — три уникальных адреса
Вот самое важное, что нужно понять про объекты: каждый раз, когда вы вызываете класс и создаёте новый объект, Python создаёт отдельную, независимую сущность в памяти. Даже если все объекты созданы по одному классу — они разные.
Это как если бы вы трижды использовали одну форму для печенья: у вас три печенья одной формы, но это три разных печенья. Можно одно съесть, второе уронить, третье украсить — они независимы друг от друга.
class Dog:
pass
# Создаём три объекта одного класса
dog1 = Dog()
dog2 = Dog()
dog3 = Dog()
# Проверяем их id — каждый уникален!
print(id(dog1)) # например: 4432156320
print(id(dog2)) # например: 4432156480 ← другой!
print(id(dog3)) # например: 4432156640 ← тоже другой!
# Все три — это Dog
print(isinstance(dog1, Dog)) # True
print(isinstance(dog2, Dog)) # True
print(isinstance(dog3, Dog)) # True
# Но они НЕ равны друг другу (это разные объекты)
print(dog1 == dog2) # False
print(dog1 is dog2) # False — оператор is проверяет именно id
Посмотрите на последние две строки. В Python есть два способа сравнивать:
== — равенство по значению
Одинаково ли содержимое объектов? Два разных списка [1, 2] и [1, 2] равны по ==, хотя это разные объекты.
is — идентичность
Это буквально один и тот же объект в памяти? Сравнивает id(). dog1 is dog1 — True. dog1 is dog2 — False.
🎯 Золотое правило: каждый вызов класса создаёт новый уникальный объект. Один и тот же класс — бесконечное количество объектов. Это и есть мощь ООП: один шаблон, тысячи экземпляров.
💻 Полный пример: собираем всё вместе
Давайте напишем чуть более осмысленный пример и используем все инструменты, которые узнали сегодня:
class Car:
pass
# Создаём три автомобиля
car1 = Car()
car2 = Car()
car3 = Car()
# Проверяем типы
print(type(car1)) # <class '__main__.Car'>
print(type(car2)) # <class '__main__.Car'>
# Каждый автомобиль уникален — свой id
print(f"Адрес car1: {id(car1)}")
print(f"Адрес car2: {id(car2)}")
print(f"Адрес car3: {id(car3)}")
print(f"Все id разные: {id(car1) != id(car2) != id(car3)}") # True
# Проверяем принадлежность
print(isinstance(car1, Car)) # True
print(isinstance(car1, int)) # False — это явно не число
# car1 и car2 — разные объекты, хотя оба Car
print(car1 is car2) # False
print(car1 is car1) # True — сам с собой всегда True
Запустите этот код в своём интерпретаторе Python. Числа в id() у вас будут другими — это нормально: они зависят от конкретного запуска программы и состояния памяти вашего компьютера. Но они всегда будут различаться для разных объектов.
📋 Итоги урока
Сегодня вы сделали огромный шаг — написали первые классы и объекты на Python. Вот что теперь вы знаете:
class ИмяКласса:— синтаксис объявления класса- Имена классов пишутся в PascalCase — каждое слово с заглавной буквы
pass— заглушка для пустого тела класса (и не только)obj = MyClass()— создание объекта (экземпляра) класса- Объект и экземпляр — это синонимы
type(obj)— показывает класс объектаid(obj)— уникальный адрес объекта в памятиisinstance(obj, Class)— проверяет принадлежность классу (предпочтительнееtype() ==)- Каждый вызов класса создаёт новый уникальный объект с уникальным
id
🚀 В следующем уроке мы оживим наши объекты: добавим атрибуты через __init__ — специальный метод инициализации. Вы поймёте, как каждый объект получает свои собственные данные, и наш класс Dog наконец научится хранить имя, породу и возраст каждой конкретной собаки.