Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Программирование на языке Python. Учебное пособие.pdf
X
- •ПРЕДИСЛОВИЕ
- •I. ВВЕДЕНИЕ В ПРОГРАММИРОВАНИЕ
- •1. Начало работы
- •2. Основные понятия
- •Ввод и вывод данных
- •Логические операторы
- •Условные инструкции
- •Инструкции
- •Типы данных
- •Константы и переменные
- •Арифметические операторы
- •Операторы сравнения
- •3. Функции
- •Определение функций
- •Встроенные функции
- •Многократное использование функций
- •Типы параметров функции
- •Область видимости
- •Обработка исключений
- •Строки документации
- •4. Контейнеры
- •Методы
- •Списки
- •Кортежи
- •Словари
- •Контейнеры внутри контейнеров
- •5. Операции со строками
- •6. Циклы
- •Цикл for
- •Цикл while
- •Операторы break и continue
- •Функция Enumerate
- •Вложенные циклы
- •Настройка функции print()
- •7. Модули
- •8. Файлы
- •Запись в файлы
- •Использование конструкции with
- •Чтение из файлов
- •CSV-файлы
- •II. ВВЕДЕНИЕ В ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ (ООП)
- •9. Парадигмы программирования
- •Состояние
- •Процедурное программирование
- •Функциональное программирование
- •Объектно-ориентированное программирование
- •10. Базовые понятия ООП
- •Наследование
- •Полиморфизм
- •Абстракция
- •Инкапсуляция
- •Композиция
- •11. Дополнительные понятия ООП
- •Как работают переменные
- •Ключевое слово is
- •Константа None
- •Классы – это объекты
- •Переменные класса и переменные экземпляра класса
- •Приватные переменные
- •Методы переопределения
- •Функция super
- •Переопределение встроенных методов
- •12. Создание игр
- •Карты
- •Колода
- •Игрок
- •Игра
- •Игра «War»
- •13. Bash
- •Запуск Bash
- •Использование Bash
- •Относительные и абсолютные пути
- •Навигация
- •Флаги
- •Редактор vim
- •Команда touch
- •Просмотр файлов командой less
- •Пользователи
- •Разрешения
- •Программы Bash
- •Скрытые файлы
- •Переменные окружения
- •Переменная окружения $PATH
- •Вертикальная черта (пайп)
- •Команда cat
- •Последние команды
- •Быстрые перемещения
- •Экранирование (escaping)
- •Регулярные выражения в Python
- •15. Менеджеры пакетов
- •Вкладка «Завершение»
- •Функция wildcard (подстановочный знак)
- •Другие инструменты
- •14. Регулярные выражения
- •Настройка
- •Простое совпадение
- •Игнорирование регистра
- •Вывод только конкретных совпадений слов
- •Совпадение в начале и в конце
- •Поиск совпадений с несколькими символами
- •Повторение
- •Диапазон
- •16. Управление версиями
- •Репозитории
- •Помещение и извлечение данных
- •Пример помещения данных
- •Пример извлечения данных
- •Откат версий
- •Команда git diff
- •Другой запрос на включение
- •IV. ВВЕДЕНИЕ В ИНФОРМАТИКУ
- •17. Структуры данных и алгоритмы
- •Что такое алгоритмы и структуры данных?
- •Нотация Big-O
- •Оператор деления по модулю
- •Сортировка методом пузырька
- •Поисковый алгоритм
- •Рекурсия
- •Абстрактные типы данных
- •Узлы
- •Стеки
- •Связанные списки
- •Массивы
- •Бинарные деревья
- •Поиск в ширину и в глубину
- •Хэш-таблицы
- •ЗАКЛЮЧЕНИЕ
- •БИБЛИОГРАФИЧЕСКИЙ СПИСОК

