Книга «Знакомство с Python»
Здравствуйте, Хаброжители!
Пора пополнять библиотеку программиста.
Пытаетесь найти что-нибудь для начинающих о языке Python в интернете? Не можете решить, с чего начать? Как структурировать это море информации? В каком порядке изучать?
Если вы задаетесь подобными вопросами, потому что хотите заложить фундамент будущей карьеры питониста — эта книга для вас!
Вместо скучного перечисления возможностей языка авторы рассказывают, как сочетать разные структурные элементы Python, чтобы сразу создавать скрипты и приложения.
Книга построена по принципу 80/20: большую часть полезной информации можно усвоить, изучив несколько критически важных концепций. Освоив самые популярные команды и приемы, вы сразу сосредоточитесь на решении реальных повседневных задач.
Знакомство с SQLITE
Существует много разных ядер баз данных SQL, и некоторые из них лучше подходят для каких-то конкретных целей, чем другие. Одно из самых простых и облегченных ядер баз данных SQL — SQLite — входит в стандартную установку Python, а значит, уже работает на вашем компьютере.
В этом разделе вы научитесь пользоваться пакетом sqlite3 для создания новых баз данных SQLite, а также для хранения и чтения данных.
Основы SQLite
Вот основные этапы работы с SQLite:
1. Импортирование пакета sqlite3.
2. Подключение к существующей БД или создание новой.
3. Выполнение команд SQL.
4. Закрытие подключения к БД.
Начнем знакомство с ними в интерактивном окне IDLE. Откройте IDLE и введите следующие команды:
>>> import sqlite3
>>> connection = sqlite3.connect("test_database.db")
Функция sqlite3.connect () используется для подключения или создания новой базы данных.
При выполнении команды .connect («test_database.db») Python ищет существующую БД с именем «test_database.db». Если БД с таким именем не найдена, в текущем рабочем каталоге создается новая.
Чтобы создать БД в другом каталоге, необходимо указать полный путь в аргументе .connect ().
ПРИМЕЧАНИЕ
Также возможно создать базу данных в памяти, передав .connect () строку »: memory:»:connection = sqlite3.connect(":memory:")
Этот способ хорошо подходит для хранения данных, которые должны существовать только во время работы программы.
Аргумент .connect () возвращает объект sqlite3.Connection. В этом можно убедиться при помощи type ():
>>> type(connection)
Объект Connection осуществляет соединение между программой и базой данных. Он содержит набор атрибутов и методов, которые могут использоваться для взаимодействия с БД.
Для хранения данных понадобится объект Cursor, который можно получить вызовом connection.cursor ():
>>> cursor = connection.cursor()
>>> type(cursor)
Объект sqlite3.Cursor становится «окном» для взаимодействия с базой данных. При помощи Cursor можно создавать таблицы базы данных, выполнять команды SQL и получать результаты запроса.
ПРИМЕЧАНИЕ
В терминологии баз данных курсором называется объект, предназначенный для выборки результатов запроса к базе данных — по одной строке данных за раз.
Воспользуемся функцией SQLite datetime () для получения значения текущего местного времени:
>>> query = "SELECT datetime('now', 'localtime');"
>>> results = cursor.execute(query)
>>> results
«SELECT datetime ('now', 'localtime');» — команда SQL, возвращающая дату и время в настоящий момент. Текст запроса присваивается переменной query и передается cursor.execute (). Команда применяет запрос к базе данных и возвращает объект Cursor, который присваивается переменной results.
Возможно, вас интересует, где увидеть время, возвращенное datetime (). Чтобы получить результаты запроса, используйте метод results.fetchone (), который возвращает кортеж с первой строкой результатов:
>>> row = results.fetchone()
>>> row
('2018-11-20 23:07:21',)
Так как .fetchone () возвращает кортеж, необходимо обратиться к первому элементу для получения строки с информацией о дате и времени:
>>> time = row[0]
>>> time
'2018-11-20 23:09:45'
Наконец, вызовите connection.close () для закрытия подключения к базе данных:
>>> connection.close()
Важно всегда закрывать подключение к БД после завершения работы с ней, чтобы системные ресурсы не оставались занятыми после того, как ваша программа прекратит работу.
Использование with для управления подключением к базе данных
Вспомните, о чем мы говорили в главе 12: команда with может использоваться с open () для открытия файла и его автоматического закрытия после выполнения блока with. Та же схема используется с подключениями баз данных SQLite, и этот способ открытия подключений считается предпочтительным.
Пример использования datetime () из предыдущего примера с командой with для управления подключением к БД:
>>> with sqlite3.connect("test_database.db") as connection:
... cursor = connection.cursor()
... query = "SELECT datetime('now', 'localtime');"
... results = cursor.execute(query)
... row = results.fetchone()
... time = row[0]
...
>>> time
'2018-11-20 23:14:37'
В этом примере объект Connection, возвращенный sqlite3.connect (), присваивается переменной connection в команде with.
Код в блоке with создает новый объект Cursor методом connection.cursor (), а затем получает текущее время методами .execute () и .fetchone () объекта Cursor.
Управление подключениями к базе данных с помощью команды with обладает множеством преимуществ. Полученный код часто оказывается более чистым и компактным, чем код без использования with. Более того, как будет показано в следующем примере, любые изменения, вносимые в базу данных, автоматически сохраняются.
Работа с таблицами базы данных
Обычно создавать целую базу данных только для получения текущего времени не стоит. Базы данных, как правило, используются для сохранения и чтения информации. Чтобы сохранить информацию в базе, следует создать таблицу и записать в нее набор значений.
Создадим таблицу People с тремя столбцами: FirstName, LastName и Age. Запрос SQL для создания этой таблицы выглядит так:
Обратите внимание: после FirstName и LastName следует слово TEXT, а после Age следует слово INT. Оно сообщает SQLite, что значения в столбцах FirstName и LastName являются текстовыми, тогда как значения в столбце Age являются целыми числами.
После того как таблица будет создана, ее можно заполнить данными командой INSERT INTO SQL. Следующий запрос вставляет значения Ron, Obvious и 42 в столбцы FirstName, LastName и Age соответственно:
INSERT INTO People VALUES('Ron', 'Obvious', 42);
Обратите внимание: строки 'Ron' и 'Obvious' заключены в одинарные кавычки. При этом они остаются валидными строками в Python, но, что важнее, в SQLite валидны только строки в одинарных кавычках.
ВАЖНО!
Когда вы записываете запросы в SQL в виде строк на языке Python, проследите, чтобы они заключались в двойные кавычки. Это позволит вам использовать одинарные кавычки внутри них как ограничители строк в SQLite.SQLite — не единственная СУБД SQL, где действует соглашение об одинарных кавычках. Постоянно помните об этом, работая с базами данных SQL.
А теперь посмотрим, как выполнить эти команды и сохранить изменения в базе данных. Сначала это будет сделано без команды with.
В новом окне редактора введите следующую программу:
import sqlite3
create_table = """
CREATE TABLE People(
FirstName TEXT,
LastName TEXT,
Age INT
);"""
insert_values = """
INSERT INTO People VALUES(
'Ron',
'Obvious',
42
);"""
connection = sqlite3.connect("test_database.db")
cursor = connection.cursor()
cursor.execute(crate_table)
cursor.execute(insert_values)
connection.commit()
connection.close()
Сначала создаются две строки с командами SQL, которые создают таблицу People и вставляют в нее данные. Эти строки присваиваются переменным create_table и insert_values.
Обе команды записываются в синтаксисе с утроенными кавычками, чтобы мы могли отформатировать код. SQL игнорирует отступы, что позволяет использовать пробелы в строке для улучшения удобочитаемости кода Python.
Затем мы создаем объект Connection вызовом sqlite3.connect () и присваиваем его переменной connection. Также можно создать объект Cursor вызовом connection.cursor () и использовать его для выполнения двух команд SQL.
Наконец, метод connection.commit () сохраняет информацию в базе данных. Этот метод сохраняет внесенные изменения. Если не выполнить connection.commit (), то таблица People создана не будет.
Сохраните файл и нажмите F5, чтобы запустить программу. База данных test_database.db содержит таблицу People с одной строкой данных. В этом можно убедиться в интерактивном окне:
>>> connection = sqlite3.connect("test_database.db")
>>> cursor = connection.cursor()
>>> query = "SELECT * FROM People;"
>>> results = cursor.execute(query)
>>> results.fetchone()
('Ron', 'Obvious', 42)
А теперь перепишем программу с использованием команды with для управления подключением к базе данных.
Прежде чем что-либо делать, необходимо удалить таблицу People, чтобы создать ее заново. Введите следующий код в интерактивном окне, чтобы удалить таблицу People из базы данных:
>>> cursor.execute("DROP TABLE People;")
>>> connection.commit()
>>> connection.close()
Вернитесь к окну редактора и измените программу следующим образом:
import sqlite3
create_table = """
CREATE TABLE People(
FirstName TEXT,
LastName TEXT,
Age INT
);"""
insert_values = """
INSERT INTO People VALUES(
'Ron',
'Obvious',
42
);"""
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute(create_table)
cursor.execute(insert_values)
Ни вызов connection.close (), ни вызов connection.commit () не обязательны. Любые изменения, вносимые в базу данных, будут автоматически сохранены при завершении выполнения блока with. Это еще одно преимущество использования команды with для управления подключением к БД.
Выполнение нескольких команд SQL
Сценарий SQL представляет собой набор разделенных точкой с запятой команд SQL, которые могут выполняться одновременно. Объекты Cursor содержат метод .executescript () для выполнения сценариев SQL.
Следующая программа выполняет сценарий SQL, который создает таблицу People и вставляет в нее несколько значений:
import sqlite3
sql = """
DROP TABLE IF EXISTS People;
CREATE TABLE People(
FirstName TEXT,
LastName TEXT,
Age INT
);
INSERT INTO People VALUES(
'Ron',
'Obvious',
'42'
);"""
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.executescript(sql)
Также возможно выполнить несколько сходных команд, вызвав метод .executemany () и передав кортеж кортежей, в котором каждый внутренний кортеж предоставляет информацию для одной команды.
Например, если у вас имеется большой набор записей о людях, которые нужно вставить в таблицу People, вы можете сохранить эту информацию в следующем кортеже кортежей:
people_values = (
("Ron", "Obvious", 42),
("Luigi", "Vercotti", 43),
("Arthur", "Belling", 28)
)
После этого всю информацию можно вставить всего одной строкой кода:
cursor.executemany("INSERT INTO People VALUES(?, ?, ?)", people_values)
Вопросительные знаки обозначают место для подстановки элементов кортежей, содержащихся в people_values. Это называется параметризованной командой.
Каждый знак? представляет параметр, который заменяется значением из people_values при выполнении метода. Параметры заменяются по порядку. Иначе говоря, первый знак? заменяется первым значением в people_values, второй знак? заменяется вторым значением и т. д.
Проблемы безопасности с параметризованными командами
По соображениям безопасности — особенно при взаимодействиях с таблицами SQL, основанными на данных, введенных пользователем, — всегда следует применять параметризованные команды SQL. Дело в том, что пользователь теоретически может ввести данные, которые выглядят как код SQL и вызывают неожиданное поведение команд SQL. Это называется атакой внедрения SQL, причем, возможно, у пользователя нет вредоносных намерений и это происходит абсолютно случайно.
Допустим, вы хотите вставить запись в таблицу People на основании информации, введенной пользователем. Первая попытка может выглядеть примерно так:
import sqlite3
# Получить данные людей от пользователя
first_name = input("Enter your first name: ")
last_name = input("Enter your last name: ")
age = int(input("Enter your age: "))
# Выполнить команды вставки для введенных данных
query = (
"INSERT INTO People Values"
f"('{first_name}', '{last_name}', {age});"
)
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute(query)
А если имя пользователя содержит апостроф? Попробуйте добавить в таблицу имя Flannery O’Connor — и вы увидите, что программа перестает работать. Дело в том, что апостроф — то же самое, что одинарная кавычка, и для программы все выглядит так, словно код SQL завершается раньше, чем вы предполагали.
В данном случае код только порождает ошибку, что уже достаточно плохо. Однако в некоторых случаях некорректный ввод может привести к повреждению всей таблицы. Многие другие трудно прогнозируемые случаи могут нарушить структуру таблицы SQL и даже удалить части базы данных. Чтобы этого не произошло, всегда используйте параметризованные команды.
В следующем коде параметризованная команда используется для безопасной вставки пользовательского ввода в базу данных:
import sqlite3
first_name = input("Enter your first name: ")
last_name = input("Enter your last name: ")
age = int(input("Enter your age: "))
data = (first_name, last_name, age)
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute("INSERT INTO People VALUES(?, ?, ?);", data)
Параметризация также пригодится для обновления строки в базе данных командой SQL UPDATE:
cursor.execute(
"UPDATE People SET Age=? WHERE FirstName=? AND LastName=?;",
(45, 'Luigi', 'Vercotti')
)
Этот код обновляет значение столбца Age значением 45 для строки, в которой поле FirstName содержит 'Luigi', а поле LastName содержит 'Vercotti'.
Чтение данных
Вставка и обновление информации в базе данных вряд ли принесут пользу, если вам не удается прочитать информацию из этой базы данных.
Для чтения информации из базы данных можно воспользоваться методами курсора .fetchone () и .fetchall (). Метод .fetchone () возвращает одну строку данных из результатов запроса, тогда как .fetchall () читает сразу все результаты запроса.
Следующая программа демонстрирует использование .fetchall ():
import sqlite3
values = (
("Ron", "Obvious", 42),
("Luigi", "Vercotti", 43),
("Arthur", "Belling", 28),
)
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute("DROP TABLE IF EXISTS People")
cursor.execute("""
CREATE TABLE People(
FirstName TEXT,
LastName TEXT,
Age INT
);"""
)
cursor.executemany("INSERT INTO People VALUES(?, ?, ?);", values)
# Выбрать все имена и фамилии людей, возраст которых
# превышает 30 лет
cursor.execute(
"SELECT FirstName, LastName FROM People WHERE Age > 30;"
)
for row in cursor.fetchall():
print(row)
В этой программе мы сначала удаляем таблицу People, чтобы уничтожить изменения, внесенные в предыдущих примерах этого раздела. Затем мы заново создаем таблицу People и вставляем в нее несколько значений. Далее вызовом .execute () выполняется команда SELECT, которая возвращает имена и фамилии всех людей, возраст которых превышает 30.
Наконец, .fetchall () возвращает результаты запроса в виде списка кортежей, в котором каждый кортеж содержит одну строку данных из результатов запроса.
Если ввести программу в новом окне редактора, а затем сохранить и запустить файл, в интерактивном окне появится следующий вывод:
('Ron', 'Obvious')
('Luigi', 'Vercotti')
Действительно, это единственные люди в базе данных, чей возраст более 30 лет.
Упражнения
1. Создайте новую базу данных, содержащую таблицу Roster. Таблица состоит из трех полей: Name, Species и Age. Столбцы Name и Species должны быть текстовыми, а столбец Age должен быть целочисленным полем.
2. Заполните созданную таблицу следующими значениями:
3. Обновите поле Name записи Jadzia Dax, чтобы оно содержало значение Ezri Dax.
4. Выведите значения Name и Age для всех строк данных, у которых поле Species содержит значение Bajoran.
Веб-сайт realpython.com был запущен в 2012 году. В настоящее время он ежемесячно помогает более чем трем миллионам разработчиков Python своими бесплатными учебными пособиями и курсами.
Все, кто работал над книгой «Знакомство с Python», — практики, имеющие многолетний профессиональный опыт в программировании, члены преподавательской команды Real Python.
Дэвид Эймос — технический директор по контенту сайта Real Python. После ухода из образовательной системы в 2015 году Дэвид работал на различных технических должностях как программист и специалист по обработке данных. В 2019 году он перешел в штат Real Python, чтобы развить свое увлечение образованием. Дэвид возглавил переработку и обновление материала книги для Python 3.
Дэн Бейдер — владелец и старший редактор сайта Real Python, а также ведущий разработчик образовательной платформы realpython.com. Дэн занимается программированием более 20 лет, он имеет степень магистра в области компьютерных технологий. А кроме того, Дэн написал «Python Tricks» — популярную книгу для продвинутых разработчиков Python.
Джоанна Яблонски — главный редактор сайта Real Python. Она любит естественные языки в той же степени, что и языки программирования. Ее пристрастие к загадкам, закономерностям и нудным мелочам привело к тому, что она выбрала карьеру переводчика. Прошло совсем немного времени, и она влюбилась в новый язык — Python! Джоанна присоединилась к проекту Real Python в 2018 году и с тех пор помогает программистам Python повышать профессиональную квалификацию.
Флетчер Хейслер — основатель проекта Hunter, он обучает разработчиков тонкостям программирования и построению безопасных современных веб-приложений. Флетчер, один из основателей Real Python, в 2012 году написал первую версию учебного курса Python, на котором основана эта книга.
Более подробно с книгой можно ознакомиться на сайте издательства:
» Оглавление
» Отрывок
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Python