Случайные числа в программировании играют важную роль во многих процессах, например, при симуляции броска кости, генерации случайных входных данных или моделировании процессов.
Однако компьютер не способен создать по-настоящему случайное число, так как он является детерминированной системой, то есть при одних и тех же входных данных выдаёт один и тот же результат. Поэтому в программировании используются псевдослучайные числа, вычисляемые на основе начального значения, которое называют seed (с англ. – зерно).
Для работы с псевдослучайными числами в Python используется модуль random. Для использования функций из этого модуля его следует импортировать:
import random
Вместе с установкой интерпретатора Python вы получаете большой набор готовых инструментов, собранных в стандартной библиотеке Python. Модули, входящие в неё, считаются неотъемлемой частью языка, что позволяет не устанавливать дополнительные пакеты и решать самые разные задачи: математические вычисления, генерация случайных чисел, работа с датой и временем, и много другое.
Среди всего многообразия модулей стандартной библиотеки модуль math является незаменимым помощником, когда речь заходит о более сложных математических вычислениях, которые выходят за рамки базовых арифметических операций, рассмотренных ранее.
Для использования функций и констант из этого модуля его следует импортировать:
import math
Написание сложной программы с нуля, в котором весь код содержится в одном огромном файле, может быть невероятно долгим и трудозатратным. Гораздо эффективнее не только разбить программу на отдельные файлы (и папки), но и использовать уже готовые части кода – те, что вы ранее написали сами или те, что были разработаны и протестированы другими специалистами.
Файл с расширением .py называется модулем, а имя этого файла является именем модуля. При этом все переменные, функции и классы из одного модуля можно свободно использовать в других модулях. Если код в файле предназначен для непосредственного запуска, а не для повторного использования, то такой файл часто называют скриптом.
Набор связанных модулей, которые совместно обеспечивают определённую функциональность, называется пакетом. Он представляет собой папку, которая содержит несколько файлов с расширением .py и часто содержит специальный служебный файл __init__.py, обозначающий, что эта папка является пакетом.
Библиотека является более общим понятием, так как в программировании под ней понимают набор готового кода, который можно повторно использовать в своём проекте. Библиотека в Python может быть представлена как одним модулем, так и пакетом, который, в том числе, может содержать другие пакеты.
Возможности перегрузки операторов в Python не ограничиваются базовыми арифметическими операциями и сравнением объектов, так как существуют методы, позволяющие создать свою собственную коллекцию, то есть класс, представляющий собой контейнер элементов, как список или словарь. Объекты такого класса могут быть перебраны в цикле for, использовать оператор in и функцию len(), а также синтаксис обращения по индексу или ключу через квадратные скобки.
Методы __iter__() и __next__() лежат в основе механизма итерации в Python и позволяют перебирать объекты в цикле for или использовать их в других функциях, которые принимают итерируемый объект, например, sum() или zip(). Также эти методы можно явно вызывать с помощью функций iter() и next(), как мы уже делали для встроенных типов данных, когда рассматривали работу итерируемых объектов.
Метод __iter__() превращает объект в итератор. В простейшем случае, сам итерируемый объект часто выступает в роли своего итератора, поэтому __iter__() просто возвращает self.
Встроенные типы данных поддерживают множество стандартных операций. Например, числа поддерживают все арифметические операции, а строки мы можем складывать и умножать на число.
Однако, когда вы создаёте собственный класс, Python использует поведение по умолчанию и не знает, какое логическое значение вы вкладываете в операции сложения или сравнения для объектов этого класса.
Например, давайте вспомним, что в математике есть такое понятие как вектор – направленный отрезок, соединяющий две точки. Тогда координатами вектора AB с началом в точке A с координатами (x1; y1) и концом в точке B с координатами (x2; y2) является разность соответствующих координат конца и начала, то есть (x2 - x1; y2 - y1). Поэтому при создании класса вектора Vector мы можем инициализировать его координатами x и y, представляющими собой его смещение относительно начала координат:
class Vector:
def __init__(self, x: int | float, y: int | float):
self.x = x
self.y = y
def __repr__(self) -> str:
return f"Vector(x={self.x}, y={self.y})"
До сих пор мы говорили об атрибутах экземпляра класса, которые принадлежат конкретному объекту. Однако в Python представлены, в том числе, и атрибуты класса, которые принадлежат самому классу и являются общими для всех его экземпляров.
Чтобы понять разницу, давайте рассмотрим пример с классом Parcel, описывающим посылку в курьерской службе. У такого класса могут быть следующие атрибуты:
- Атрибуты экземпляра класса – это уникальные характеристики конкретной посылки. Например,
weight для веса или address для адреса доставки. У каждой посылки свой вес и свой адрес.
- Атрибуты класса – это характеристики, которые одинаковы для всех посылок, обрабатываемых этой службой. Например,
carrier для названия курьерской компании. Это свойство не меняется от посылки к посылке.
В процессе разработки часто возникает необходимость получить информацию о созданном объекте: его атрибутах, методах или просто читабельное строковое представление. Для этого в Python существуют специальные инструменты.
Когда мы работали с объектами встроенных типов данных, то достаточно легко могли выводить их на экран. Но если мы используем функцию print() с объектом собственного класса, то увидим лишь название класса и адрес этого объекта в памяти компьютера.
Давайте создадим простой класс Student для работы со студентами и выведем на экран объект этого класса:
class Student:
def __init__(self, name: str, email: str):
self.name = name
self.email = email
student1 = Student("Романова Е.А.", "best_of_the_best@example.ru")
print(student1)
# Вывод: <__main__.Student object at 0x...>
Здесь вы видите имя __main__, означающее, что данный класс был определен в запускаемом файле. А также название класса объекта и адрес этого объекта в шестнадцатеричной системе счисления.
Наследование и полиморфизм – это ещё два основных принципа ООП, которые часто используются вместе. Наследование позволяет дочернему классу перенимать свойства и поведение родительского класса. А полиморфизм позволяет работать с объектами разных классов через единый интерфейс.
Наследование – это механизм, который позволяет создавать новый класс на основе уже существующего. Это как в жизни: вы наследуете черты от своих родителей, но при этом имеете и свои собственные, уникальные качества. В программировании это означает, что дочерний класс (потомок) получает все атрибуты и методы родительского класса (предка), а затем может добавлять новые или переопределять существующие.
Это ключевой принцип, позволяющий повторно использовать код. Вместо того чтобы писать один и тот же код для похожих классов, вы создаете базовый класс с общими характеристиками, а затем наследуете его.
Инкапсуляция – это один из трёх основных ООП. И как мы уже говорили, её главной идеей является сокрытие внутренней реализации объекта от внешнего мира и предоставление контролируемого доступа к его данным.
Вспомним пример с автомобилем из предыдущего параграфа. Инкапсуляция позволяет контролировать, как и когда данные объекта могут быть изменены. Так, вызов метода продажи sell() не только изменяет статус наличия автомобиля в автосалоне, но и выполняет другие необходимые действия, например, выводит сообщение о продаже. Прямое изменение атрибута не обеспечит совершение этих действий.
В отличие от некоторых других языков программирования, в Python нет строгих механизмов для полного запрета доступа к атрибутам и методам. Вместо этого используется соглашение об именовании, которое говорит разработчикам, как следует обращаться к этим данным.
Любой объект создаётся на основе класса, который является шаблоном, определяющим свойства и поведение объекта. Поэтому такой объект ещё называют экземпляром класса. Может быть вы помните, что функция type() возвращает конструкцию class <тип>. Это связано с тем, что все встроенные типы данных являются классами. Например, строка "Привет!" является объектом или экземпляром класса str и поддерживает всего его свойства и методы.
И мы можем не только использовать встроенные классы, но и создавать свои собственные. Давайте создадим класс для автомобиля в автосалоне. Для этого нам понадобится ключевое слово class, за которым следует имя класса, написанное с большой буквы. Если названия переменных и функций мы писали в «змеином регистре» snake_case, то классы принято именовать в «верблюжьем регистре» CamelCase. То есть каждое слово начинается с заглавной буквы и без разделителей следует за предыдущим:
class Car:
pass
Здесь мы создали пустой класс Car с помощью уже знакомого нам ключевого слова pass.