Как видно, здесь используется один и тот же интерфейс, а именно функция
print, для трех разных типов данных: строки, целого и вещественного числа.
Иначе говоря, чтобы вывести эти разнородные типы данных, нет необходимости определять и вызывать три отдельные функции персонально для каждого
типа данных и, таким образом, перегружать программу лишним кодом. Вместо
этого в программе используется функция print для представления единого
интерфейса для вывода всех этих данных.
В следующем примере использована встроенная функция type, возвра-
щающая тип данных объекта.
s = 'Hello World!'
print(type(s))
i = 10
print(type(i))
f = 15.2
print(type(f))
>> <class 'str'>
>> <class 'int'>
>> <class 'float'>
Посмотрим на другой пример. Допустим, хотим написать программу, ко-
торая может рисовать различные фигуры: треугольники, квадраты и круги.
Каждая из этих фигур рисуется по-своему, поэтому методы их рисования будут
иметь разные реализации. Для этого необходимо определить три различных
класса Triangle, Square и Circle и метод draw для каждого из них. Тогда
Triangle.draw() нарисует треугольник, Square.draw() нарисует квадрат, а Circle.draw() нарисует круг. Каждый из этих объектов имеет собственный интерфейс рисования draw, который умеет рисовать самого себя. Когда есть объект геометрической формы, можно вызвать функцию draw, чтобы
нарисовать фигуру. Один и тот же интерфейс представлен для всех различных
типов данных.
Если бы Python не поддерживал полиморфизм, понадобилась бы функция
(метод) для создания треугольника, и еще одна функция, называемая
draw_triangle, для его рисования; функция для создания круга и функция
draw_circle для его рисования и т.п. Поскольку Python обладает полимор-
91

физмом, любую фигуру можно просто нарисовать с помощью своего метода
рисования draw. Это делает объекты формы намного проще в использовании
и объяснении, например: есть три функции, представляющие три разные фигуры, и еще три функции, которые рисуют каждую из них; можно сказать тому,
кто использует код: имеются три фигуры – если необходимо нарисовать одну,
вызывается ее метод draw.
Заметим, что если бы был список упомянутых объектов и требовалось бы
нарисовать каждый из них, то сначала пришлось бы узнавать тип каждого объекта, затем вызывать подходящий для этого типа метод, увеличивая программу,
делая ее более сложной для чтения и написания, а также менее надежной. И это
усложнило бы процесс совершенствования программы, поскольку всякий раз
при добавлении новой фигуры пришлось бы отслеживать каждое место
в коде, где рисуются фигуры, проверять их тип данных (чтобы выяснить, какой
метод использовать) и вызывать новую функцию рисования. Ниже приведен
пример рисования фигур с полиморфизмом и без него.
# Рисуем фигуры без использования полиморфизма
shapes = [tr1, sql, cr1]
for a_shape in shapes:
if type(a_shape) == "Triangle":
a_shape.draw_triangle()
if type(a_shape) == "Square":
a_shape.draw_square()
if type(a_shape) == "___":
a_shape.draw_circle()
# Рисуем фигуры c использованием полиморфизма
shapes = [tr1,sql,cr1]
for a_shape in shapes:
a_shape.draw()
Заметим, что если бы возникла необходимость добавить новую фигуру
в список shapes без использования понятия полиморфизма, то нужно было бы
изменить код в цикле for для идентификации типа a_shape, а затем вызвать
соответствующий ему метод draw. При использовании же полиморфизма можно добавлять сколько угодно классов фигур в список shapes и фигура будет
рисовать себя без какого-либо дополнительного кода.
92

Абстракция
Абстракция – это концепция объектно-ориентированного программиро-
вания, которая «показывает» только существенные (релевантные) атрибуты
и «скрывает» ненужную информацию (иррелевантную). Иначе говоря, абстракция обычно определяется как извлечение релевантной информации из большого набора данных, где использование абстракции позволяет инженерам и другим специалистам упростить базовый код. Таким образом, суть абстракции состоит в том, чтобы сохранить информацию, уместную в данном контексте (релевантную информацию), и забыть информацию, не относящуюся к этому контексту (иррелевантную). Очевидно, что в процессе абстрагирования происходит
процесс изъятия или удаления (элиминации) характеристик чего-либо, чтобы
свести его к набору существенных (релевантных) характеристик.
При использовании абстракции в объектно-ориентированном программировании создается класс и определяются его методы. Допустим, создается класс
для представления человека. Когда определяется класс Person и связанные
с ним методы, то создается абстракция. Определение человека может включать
цвет глаз, волос, рост и этническую принадлежность, а также способность читать, писать и рисовать. К примеру, человек ростом 160 см с голубыми глазами,
светлыми волосами, не умеющий читать, писать и рисовать, и человек ростом
195 см с карими глазами, каштановыми волосами, умеющий читать, писать
и рисовать, попадают в категорию созданной абстракции человека.
Когда разрабатываются объектно-ориентированные программы, то создаются абстракции различных концепций, работающие вместе, формируя программу. Например, можно создать абстракцию человека и абстракцию правительства и смоделировать, сколько людей живет при каждом правительстве
в мире. Абстракция позволяет моделировать объекты с четкими границами
и заставлять их взаимодействовать друг с другом.
Инкапсуляция
В объектно-ориентированном программировании инкапсуляция скрывает
внутренние данные нашего кода. Когда код инкапсулирован, это означает, что
при его вызове вызывающая сторона не может получить доступ к внутренним
данным кода.
93

Рассмотрим метод get_data:
class Data:
def get_data(self, index, n):
data = [1, 2, 3, 4, 5]
data.append(n)
Метод имеет переменную с именем data. Когда вызывается get_data,
нет возможности получить доступ к этой переменной из-за инкапсуляции. Если
бы не было инкапсуляции, можно было бы получить доступ к переменной
data – и добавить к ней n:
# предупреждение: этот код не работает
Data.get_data.data.append(6)
Если бы это было разрешено, любой мог бы получить доступ к переменной
data в методе get_data. Вместо того чтобы полагаться на реализацию метода get_data, можно было бы добавлять n к данным. Это не проблема до тех
пор, пока не изменится реализация get_data. При принятии решения о типе
переменной data, является ли она кортежем или списком, и внесении изменений в код, это нарушает его, т.к. у кортежа нет метода append. Но из-за инкапсуляции этот сценарий невозможен (вот почему код не работает), и можно
быть уверенным, что изменения во внутренней реализации кода не нарушат код
клиента (клиент – это код вне класса, который использует объект).
Итак, инкапсуляция позволяет переменные и методы группировать в единое целое – объект.
class Rectangle():
def __init__(self, w, l):
self.width = w
self.len = l
def area(self):
return self.width * self.len
Как видим, переменные len и width хранят состояние объекта, которое
сгруппировано в том же блоке (объекте), что и метод area. Метод использует
состояние объекта, чтобы вернуть площадь прямоугольника.
94

class Data:
def __init__(self):
self.nums = [1, 2, 3, 4, 5]
def change_data(self, index, n):
self.nums[index] = n
Как уже было сказано выше, инкапсуляция позволяет скрыть конкретную
информацию и контролировать доступ к внутреннему состоянию объекта.
class Data:
def __init__(self):
self.nums = [1, 2, 3, 4, 5]
def change_data(self, index, n):
self.nums[index] = n
В этом фрагменте кода класс Data имеет переменную nums экземпляра
класса, содержащую список целых чисел. Существуют два способа изменить
элементы в nums – при помощи метода change_data или, напрямую получая
доступ к переменной nums экземпляра с помощью объекта Data.
class Data:
def __init__(self):
self.nums = [1, 2, 3, 4, 5]
def change_data(self, index, n):
self.nums[index] = n
data_one = Data()
data_one.nums[0] = 100
print(data_one.nums)
data_two = Data()
data_two.change_data(0, 100)
print(data_two.nums)
>> [100, 2, 3, 4, 5]
>> [100, 2, 3, 4, 5]
Оба способа изменения элементов nums вполне рабочие, но если попы-
таться переменную nums сделать кортежем вместо списка, то код клиента
nums[0] = 100 не сработает, так как кортежи неизменяемы.
95

Во многих языках эту проблему решают, определяя так называемые закрытые переменные и закрытые методы – переменные и методы, к которым
могут обращаться объекты в коде, реализующем различные методы, но клиент
не может. Кроме закрытых переменных и методов существуют открытые пе-
ременные, к которым клиент может получить доступ.
В Python все переменные – открытые. Вместо механизма закрытых переменных и методов используются конвенции (соглашения) имен. Если в программе есть переменная или метод, к которым вызывающий их не должен получить доступ, то перед их именами необходимо добавить нижнее подчеркивание, что сигнализирует о том, что их нельзя использовать.
class PublicPrivateExample:
def __init__(self):
self.public = "безопасно"
self._unsafe = "опасно"
def public_method(self):
# клиенты могут это использовать
pass
def _unsafe_method(self):
# клиенты не должны это использовать
pass
self.public = "безопасно"
self._unsafe = "опасно"
В этом фрагменте кода self.public использовать безопасно, а переменную self._unsafe использовать нельзя. То же самое касается возможностей использования методов public_method и _unsafe_method.
Композиция
Несмотря на то, что композиция не является одним из четырех столпов
объектно-ориентированного программирования, это важная концепция, связанная со всеми остальными. Композиция используется для представления отно-
96

шений «имеет». Это происходит, когда один объект хранит другой объект в качестве переменной. Например, предположим, что нужно представить отношения между собакой и ее владельцем – это отношение «имеет» – у собаки есть
хозяин. Для этого необходимо в первую очередь определить классы собак
и людей:
class Dog():
def __init__ (self, name, breed, owner):
self.name = name
self.breed = breed
self.owner = owner
class Person():
def __init__ (self, name):
self.name = name
Когда создается объект Dog, то передается объект Person в качестве па-
раметра owner:
mick = Person("Mick Jagger")
dog = Dog("Stanley", "French Bulldog", mick)
print(dog.owner)
>> Mick Jagger
Теперь у собаки "Stanley" (объект stan) есть владелец – объект Per-
son по имени "Mick Jagger" (экземпляр класса owner), на который можно
легко ссылаться.
11. Дополнительные понятия ООП
В этой главе будут рассмотрены дополнительные концепции, связанные
с объектно-ориентированным программированием. Хотя некоторые из этих
примеров характерны для Python, большинство из этих концепций присутствуют и в других языках, поддерживающих объектно-ориентированное программирование.
97

Как работают переменные
Переменная «указывает» на объект:
number = 100
В данном примере переменная number указывает на целочисленный объ-
ект со значением 100.
Если присваивается number другое значение:
number = 101,
то оно указывает на новый целочисленный объект со значением 101, а старый
со значением 100 отбрасывается, поскольку он больше не используется. Две
переменные могут указывать на один и тот же объект:
x = 100
y = x
Переменная x указывает на целочисленный объект со значением 100. Ко-
гда присваивается переменной y значение x, то переменная y теперь указывает
на тот же целочисленный объект, на который указывает x: обе переменные указывают на целочисленный объект со значением 100.
Посмотрим, что напечатает следующая программа.
x = 10
y = x
x += 1
print(x)
print(y)
Ответ 11 и 10. Переменная x указывает на целочисленный объект со значением 10, и когда создаете переменную y, она указывает на тот же целочисленный объект. Когда увеличиваете x, то она указывает на новый целочисленный объект со значением 11, но целочисленный объект со значением 10 не отбрасывается, потому что он используется: y по-прежнему указывает на целочисленный объект со значением 10. Итак, при печати x печатается 11, потому
что присвоили x новому целочисленному объекту – 11, но при печати y печатается 10, потому что изменение значения x, которое указывает на целочислен-
98

ный объект 11, не влияет на значение y. Вот еще один пример, иллюстрирующий это положение. Посмотрим, что будет напечатано на выходе этого кода.
x = [1, 2, 3]
y = x
y[2] = 100
print(x)
print(y)
Вывод будет [1, 2, 100] дважды. Причина в том, что и x, и y указывают на
один и тот же объект списка. В строке 3 при внесении изменений в этот единственный объект списка и при печати обеих переменных печатается объект
списка, на который они обе указывают, с изменениями, сделанными в строке 3.
Ключевое слово is
Ключевое слово is возвращает значение True, если два объекта являются
одним и тем же объектом (они хранятся в одном и том же месте в памяти),
и значение False – в противном случае.
class Person:
def __init__(self):
self.name = 'Bob'
bob = Person()
the_same_bob = bob
print(bob is the_same_bob)
another_bob = Person()
print(bob is another_bob)
>> True
>> False
При использовании ключевого слова is в выражении с объектами bob
и the_same_bob результатом будет True, поскольку обе переменные указывают на один и тот же объект Person. При создании нового объекта Person
и сравнении его с исходным объектом bob результатом будет False, поскольку переменные указывают на разные объекты Person.
99

Константа None
Встроенная константа None используется для обозначения отсутствия
значения:
x= None
x
>> None
Можно проверить, присвоено ли переменной значение None, используя
условные операторы, а именно ключевое слово is.
x = 10
if x is None:
print("x is None")
else:
print("x is not None:(")
x = None
if x is None:
print("x is None")
else:
print("x is not None:(")
>> x is not None:(
>> x is None
Так как Python является объектно-ориентированным языком, поэтому None –
тоже объект и имеет свой тип.
type(None)
>> <class 'NoneType'>
Классы – это объекты
В языке Python классы – это объекты. Эта идея восходит к первому универсальному объектно-ориентированному языку программирования SmallTalk. Это
означает, что при запуске программы, в которой определяется класс, Python превращает его в объект, который затем можно использовать в своей программе:
class Pterodactyl:
pass
100
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
