Файл: Чистыйкод дляпродолжающи х.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 03.12.2023

Просмотров: 365

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

СОДЕРЖАНИЕ

Path('spam') / 'bacon' / 'eggs'WindowsPath('spam/bacon/eggs')>>> Path('spam') / Path('bacon/eggs')WindowsPath('spam/bacon/eggs')>>> Path('spam') / Path('bacon', 'eggs')WindowsPath('spam/bacon/eggs')Так как этот код выполняется на компьютере с Windows, Path() возвращает объекты WindowsPath. В macOS и Linux возвращается объект PosixPath. (POSIX — набор стандартов для Unix-подобных операционных систем, эта тема выходит за рамки книги.) Для наших целей различать эти два типа необязательно.Объект Path можно передать любой функции стандартной библиотеки Python, ожи- дающей получить имя файла. Например, вызов функции open(Path('C:\\')/'Users'/'Al'/'Desktop'/'spam.py') эквивалентен open(r'C:\Users\Al\Desktop\spam.py')Домашний каталогМногие пользователи создают папку под названием домашняя папка (home folder) или домашний каталог (home directory), предназначенную для хранения персо- нальных файлов на компьютере. Чтобы получить объект Path для домашней папки, вызовите Path.home():>>> Path.home()WindowsPath('C:/Users/Al')Домашние каталоги помещаются в определенное место, которое зависит от типа операционной системы.В Windows домашние каталоги хранятся в каталоге C:\UsersНа Mac домашние каталоги хранятся в каталоге /UsersВ Linux домашние каталоги часто хранятся в каталоге /homeВаши сценарии почти наверняка будут иметь разрешение для чтения и записи в файлы в вашем домашнем каталоге, поэтому он идеально подходит для хранения файлов для ваших программ Python. Файловая система43Текущий рабочий каталогУ каждой программы, выполняемой на компьютере, имеется текущий рабочий каталог (CWD, Current Working Directory). Считается, что все имена файлов или пути, имена которых не начинаются с названия корневой папки, хранятся в текущем рабочем каталоге. Хотя термин «папка» считается более современным, стандартным названием является «текущий рабочий каталог» (или просто «рабочий каталог»), а не «текущая рабочая папка».Текущий рабочий каталог можно получить в виде объекта Path при помощи функ- ции Path.cwd() и изменить его функцией os.chdir(). Введите следующий фрагмент в интерактивной оболочке:>>> from pathlib import Path>>> import os>>> Path.cwd() ❶WindowsPath('C:/Users/Al/AppData/Local/Programs/Python/Python38')>>> os.chdir('C:\\Windows\\System32') ❷>>> Path.cwd()WindowsPath('C:/Windows/System32')Здесь в качестве текущего рабочего каталога выбран C:\Users\Al\AppData\Local\Pro- grams\Python\Python38❶, так что имя файла project.docx будет относиться к файлу C:\Users\Al\AppData\Local\Programs\Python\Python38\project.docx. А если сменить текущий рабочий каталог на C:\Windows\System32❷, имя файла project.docx будет относиться к C:\Windows\System32\project.docxПри попытке выбрать несуществующий каталог Python выдает сообщение об ошибке:>>> os.chdir('C:/ThisFolderDoesNotExist')Traceback (most recent call last): File "", line 1, in FileNotFoundError: [WinError 2] The system cannot find the file specified:'C:/ThisFolderDoesNotExist'Ранее для получения текущего рабочего каталога в виде строки использовалась функция os.getcwd() из модуля osАбсолютные и относительные путиСуществуют два способа определения путей к файлам.Абсолютный путь — всегда начинается от корневой папки.Относительный путь — задается относительно текущего рабочего каталога программы. 44 Глава 2.Подготовка среды и командная строкаТакже существуют папки с названием (точка) и (точка, точка). Это не реально существующие папки, а специальные имена, которые могут использоваться в путях. Точка () вместо имени папки является сокращенным обозначением для текущей папки. Две точки () обозначают родительскую папку.На рис. 2.2 представлены примеры папок и файлов. Когда текущим рабочим ката- логом назначается C:\bacon, относительные пути других файлов и папок задаются так, как показано на иллюстрации.Префикс .\ в начале относительного пути не обязателен. Например, .\spam.txt и spam.txt обозначают один и тот же файл.Абсолютные путиТекущий рабочий каталогОтносительные путиРис. 2.2. Относительные пути для папок и файлов в рабочем каталоге C:\baconПрограммы и процессыПрограммой называется любое приложение, которое можно запустить на ком- пьютере: веб-браузер, электронная таблица, текстовый процессор и т. д. Процесс представляет собой выполняемый экземпляр программы. Например, на рис. 2.3 показаны пять работающих процессов одной программы-калькулятора.Процессы существуют независимо друг от друга, даже если они выполняют одну и ту же программу. Например, если вы запустите сразу несколько экземпляров про- граммы Python, каждый процесс имеет собственный набор значений переменных. Каждый процесс (даже если они относятся к одной программе) имеет свой текущий рабочий каталог и значения переменных среды. Как правило, в каждой командной строке выполняется только один процесс (хотя ничто не мешает вам открыть не- сколько окон командной строки одновременно). Командная строка45Рис. 2.3. Одна программа-калькулятор запущена в нескольких экземплярах, каждый экземпляр является отдельным процессомКаждая операционная система использует собственный способ просмотра списка выполняемых процессов. В Windows Диспетчер задач вызывается комбинацией кла- виш Ctrl-Shift-Esc. В macOS следует выполнить команду ApplicationsUtilitiesActivity Monitor. В Ubuntu Linux комбинация клавиш Ctrl-Alt-Del открывает приложение, так- же называемое Диспетчером задач (Task Manager). В Диспетчере задач также можно принудительно завершить выполняемый процесс, если он перестает реагировать на действия пользователя.Командная строкаКомандной строкой называется программа текстового режима, в которой можно вводить команды для взаимодействия с операционной системой и запуска про- грамм. Также командную строку иногда называют интерфейсом командной строки (CLI, Command Line Interface), терминалом, оболочкой или консолью. Командная строка — альтернатива для графического интерфейса пользователя (GUI, Graphic User Interface), который позволяет вам взаимодействовать с компьютером, не ограничиваясь текстовым интерфейсом. Графический интерфейс представляет информацию визуально, в нем проще выполнять различные операции, чем в режи- ме командной строки. Многие пользователи считают командную строку слишком сложным инструментом и никогда не обращаются к ней. Эти опасения отчасти 46 Глава 2.Подготовка среды и командная строка обусловлены нехваткой информации об ее использовании. Если кнопка в графи- ческом интерфейсе хотя бы обозначает место, где следует щелкнуть, то пустое окно терминала не дает подсказки, что именно нужно ввести.Тем не менее существуют веские причины, для того чтобы освоить командную строку на хорошем уровне. Во-первых, для подготовки среды часто приходится использовать именно ее, а не графические окна. Во-вторых, вводить команды с клавиатуры намного быстрее, чем щелкать мышью в графических окнах. Тек- стовые команды также более понятны по сравнению с перетаскиванием значка на другой значок. Из-за этого они лучше подходят для автоматизации: вы можете объединить несколько конкретных команд в сценарии для выполнения сложных операций.Программа командной строки существует в исполняемом файле на вашем ком- пьютере. В этом контексте ее часто называют программой командной оболочки. При запуске программы командной оболочки на экране появляется терминальное окно.В системе Windows командной оболочкой является файл C:\Windows\Sys- tem32\cmd.exeВ macOS — файл /bin/bashВ Ubuntu Linux — файл /bin/bashЗа прошедшие годы программисты написали много разных командных оболочек для операционной системы Unix, включая Bourne Shell (исполняемый файл с име- нем sh) и позднее — Bourne-Again Shell (исполняемый файл с именем Bash). Linux использует Bash по умолчанию, тогда как в macOS применяется похожая оболочка Zsh (или Z Shell) в Catalina и более поздних версиях. Из-за другой истории раз- работки в Windows используется оболочка с именем Командная строка. Все эти про- граммы делают одно и то же: открывают окно терминала с текстовым интерфейсом, в котором пользователь вводит команды и запускает программы.В этом разделе описаны некоторые общие концепции и основные команды команд- ной строки. Вы, конечно, можете изучить массу разнообразных команд и стать на- стоящим профи, но на самом деле достаточно знать всего около дюжины команд. Точные их имена могут слегка видоизменяться в зависимости от операционной системы, но базовые действия остаются одними и теми же.Открытие окна терминалаЧтобы открыть окно терминала, выполните следующие действия.В Windows щелкните на кнопке Пуск, введите Командная строка и нажмите Enter Командная строка47В macOS щелкните на значке Spotlight в правом верхнем углу, введите Ter- minal и нажмите EnterВ Ubuntu Linux нажмите клавишу WIN, чтобы открыть Dash, введите Ter- minal и нажмите Enter. Также можно воспользоваться комбинацией клавиш Ctrl-Alt-TВ терминале появляется приглашение для ввода команд (по аналогии с интерак- тивной оболочкой, в которой выводится приглашение >>>). В Windows в качестве приглашения используется полный путь к текущей папке:C:\Users\Al>здесь вводятся ваши командыВ macOS приглашение состоит из имени компьютера, двоеточия и текущего ра- бочего каталога, обозначенного тильдой (); далее идет имя пользователя и знак доллара ($):Als-MacBook-Pro: al$ здесь вводятся ваши командыВ Ubuntu Linux приглашение похоже на приглашение macOS, не считая того что оно начинается с имени пользователя и амперсанда (@):al@al-VirtualBox:$ здесь вводятся ваши командыВо многих книгах и учебниках приглашение командной строки отображается в виде $ для упрощения примеров. Внешний вид приглашения можно изменять, но эта тема выходит за рамки книги.Запуск программ из командной строкиЧтобы запустить программу или команду, введите ее имя в командной стро- ке. Давайте запустим программу-калькулятор, входящую в состав стандартных программ операционной системы. Введите в командной строке следующую команду.В системе Windows введите calc.exeВ macOS введите open -a Calculator. (Строго говоря, эта команда запускает программу open, которая затем запускает программу Calculator.)В Linux введите gnome-calculatorВ Linux в именах программ и команд учитывается регистр символов, но в Windows и macOS регистр игнорируется. Это означает, что в Linux нужно ввести команду gnome-calculator, тогда как в Windows допустимо ввести Calc.exe, а в macOS — OPEN -a Calculator 48 Глава 2.Подготовка среды и командная строкаКогда вы указываете имена программ в командной строке, это равносильно за- пуску программы-калькулятора из меню Пуск, Finder или Dash. Имена программ работают как команды, потому что программы calc.exe, open и gnome-calculator на- ходятся в папках, включенных в переменные среды PATH. Эта тема более подробно рассматривается в разделе «Переменные среды и PATH», с. 60. А пока достаточно сказать, что при вводе имени программы в командной строке оболочка проверяет, существует ли программа с указанным именем в одной из папок, включенных в PATHВ Windows оболочка ищет программу в текущем рабочем каталоге (который указан в приглашении), а потом переходит к проверке папок из PATH. Чтобы приказать командной строке в macOS и Linux начать с проверки текущего рабочего каталога, необходимо ввести ./ перед именем файла.Если программа не находится в папке, включенной в PATH, у вас есть два варианта.Воспользоваться командой cd для назначения текущим рабочим каталогом папки, содержащей программу, а затем указать имя программы. Например, можно ввести следующие две команды:cd C:\Windows\System32calc.exeВведите полный путь к исполняемому файлу программы. Например, вместо calc.exe укажите C:\Windows\System32\calc.exeВ Windows, если имя программы завершается расширением .exe или .bat, расшире- ние приводить необязательно; если просто ввести имя calc, результат будет таким же, как в случае ввода calc.exe. Исполняемые программы в macOS и Linux часто не имеют специальных расширений, помечающих их как исполняемые; вместо этого для них устанавливается разрешение исполнения. Дополнительную информацию вы найдете в разделе «Запуск программ Python без командной строки», с. 65.Аргументы командной строкиАргументы командной строки представляют собой текстовые фрагменты, которые вводятся после имени команды. Как и аргументы, передаваемые при вызове функций Python, они предоставляют команде дополнительную информацию или указания. Например, при выполнении команды cd C:\Users часть C:\Users является аргументом команды cd, которая сообщает команде, какая папка должна стать текущим рабочим каталогом. Или при запуске сценария Python из окна терминала командой python yourScript.py часть yourScript.py является аргументом, который сообщает програм- ме python, в каком файле следует искать выполняемую последовательность команд.Ключи командной строки (их также называют флагами или опциями) представля- ют собой однобуквенные или короткие аргументы командной строки. В системе Командная строка49Windows ключи командной строки часто начинаются с косой черты (/); в macOS и Linux они начинаются с одного дефиса (-) или двух дефисов (--). Вы уже ис- пользовали ключ командной строки -a при выполнении команды macOS open-aCalculator. В macOS и Linux в ключах командной строки часто различается регистр символов, но в Windows регистр игнорируется. Ключи командной строки разделяются пробелами.Имена папок и файлов часто используются в качестве аргументов командной стро- ки. Если имя папки или файла содержит пробел, заключите имя в двойные кавычки, чтобы не создавать путаницу в командной строке. Например, если вы хотите сделать текущим каталогом папку с именем Vacation Photos, при вводе команды cdVacationPhotos командная строка решит, что вы передаете два аргумента, Vacation и PhotosПоэтому следует ввести команду cd"VacationPhotos":C:\Users\Al>cd "Vacation Photos"C:\Users\Al\Vacation Photos>Другой типичный аргумент многих команд — --help в macOS и Linux или /?в Windows. Они выводят информацию о команде. Например, если выполнить команду cd/? в Windows, командная оболочка сообщит, что делает команда cd, и выведет сводку аргументов командной строки этой команды:C:\Users\Al>cd /?Вывод имени либо смена текущего каталога.CHDIR [/D] [диск:][путь]CHDIR [..]CD [/D] [диск:][путь]CD [..] .. обозначает переход в родительский каталог.Команда CD диск: отображает имя текущего каталога указанного диска.Команда CD без параметров отображает имена текущих диска и каталога.Параметр /D используется для одновременной смены текущих диска и каталога.Справка сообщает, что команда Windows cd также может вводиться по имени chdir. (Большинство пользователей не станет вводить chdir, когда более корот- кая команда cd делает то же самое.) Квадратные скобки содержат необязательные аргументы. Например, описание CD[/D][диск:][путь] указывает, что с ключом /Dможно задать диск или путь.Хотя аргументы /? и --help для команд выводят информацию, объяснения часто выглядят не совсем понятно и годятся лишь для опытных пользователей. Они вряд ли помогут новичкам. Лучше обратиться к книге или веб-учебнику. 50 Глава 2.Подготовка среды и командная строкаВыполнение кода Python из командной строки с ключом -cЕсли вам понадобится небольшой объем временного кода Python, который нужно выполнить один раз, а потом выбросить, передайте ключ -c при вызове python.exe в Windows или python3 в macOS и Linux. Выполняемый код следует указать по- сле ключа -c в двойных кавычках. Например, введите следующую команду в окне терминала:C:\Users\Al>python -c "print('Hello, world')"Hello, worldКлюч -c хорошо подходит для просмотра результатов одной инструкции Python, когда вы не желаете тратить время на вход в интерактивную оболочку. Например, можно быстро отобразить вывод функции help(), а затем вернуться в командную строку:C:\Users\Al>python -c "help(len)"Help on built-in function len in module builtins:len(obj, /) Return the number of items in a container.C:\Users\Al>Выполнение программ Python из командной строкиПрограммы Python представляют собой текстовые файлы с расширением .py. Они не являются исполняемыми файлами; интерпретатор Python читает эти файлы и выполняет содержащиеся в них команды Python. В Windows исполняемый файл интерпретатора называется python.exe, а в macOS и Linux — python3 (файл python содержит интерпретатор Python версии 2). При выполнении команды python yourScript.py или python3yourScript.py будут выполнены команды Python, хра- нящиеся в файле с именем yourScript.pyЗапуск программы py.exeВ системе Windows Python устанавливает программу py.exe в папку C:\Windows. Эта программа идентична python.exe, но она получает дополнительный аргумент команд- ной строки, который позволяет запустить любую версию Python, установленную на вашем компьютере. Команду py можно выполнить из любой папки, потому что папка C:\Windows включена в переменную среды PATH. Если у вас установлено сразу несколько версий Python, команда py автоматически запустит новейшую версию из доступных на компьютере. Также можно передать аргумент командной строки -3или -2, чтобы запустить новейшую из установленных версий: Python 3 или Python 2 Командная строка51соответственно. Еще можно ввести более конкретный номер версии (например, -3.6 или -2.7), чтобы запустить конкретную версию Python. После ключа версии следует передать py.exe все те же аргументы командной строки, которые передаются python.exe. Выполните следующие команды из командной строки Windows:C:\Users\Al>py -3.6 -c "import sys;print(sys.version)"3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 03:37:03) [MSC v.1900 64 bit (AMD64)]C:\Users\Al>py -2.7Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit (AMD64)] on win32Type "help", "copyright", "credits" or "license" for more information.>>>Программа py.exe удобна в тех случаях, когда на вашем компьютере с Windows установлено несколько версий Python и вы хотите запустить конкретную версию.Выполнение команд из программы PythonФункция Python subprocess.run() из модуля subprocess может выполнять коман- ды оболочки из программ Python, а затем выводить результат выполнения команды в виде строки. Например, следующий код выполняет команду ls-al:>>> import subprocess, locale>>> procObj = subprocess.run(['ls', '-al'], stdout=subprocess.PIPE) ❶>>> outputStr = procObj.stdout.decode(locale.getdefaultlocale()[1]) ❷>>> print(outputStr)total 8drwxr-xr-x 2 al al 4096 Aug 6 21:37 .drwxr-xr-x 17 al al 4096 Aug 6 21:37 ..-rw-r--r-- 1 al al 0 Aug 5 15:59 spam.pyФункции subprocess.run() передается список ['ls','-al']❶. Он содержит имя команды ls, за которым следуют аргументы в виде отдельных строк. Обратите вни- мание: с передачей ['ls-al'] функция работать не будет. Вывод команды хранится в виде строки в outputStr❷. Электронная документация функций subprocess.run() и locale.getdefaultlocale() даст вам лучшее представление о том, как они действуют, причем с ними ваш код будет работать в любой операционной системе, где функционирует Python.Сокращение объема вводимого текста при помощи автозавершенияПрограммисты занимаются вводом команд на своих компьютерах по несколько часов в день, поэтому современные средства командной строки предоставляют возможности для сокращения объема вводимого текста. Функция автозавершения 52 Глава 2.Подготовка среды и командная строка позволяет ввести несколько первых символов имени папки или файла, а затем на- жать клавишу Tab, чтобы командная оболочка заполнила имя до конца.Например, если ввести cd c:\u и нажать Tab в Windows, текущая команда проверит, какие папки или файлы на диске C:\ начинаются с u, и автоматически завершит имя каталога c:\Users. Буква u нижнего регистра при этом автоматически преобразуется в U. (В macOS и Linux автозавершение не исправляет регистр.) Если на диске C:\ сразу несколько папок, имена которых начинаются на U, вы можете перебрать их повторными нажатиями Tab. Чтобы сократить количество совпадений, также можно ввести команду cd c:\us; тем самым набор возможных вариантов ограничивается папками и файлами, имена которых начинаются с usМногократное нажатие Tab также работает в macOS и Linux. В следующем примере пользователь ввел команду cdD и дважды нажал Tab:al@al-VirtualBox:$ cd DDesktop/ Documents/ Downloads/al@al-VirtualBox:$ cd DДвукратное нажатие Tab после ввода D заставляет оболочку показать все возможные варианты. Оболочка выдает новое приглашение с командой, указанной к настоя- щему моменту. В этот момент можно ввести, допустим, букву e и нажать Tab, чтобы оболочка завершила команду cdDesktop/Автозавершение настолько полезно, что эта функция включена во многие гра- фические интегрированные среды и текстовые редакторы. В отличие от средств командной строки эти GUI-программы обычно отображают небольшое меню под вводимым текстом, чтобы вы могли выбрать нужный вариант для автоматического завершения оставшейся части команды.Просмотр истории командСовременные командные оболочки также запоминают введенные команды в исто-рии команд. При нажатии клавиши ↑ в терминале командная строка заполняется последней введенной вами командой. Дальнейшие нажатия клавиши ↑ продолжают перебирать более ранние команды, а нажатия клавиши ↓ возвращают к более позд- ним командам. Если вы хотите отменить команду в приглашении и начать с нового приглашения, нажмите Ctrl-CВ системе Windows можно просмотреть историю команд при помощи команды doskey/history. (Странное название программы doskey восходит к MS-DOS — операционной системе Microsoft, предшественнику Windows.) В macOS и Linux для истории команд предназначена команда history Командная строка53Часто используемые командыВ этом разделе приведен короткий список команд, которые чаще других исполь- зуются в режиме командной строки. Команд и аргументов гораздо больше, но вы можете рассматривать этот список как некий минимум, необходимый для работы с командной строкой.Аргументы командной строки для команд этого раздела заключены в квадратные скобки. Например, cd[папка] означает, что вы вводите команду cd, за которой следует имя новой папки.Определение папок и файлов с использованием универсальных символовМногим командам в аргументах командной строки должны передаваться имена папок и файлов. Часто такие команды могут получать шаблоны с универсальны- ми символами * и ?, которые определяют набор подходящих файлов. Символ *означает любое количество символов, тогда как символ ? — один произвольный символ. Выражения с универсальными символами * и ? называются глобальными шаблонами.Глобальные шаблоны позволяют определять группы файлов. Например, команда dir или ls используется для вывода всех файлов и папок в текущем рабочем ката- логе. Но если вы хотите просмотреть только файлы Python, команда dir*.py или ls*.py выведет только файлы с расширением .py. Глобальный шаблон *.py означает «любая группа символов, за которой следует .py»:C:\Users\Al>dir *.py Volume in drive C is Windows Volume Serial Number is DFF3-8658 Directory of C:\Users\Al03/24/2019 10:45 PM 8,399 conwaygameoflife.py03/24/2019 11:00 PM 7,896 test1.py10/29/2019 08:18 PM 21,254 wizcoin.py 3 File(s) 37,549 bytes 0 Dir(s) 506,300,776,448 bytes freeГлобальный шаблон records201?.txt означает «символы records201, за которыми следует один произвольный символ, а потом .txt». Шаблон выберет файлы от records2010.txt до records2019.txt (а также имена файлов вида records201X.txt). Гло- бальный шаблон records20??.txt может включать два любых символа — например, records2021.txt или records20AB.txt 54 Глава 2.Подготовка среды и командная строкаИзменение текущего рабочего каталога командой cdВыполнение команды cd[папка] заменяет текущий рабочий каталог командной оболочки указанной папкой:C:\Users\Al>cd DesktopC:\Users\Al\Desktop>Оболочка выводит текущий рабочий каталог как часть приглашения командной строки, а все папки и файлы, указанные в командах, интерпретируются относи- тельно этого каталога.Если имя папки содержит пробелы, заключите его в двойные кавычки. Чтобы выбрать в качестве текущего рабочего каталога домашнюю папку пользователя, введите команду cd в macOS и Linux или команду cd%USERPROFILE% в Windows.В системе Windows, если вы захотите изменить текущий диск, сначала необходимо указать имя диска в отдельной команде:C:\Users\Al>d:D:\>cd BackupFilesD:\BackupFiles>Для перехода к родительскому каталогу текущего рабочего каталога используйте в качестве имени папки C:\Users\Al>cd ..C:\Users>Вывод содержимого папки командами dir и lsВ системе Windows команда dir выводит список папок и файлов в текущем рабо- чем каталоге. Команда ls делает то же самое в macOS и Linux. Содержимое другой папки выводится командой dir[другая папка] или ls[другая папка]Ключи -l и -a передают полезные аргументы для команды ls. По умолчанию ls выводит только имена папок и файлов. Чтобы вывести подробную информацию с размером файла, разрешениями, временными метками последнего изменения и другой информацией, используйте ключ -l. По существующим соглашениям операционные системы macOS и Linux считают, что файлы, имена которых на- чинаются с точки, являются конфигурационными файлами, и скрывают их от нормальных команд. Чтобы команда ls выводила все файлы, включая скрытые, используйте ключ -a. Чтобы в результатах подробная информация объединялась Командная строка55с выводом всех файлов, объедините эти ключи: ls-al. Вот пример окна терминала для macOS или Linux:al@ubuntu:$ lsDesktop Downloads mu_code Pictures snap VideosDocuments examples.desktop Music Public Templates al@ubuntu:$ ls -altotal 112drwxr-xr-x 18 al al 4096 Aug 4 18:47 .drwxr-xr-x 3 root root 4096 Jun 17 18:11 ..-rw------- 1 al al 5157 Aug 2 20:43 .bash_history-rw-r--r-- 1 al al 220 Jun 17 18:11 .bash_logout-rw-r--r-- 1 al al 3771 Jun 17 18:11 .bashrc drwx------ 17 al al 4096 Jul 30 10:16 .cache drwx------ 14 al al 4096 Jun 19 15:04 .config drwxr-xr-x 2 al al 4096 Aug 4 17:33 DesktopВ системе Windows аналогом ls-al является команда dir. Пример окна терминала для Windows:C:\Users\Al>dir Volume in drive C is Windows Volume Serial Number is DFF3-8658 Directory of C:\Users\Al06/12/2019 05:18 PM .06/12/2019 05:18 PM ..12/04/2018 07:16 PM .android--snip--08/31/2018 12:47 AM 14,618 projectz.ipynb10/29/2014 04:34 PM 121,474 foo.jpgВывод содержимого вложенных папок командами dir /s и findВ системе Windows команда dir/s выводит содержимое текущего рабочего каталога и его подкаталогов. Например, следующая команда выводит все файлы .py в папке C:\ github\ezgmail, а также во всех ее вложенных папках:C:\github\ezgmail>dir /s *.py Volume in drive C is Windows Volume Serial Number is DEE0-8982 Directory of C:\github\ezgmail06/17/2019 06:58 AM 1,396 setup.py 1 File(s) 1,396 bytes Directory of C:\github\ezgmail\docs 56 Глава 2.Подготовка среды и командная строка12/07/2018 09:43 PM 5,504 conf.py 1 File(s) 5,504 bytes Directory of C:\github\ezgmail\src\ezgmail06/23/2019 07:45 PM 23,565 __init__.py12/07/2018 09:43 PM 56 __main__.py 2 File(s) 23,621 bytes Total Files Listed: 4 File(s) 30,521 bytes 0 Dir(s) 505,407,283,200 bytes freeКоманда find-name делает то же самое в macOS и Linux: al@ubuntu:/Desktop$ find . -name "*.py"./someSubFolder/eggs.py./someSubFolder/bacon.py./spam.pyСимвол приказывает find начать поиск в текущем рабочем каталоге. С ключом -name команда find ищет папки и файлы по имени. Фрагмент "*.py" приказывает find искать папки и файлы с именами, соответствующими шаблону *.py. Следует заметить, что команда find требует, чтобы аргумент после -name был заключен в двойные кавычки.Копирование файлов и папок командами copy и cpЧтобы создать дубликат файла или папки в другом каталоге, выполните команду copy[исходный файл или папка][приемная папка] или cp[исходный файл или папка][приемная папка]. Пример окна терминала Linux:al@ubuntu:/someFolder$ lshello.py someSubFolder al@ubuntu:/someFolder$ cp hello.py someSubFolderal@ubuntu:/someFolder$ cd someSubFolderal@ubuntu:/someFolder/someSubFolder$ lshello.pyПеремещение файлов и папок командами move и mvВ Windows перемещение исходного файла или папки в другую папку выполняется командой move[исходный файл или папка][приемная папка]. Команда mv[исходный файл или папка][приемная папка] делает то же самое в macOS и Linux. Пример окна терминала Linux:al@ubuntu:/someFolder$ lshello.py someSubFolder Командная строка57al@ubuntu:/someFolder$ mv hello.py someSubFolderal@ubuntu:/someFolder$ lssomeSubFolder al@ubuntu:/someFolder$ cd someSubFolder/al@ubuntu:/someFolder/someSubFolder$ lshello.pyФайл hello.py переместился из /someFolder в /someFolder/someSubFolder, в исходной папке он отсутствует.КОРОТКИЕ ИМЕНА КОМАНДКогда я только начал изучать операционную систему Linux, меня удивило, что известная мне команда copy в Linux называлась cp. Имя copy было намного более понятным, чем cp. Стоило ли создавать лаконичное, малопонятное имя ради экономии всего двух вводимых символов?Со временем у меня появилось больше опыта работы с командной стро- кой, и я понял, что ответ на этот вопрос — уверенное «да». Мы читаем ис- ходный код чаще, чем пишем его, так что развернутые имена переменных и функций приносят пользу. Но мы вводим команды в командной строке чаще, чем читаем их, поэтому в данном случае справедливо обратное: ко- роткие имена упрощают работу с командной строкой и снижают нагрузку на запястья.Переименование файлов и папок командами ren и mvКоманда ren[файл или папка][новое имя] переименовывает файл или папку в Windows, а команда mv[файл или папка][новое имя] делает то же самое в macOS и Linux. Следует заметить, что команда mv в macOS и Linux может использоваться для перемещения и переименования файлов. Если указать имя существующей папки во втором аргументе, команда mv перемещает туда файл или папку. Если задать имя, которое не соответствует существующему файлу или папке, команда mv пере- именовывает файл или папку. Пример в окне терминала Linux:al@ubuntu:/someFolder$ lshello.py someSubFolder al@ubuntu:/someFolder$ mv hello.py goodbye.pyal@ubuntu:/someFolder$ lsgoodbye.py someSubFolderТеперь файл hello.py существует под именем goodbye.py 58 Глава 2.Подготовка среды и командная строкаУдаление файлов и папок командами del и rmЧтобы удалить файл или папку в Windows, выполните команду del[файл или пап-ка]. В macOS и Linux для этого используется команда rm[файл] (rm — сокращение от remove).Две команды удаления несколько отличаются друг от друга. В Windows выпол- нение del для папки удаляет все содержащиеся в ней файлы, но не вложенные папки. Команда del также не удаляет исходную папку; для этого необходимо воспользоваться командой rd или rmdir (см. «Удаление папок командами rd и rmdir», с. 59). Кроме того, выполнение команды del[папка] не приведет к уда- лению каких-либо файлов в папках, вложенных в исходную. Чтобы удалить их, выполните команду del/s/q[папка]. Ключ /s выполняет команду del вместе с вложенными папками, а ключ /q фактически означает «делай, как я сказал, и не спрашивай подтверждения».На рис. 2.4 продемонстрированы различия между командами.Рис. 2.4. Файлы в этих папках удаляются при выполнении команды del delicious (слева) или del /s /q delicious (справа)В macOS и Linux команда rm не может использоваться для удаления папок. Впро- чем, вы можете выполнить команду rm-r[папка] для удаления папки и всего ее содержимого. В системе Windows то же самое делает команда rd/s/q[папка](рис. 2.5). Командная строка59Рис. 2.5. Файлы в этих папках удаляются при выполнении команды rd /s /q delicious или rm -r deliciousСоздание папок командами md и mkdirКоманда md[новая папка] создает новую пустую папку в Windows, а команда mkdir[новая папка] делает то же самое в macOS и Linux. Команда mkdir также работает в Windows, но md вводить быстрее.Пример в окне терминала Linux:al@ubuntu:/Desktop$ mkdir yourScriptsal@ubuntu:/Desktop$ cd yourScriptsal@ubuntu:/Desktop/yourScripts$ ls ❶al@ubuntu:/Desktop/yourScripts$Обратите внимание: только что созданная папка yourScripts пуста; когда мы вы- полняем команду ls для вывода ее содержимого, не выводится ничего ❶Удаление папок командами rd и rmdirКоманда rd[исходная папка] удаляет исходную папку в Windows, а команда rmdir[исходная папка] — в macOS и Linux. Как и в случае с mkdir, команда rmdir также работает в Windows, но команду rd вводить быстрее. Чтобы папку можно было удалить, она должна быть пустой. 60 Глава 2.Подготовка среды и командная строкаПример в окне терминала Linux:al@ubuntu:/Desktop$ mkdir yourScriptsal@ubuntu:/Desktop$ lsyourScripts al@ubuntu:/Desktop$ rmdir yourScriptsal@ubuntu:/Desktop$ lsal@ubuntu:/Desktop$Этот пример сначала создает пустую папку yourScripts, а затем удаляет ее.Чтобы удалить непустую папку (вместе со всеми папками и файлами в ней), вы- полните команду rd/s/q[исходная папка] в Windows или команду rm-rf[исходная папка] в macOS и Linux.Поиск программ командами where и whichКоманда where[программа] в Windows или which[программа] в macOS и Linux сообщает точное местонахождение программы. Когда вы вводите команду в команд- ной строке, осуществляется поиск программы в папках, входящих в переменную среды PATH (правда, Windows начинает с текущего рабочего каталога).Эти команды сообщают, какая исполняемая программа Python запускается при вводе команды python в командной оболочке. Если у вас установлено сразу не- сколько версий Python, на компьютере могут существовать разные исполняемые программы с одинаковыми именами. Выбор запускаемой программы зависит от порядка следования папок в переменной среды PATH, а команды where и which вы- водят ее полное имя:C:\Users\Al>where pythonC:\Users\Al\AppData\Local\Programs\Python\Python38\python.exeВ этом примере имя папки указывает, что версия Python, запускаемая из оболочки, находится в папке C:\Users\Al\AppData\Local\Programs\Python\Python38\Очистка терминала командами cls и clearКоманда cls в Windows или clear в macOS и Linux стирает весь текст в окне тер- минала. Например, эта возможность пригодится, если вы хотите начать с пустого окна терминала.Переменные среды и PATHУ всех выполняемых процессов программы, на каком бы языке она ни была на- писана, имеется набор переменных среды, где хранятся строки. Переменные среды Переменные среды и PATH61часто содержат настройки системного уровня, которые могут оказаться полезными в каждой программе. Например, переменная среды TEMP содержит путь к катало- гу, где любая программа может хранить временные файлы. Когда операционная система запускает программу (например, командную строку), созданный процесс получает собственную копию переменных среды операционной системы. Перемен- ные среды процесса могут изменяться независимо от набора значений переменных среды операционной системы. Но эти изменения действуют только на этот процесс, но не на операционную систему или другие процессы.Я рассматриваю переменные среды в этой главе, потому что одна из этих перемен- ных — PATH — пригодится вам при запуске программ из командной строки.Просмотр переменных средыЧтобы просмотреть список переменных среды для окна терминала, выполните команду set (в Windows) или env (в macOS или Linux) в командной строке:C:\Users\Al>setALLUSERSPROFILE=C:\ProgramDataAPPDATA=C:\Users\Al\AppData\RoamingCommonProgramFiles=C:\Program Files\Common Files--snip--USERPROFILE=C:\Users\AlVBOX_MSI_INSTALL_PATH=C:\Program Files\Oracle\VirtualBox\windir=C:\WINDOWSСлева от знака равенства (=) указывается имя переменной среды, а справа — стро- ковое значение. Каждый процесс содержит отдельный набор переменных среды, так что разные командные строки могут содержать разные значения своих пере- менных среды.Для просмотра значения одной переменной среды также можно воспользоваться командой echo. Команда echo%HOMEPATH% в Windows или echo$HOME в macOS или Linux выводит значение переменных среды HOMEPATH или HOME соответственно, которые содержат домашнюю папку текущего пользователя. В Windows результат выглядит так:C:\Users\Al>echo %HOMEPATH%\Users\AlРезультат в macOS или Linux:al@al-VirtualBox:$ echo $HOME/home/alЕсли этот процесс создает другой процесс (например, как командная строка за- пускает интерпретатор Python), дочерний процесс получает собственную копию 62 Глава 2.Подготовка среды и командная строка переменных среды родительского процесса. После этого дочерний процесс может изменить значения своих переменных среды, и наоборот.Набор переменных среды операционной системы можно рассматривать как эта- лонную копию, с которой процесс копирует свои переменные среды. Переменные среды операционной системы изменяются реже переменных программы Python. Более того, многие пользователи вообще не изменяют значения переменных среды.Переменная среды PATHКогда вы вводите команду (например, python в Windows или python3 в macOS и Linux), терминал ищет программу с таким именем в текущей папке. Если програм- ма там не найдена, то проверяются папки, перечисленные в переменной среды PATHНапример, на моем компьютере с Windows файл программы python.exe хранится в папке C:\Users\Al\AppData\Local\Programs\Python\Python38. Чтобы его запустить, мне придется ввести полное имя C:\Users\Al\AppData\Local\Programs\Python\Python38\python.exe или сначала перейти в эту папку, а затем ввести python.exe. Длинное имя приходится набирать долго, поэтому я включил эту папку в переменную среды PATHТеперь при вводе python.exe командная строка ищет программу с заданным именем в папках, перечисленных в PATH, а мне не приходится вводить путь полностью.Так как переменная среды может содержать только одно строковое значение, для включения нескольких имен папок в переменную среды PATH следует использовать специальный формат. В Windows имена папок разделяются символами ; (точка с запятой). Для просмотра текущего содержимого PATH введите команду path:C:\Users\Al>pathC:\Path;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Users\Al\AppData\Local\Microsoft\WindowsAppsВ macOS и Linux имена точек разделяются двоеточиями:al@ubuntu:$ echo $PATH/home/al/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binПорядок имен папок важен. Если в папках C:\WINDOWS\system32 и C:\WINDOWSнаходятся два разных файла с именем someProgram.exe, то при вводе команды someProgram.exe будет выполнена программа из C:\WINDOWS\system32, потому что эта папка первой встречается в переменной среды PATHЕсли введенная вами программа или команда не существует ни в текущем рабо- чем каталоге, ни в одном из подкаталогов в PATH, командная строка сообщает об ошибке — например, command not found or not recognized as an internal or external Переменные среды и PATH63command (Команда не найдена или не опознана как внутренняя или внешняя коман- да). Если вы не допустили опечатку, проверьте, в какой папке находится программа и включена ли эта папка в переменную среды PATHИзменение переменной среды PATH в командной строкеПеременную среды PATH текущего окна терминала можно изменить, включив в нее дополнительные папки. Процессы добавления папок в PATH несколько различают- ся в Windows и macOS/Linux. В Windows для добавления новой папки в текущее значение PATH можно воспользоваться командой path:C:\Users\Al>path C:\newFolder;%PATH% ❶C:\Users\Al>path ❷C:\newFolder;C:\Path;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Users\Al\AppData\Local\Microsoft\WindowsAppsЧасть %PATH%❶ заменяется текущим значением переменной среды PATH, так что фактически вы добавляете новую папку и точку с запятой в начало существующего значения PATH. Снова выполните команду path, чтобы просмотреть новое значение PATH❷В macOS и Linux для назначения переменной среды PATH можно использовать синтаксис, сходный с синтаксисом команды присваивания в Python:al@al-VirtualBox:$ PATH=/newFolder:$PATH ❶al@al-VirtualBox:$ echo $PATH ❷ /newFolder:/home/al/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/ bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binЧасть $PATH❶ заменяется текущим значением переменной среды PATH, так что фактически вы добавляете новую папку и точку с запятой в начало существующе- го значения PATH. Снова выполните команду echo$path, чтобы просмотреть новое значение PATH❷Но оба описанных способа добавления папок в PATH применяются только к теку- щему окну терминала и ко всем программам, которые запускаются из него после добавления. Если вы откроете новое окно терминала, в нем этих изменений уже не будет. Чтобы папка была включена в PATH постоянно, необходимо изменить эталонный набор переменных среды операционной системы.Постоянное включение папок в PATH в WindowsВ Windows существуют два набора переменных среды: системные переменные среды (которые распространяются на всех пользователей) и переменные среды 64 Глава 2.Подготовка среды и командная строкапользователя (которые переопределяют значения системных переменных среды, но распространяются только на текущего пользователя). Чтобы отредактировать их, откройте меню Пуск и введите Edit environment variables for your account (От- редактируйте переменные среды для своей учетной записи). Открывается окно Переменные среды (рис. 2.6).Рис. 2.6. Окно Переменные среды в WindowsВыберите Path в списке переменных среды пользователя (не в списке системных переменных!), щелкните на кнопке Изменить и введите новое имя папки в открыв- шемся текстовом поле (не забудьте о разделителе ;). Щелкните на кнопке OKЭто не самый удобный интерфейс, и, если вам приходится часто редактировать переменные среды в Windows, я рекомендую установить бесплатную программу Rapid Environment Editor (https://www.rapidee.com/). Учтите, что после установки необходимо запустить программу с правами администратора, чтобы редактировать системные переменные среды. Щелкните на кнопке Пуск, введите Rapid Environment Запуск программ Python без командной строки65Editor, щелкните правой кнопкой мыши на значке программы и выберите команду Запуск от имени администратораВ командной строке для постоянного изменения системной переменной PATH можно воспользоваться командой setx:C:\Users\Al>setx /M PATH "C:\newFolder;%PATH%"Для выполнения команды setx необходимо запустить командную строку с правами администратора.Постоянное включение папок в PATH в macOS и LinuxЧтобы добавить папки в переменные среды PATH для всех окон терминала в macOS и Linux, необходимо изменить текстовый файл .bashrc в домашней папке. Добавьте в него следующую строку:export PATH=/newFolder:$PATHЭта строка изменяет PATH для всех будущих окон терминала. В macOS Catalina и в более поздних версиях оболочка по умолчанию сменилась с Bash на Z Shell, в этом случае необходимо изменить файл .zshrc в домашней папке.Запуск программ Python без командной строкиВероятно, вы уже умеете запускать программы из оболочек, предоставляемых ва- шей операционной системой. В Windows существует меню Пуск, в macOS — Finder и Dock, а в Ubuntu Linux — Dash. Программы добавляют себя в эти оболочки при установке. Также программу можно запустить двойным щелчком на значке про- граммы в файловом менеджере (таком, как Проводник в Windows, Finder в macOS или Files в Ubuntu Linux).Но для программ Python эти способы не годятся. Часто при двойном щелчке на файле .py программа Python не запускается, а открывается в редакторе или IDE. А попытавшись запустить Python напрямую, вы просто откроете интерактивную оболочку Python. Чтобы запустить программу Python, пользователи чаще всего открывают ее в IDE и выбирают команду меню Run или же выполняют ее в команд- ной строке. Оба способа не слишком удобны, если вам нужно просто запустить программу Python.Но вы можете настроить программы Python так, чтобы они запускались из обо- лочки вашей операционной системы (как и другие приложения, установленные в системе). Далее я подробно расскажу, как сделать это в конкретной операци- онной системе. 66 Глава 2.Подготовка среды и командная строкаЗапуск программ Python в WindowsВ системе Windows программы Python можно запустить еще несколькими спо- собами. Не стоит открывать окно терминала, можно открыть диалоговое окно запуска программы клавишами WIN-R, а затем ввести команду pyC:\path\to\yourScript.py, как показано на рис. 2.7. Программа py.exe находится в папке C:\Windows, которая уже включена в переменную окружения PATH, а указывать рас- ширение .exe при запуске программ необязательно.Рис. 2.7. Диалоговое окно запуска программы в WindowsТем не менее этот способ требует ввода полного пути к вашему сценарию. Кроме того, окно терминала, в котором отображается вывод программы, автоматически закрывается при ее завершении, и вы можете упустить часть вывода.Проблемы удается решить созданием пакетного сценария — небольшого текстового файла с расширением .bat, который может выполнять сразу несколько терминаль- ных команд (по аналогии со сценариями командной оболочки в macOS и Linux). Для создания этих файлов можно воспользоваться текстовым редактором (например, Блокнотом). Создайте новый текстовый файл, содержащий следующие две строки:@py.exe C:\path\to\yourScript.py %*@pauseЗамените этот путь абсолютным путем к вашей программе и сохраните файл с расширением .bat (например, yourScript.bat). Знак @ в начале каждой команды предотвращает ее вывод в окне терминала, а %* передает все аргументы команд- ной строки, введенные после имени пакетного файла, сценарию Python. В свою очередь, сценарий Python читает аргументы командной строки из списка sys.argvПакетный файл избавляет вас от необходимости вводить полный абсолютный путь Запуск программ Python без командной строки67к программе Python каждый раз, когда вы захотите ее выполнить. Команда @pause добавляет сообщение Press any key to continue... в конец сценария Python, чтобы окно программы не исчезало с экрана слишком быстро.Я рекомендую разместить все пакетные файлы и файлы .py в одной папке, уже вклю- ченной в переменную среды PATH — например, в домашнюю папку C:\Users\<ИМЯ_ПОЛЬЗОВАТЕЛЯ>. После создания пакетного файла для запуска сценария Python вам достаточно нажать WIN-R, ввести имя пакетного файла (указывать расширение .bat не обязательно) и нажать ENTER.Запуск программ Python в macOSВ macOS можно создать сценарий командной оболочки для запуска сценариев Python; для этого следует создать текстовый файл с расширением .command. Соз- дайте такой файл в текстовом редакторе (например, TextEdit) и введите следующие команды:#!/usr/bin/env bash python3 /path\to\yourScript.pyСохраните этот файл в домашней папке. В окне терминала сделайте этот сценарий исполняемым при помощи команды chmod u+x вашСценарий.command. Теперь вы сможете щелкнуть на значке Spotlight (или нажать COMMAND-ПРОБЕЛ) или ввести имя сценария оболочки, чтобы выполнить его. А сценарий оболочки за- пустит ваш сценарий Python.Запуск программ Python в Ubuntu LinuxВ Ubuntu Linux не существует быстрого способа запуска сценариев Python, похожего на те, которые существуют в Windows и macOS, но все же облегчить себе жизнь можно. Сначала убедитесь в том, что файл .py хранится в домашней папке. Затем включите следующую строку, которая должна быть первой строкой файла .py:#!/usr/bin/env python3Строка сообщает Ubuntu, что для запуска этого файла должна использоваться про- грамма python3. После этого выполните команду chmod в терминале, чтобы добавить для этого файла разрешение исполнения:al@al-VirtualBox:$ chmod u+x yourScript.pyТеперь каждый раз, когда вы захотите быстро запустить сценарий Python, откройте новое окно терминала клавишами Ctrl-Alt-T. В терминале будет выбрана домашняя 68 Глава 2.Подготовка среды и командная строка папка, так что вы можете просто ввести ./yourScript.py для запуска этого сценария. Префикс ./ необходим, потому что он сообщает Ubuntu, что сценарий yourScript.py находится в текущем рабочем каталоге (домашней папке в данном случае).ИтогиПодготовка среды подразумевает действия, необходимые для перевода вашего компьютера в режим, в котором можно легко запускать программы. Для этого необходимо знать ряд низкоуровневых концепций, относящихся к работе вашего компьютера, таких как файловая система, пути к файлам, процессы, командная строка и переменные среды.Файловая система определяет способ упорядоченного хранения файлов на вашем компьютере. Положение файла обозначается либо полным, абсолютным путем, либо путем, заданным относительно текущего рабочего каталога. Перемещение по файловой системе можно осуществлять из командной строки. Командную строку также называют терминалом, оболочкой или консолью, но все эти слова обознача- ют одно и то же: текстовую программу, в которой можно вводить команды. И хотя командная строка и имена часто используемых команд несколько различаются в Windows и macOS/Linux, по сути они решают одни и те же задачи.Когда вы вводите команду или имя программы, командная строка проверяет папки, включенные в переменную среды PATH, в поисках имени. Это важно знать, чтобы по- нять причину ошибок command not found (Команда не найдена). Последовательность действий при добавлении новых папок в переменную среды PATH также несколько отличается в Windows и macOS/Linux.На освоение командной строки может потребоваться время, потому что вам при- дется изучать множество команд и аргументов командной строки. Не огорчайтесь, если вы тратите много времени для поиска информации в интернете; опытные разработчики занимаются этим каждый день. 1   2   3   4   5   6   7   8   9   ...   40

Black: бескомпромиссная система форматирования кодаBlack автоматически форматирует код в ваших файлах .py. Хотя вы должны по- нимать правила форматирования, рассмотренные в этой главе, Black выполняет всю работу по стилевому оформлению за вас. Если вы участвуете в совместном проекте, то можете моментально урегулировать многие споры относительно фор- матирования кода — просто поручите решение Black.У нас нет возможности изменить многие правила, которым следует Black, поэтому Black описывается как «бескомпромиссная система форматирования кода». Соб- ственно, его название (Black — черный) происходит от знаменитого высказывания Генри Форда относительно цветов выпускаемых автомобилей: «Цвет автомобиля может быть любым, при условии что он черный». 80 Глава 3.Форматирование кода при помощи BlackЯ описал конкретные стилевые правила, используемые Black; полное руководство по стилю для Black вы найдете по адресу https://black.readthedocs.io/en/stable/the_black_code_style.html.Установка BlackУстановите Black при помощи программы pip, входящей в комплект поставки Python. В Windows для этого следует открыть окно командной строки и ввести команду:C:\Users\Al\>python -m pip install --user blackВ macOS и Linux откройте окно терминала и введите команду python3 вместо python (это следует делать во всех фрагментах кода в этой книге, где используется команда python):Als-MacBook-Pro: al$ python3 -m pip install --user blackКлюч -m приказывает Python запустить модуль pip как приложение (некоторые модули Python имеют соответствующую настройку). Чтобы убедиться в том, что установка прошла успешно, выполните команду python-m black. На экране должно появиться сообщение «No paths given.Nothing to do» вместо «No module named black».Запуск Black из командной строкиBlack можно запустить для любого файла Python из командной строки или окна терминала. Кроме того, IDE или редактор кода позволяет запустить Black в фоновом режиме. Инструкции о том, как обеспечить работу Black с Jupyter Notebook, Visual Studio Code, PyCharm и другими редакторами, вы найдете на домашней странице Black по адресу https://github.com/psf/black/.Допустим, вы хотите отформатировать файл с именем yourScript.py автоматически. В командной строке Windows выполните следующую команду (в macOS и Linux используйте команду python3 вместо python):C:\Users\Al>python -m black yourScript.pyПосле выполнения этой команды содержимое yourScript.py будет отформатировано в соответствии с руководством по стилю Black.Переменная среды PATH может быть уже настроена для прямого запуска Black. В этом случае для форматирования yourScript.py достаточно ввести следующую команду:C:\Users\Al>black yourScript.py Black: бескомпромиссная система форматирования кода81Чтобы выполнить Black для каждого файла .py в папке, укажите в команде папку вместо отдельного файла. Следующий пример Windows форматирует все файлы в папке C:\yourPythonFiles, включая ее вложенные папки:C:\Users\Al>python -m black C:\yourPythonFilesПередача папки пригодится в том случае, если ваш проект содержит несколько файлов Python и вы не хотите вводить отдельную команду для каждого файла.Хотя Black довольно строго относится к форматированию кода, в следующих трех подразделах описаны некоторые параметры, которые вы можете настроить. Что- бы просмотреть полный набор ключей Black, выполните команду python-m black--helpРегулировка длины строки BlackСтандартная строка кода Python состоит из 80 символов. История 80-символьных строк уходит корнями в 1920-е годы, когда компания IBM представила перфокар- ты на 80 столбцов и 12 строк; 80-символьный стандарт стал использоваться для принтеров, мониторов и окон командной строки, появившихся через несколько десятилетий.Но в XXI веке экраны с высоким разрешением позволяют выводить текст длиной более 80 символов. С большей длиной строки вам не придется выполнять верти- кальную прокрутку, чтобы просмотреть файл. С другой стороны, короткие строки не перегружены, а вы можете сравнить два исходных файла, разместив их на экране рядом друг с другом, без горизонтальной прокрутки.Black по умолчанию использует строку длиной 88 символов. Выбор объясняется не очень убедительно: это на 10% больше стандартной 80-символьной строки. Лично я предпочитаю 120 символов. Чтобы приказать Black форматировать код с 120-символьной (например) строкой, используйте ключ командной строки -l120 (буква l в нижнем регистре, не цифра 1). В Windows команда выглядит примерно так:C:\Users\Al>python -m black -l 120 yourScript.pyКакую бы длину строки вы ни выбрали для своего проекта, для всех файлов .py в проекте должно использоваться одинаковое значение.Отключение настройки двойных кавычек для BlackBlack автоматически изменяет все строковые литералы в вашем коде так, чтобы вместо одинарных кавычек они заключались в двойные кавычки, если только строка 82 Глава 3.Форматирование кода при помощи Black не содержит внутренние символы двойных кавычек; в этом случае используются одинарные кавычки. Допустим, файл yourScript.py содержит следующий фрагмент:a = 'Hello' b = "Hello" c = 'Al\'s cat, Zophie.'d = 'Zophie said, "Meow"' e = "Zophie said, \"Meow\"" f = '''Hello''' После применения Black файл yourScript.py будет отформатирован следующим образом:a = "Hello" ❶b = "Hello"c = "Al's cat, Zophie." d = 'Zophie said, "Meow"' ❷e = 'Zophie said, "Meow"' f = """Hello""" ❸В результате применения Black с настройками двойных кавычек код Python будет похож на код, написанный на других языках программирования, где строковые литералы часто заключаются в двойные кавычки. Обратите внимание: строки переменных a, b и c используют двойные кавычки. Строка переменной d сохраняет исходные одинарные кавычки, чтобы избежать исчезновения двойных кавычек в строке ❷. Обратите внимание: Black также использует двойные кавычки для многострочных текстов Python, заключенных в тройные кавычки ❸Но если вы хотите, чтобы ваши строковые литералы остались в том виде, в котором они были написаны, а тип используемых кавычек не изменялся, передайте Black ключ командной строки -S (обратите внимание: буква S в верхнем регистре). На- пример, при применении Black к исходному файлу yourScript.py в Windows будет получен следующий результат:C:\Users\Al>python –m black -S yourScript.pyAll done!1 file left unchanged.Также можно использовать ключи длины строки -l и -S в одной команде:C:\Users\Al>python –m black –l 120 -S yourScript.pyПредварительный просмотр изменений, вносимых BlackХотя Black не переименовывает переменные и не изменяет работу программы, возможно, вам не понравятся стилевые изменения, предложенные Black. Если вы хотите оставить исходное форматирование, то либо используйте механизм контроля Black: бескомпромиссная система форматирования кода83версий для исходного кода, либо создавайте резервные копии самостоятельно. Также можно просмотреть изменения, которые внесет Black, без фактического из- менения файлов; для этого следует запустить Black с ключом командной строки --diff. В Windows это выглядит так:C:\Users\Al>python -m black --diff yourScript.pyЭта команда выводит различия в формате diff, который часто используется си- стемами контроля версий, но удобочитаем для людей. Например, если yourScript.py содержит строку weights=[42.0,3.1415,2.718], при выполнении с ключом --diff будет выведен следующий результат:C:\Users\Al\>python -m black --diff yourScript.py--- yourScript.py 2020-12-07 02:04:23.141417 +0000+++ yourScript.py 2020-12-07 02:08:13.893578 +0000@@ -1 +1,2 @@-weights=[42.0,3.1415,2.718]+weights = [42.0, 3.1415, 2.718]Знак «минус» означает, что Black удалит строку weights=[42.0,3.1415,2.718] и за- менит ее строкой, которая выводится с префиксом «плюс»: weights=[42.0,3.1415,2.718]. Учтите, что после того, как вы запустите Black для изменения файлов с ис- ходным кодом, отменить внесенные изменения уже не удастся. Необходимо либо создать резервные копии вашего исходного кода, либо воспользоваться системой контроля версий (такой как Git) перед запуском Black.Отключение Black для отдельных частей кодаКаким бы замечательным ни был инструмент Black, возможно, вы не захотите, чтобы он форматировал некоторые части вашего кода. Например, я предпочитаю самостоятельно расставлять отступы в разделах, где я выравниваю несколько вза- имосвязанных команд присваивания, как в следующем примере:# Константы для разных интервалов времени:SECONDS_PER_MINUTE = 60SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTESECONDS_PER_DAY = 24 * SECONDS_PER_HOURSECONDS_PER_WEEK = 7 * SECONDS_PER_DAYBlack удалит дополнительные пробелы перед оператором присваивания =, отчего они, по моему мнению, будут хуже читаться:# Константы для разных интервалов времени:SECONDS_PER_MINUTE = 60SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTESECONDS_PER_DAY = 24 * SECONDS_PER_HOURSECONDS_PER_WEEK = 7 * SECONDS_PER_DAY 84 Глава 3.Форматирование кода при помощи BlackДобавив комментарии #fmt:off и #fmt:on, можно запретить Black форматирование строк в этом фрагменте, а затем продолжить его:# Константы для разных промежутков времени:# fmt: offSECONDS_PER_MINUTE = 60SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTESECONDS_PER_DAY = 24 * SECONDS_PER_HOURSECONDS_PER_WEEK = 7 * SECONDS_PER_DAY# fmt: onТеперь запуск Black для этого файла не приведет к изменению отступов (и лю- бого другого форматирования) в коде между этими двумя комментариями.ИтогиХотя хорошее форматирование люди часто не замечают, плохое форматирование вызывает раздражение. Стиль — понятие субъективное, но у разработчиков обычно вырабатываются общие представления о том, что считать хорошим или плохим форматированием; при этом, конечно же, остается место для личных предпочтений.Синтаксис Python достаточно гибок в том, что касается стиля. Если вы пишете код, который никто никогда не увидит, вы можете писать его так, как вам нравится. Но разработкой ПО чаще всего занимаются в команде. Работаете ли вы с другими программистами над проектом или просто хотите попросить более опытных коллег оценить вашу работу, важно отформатировать код в соответствии с общепринятыми руководствами по стилю.Форматирование кода в редакторе — монотонная работа, которую можно автома- тизировать таким инструментом, как Black. В этой главе рассмотрены некоторые правила, которым следует Black, для того чтобы сделать ваш код более удобочитае- мым: вертикальные и горизонтальные отступы, чтобы код не был слишком плотным и хорошо читался, и ограничение длины каждой строки. Black следит за соблюдением этих правил за вас, что предотвращает возможные стилевые разногласия с коллегами.Однако стиль программирования не сводится к применению отступов и выбору между одинарными и двойными кавычками. Например, использование содержа- тельных имен переменных также является критическим фактором удобочитаемости кода. И хотя такие автоматизированные инструменты, как Black, могут принимать синтаксические решения (например, выбирать число отступов в коде), они не спо- собны принимать семантические решения — скажем, насколько удачно выбрано имя переменной. За это отвечаете вы. Об этом мы поговорим в следующей главе. 4Выбор понятных имен«В программировании есть только две сложные проблемы — выбор имен, аннулирование кэша и ошибки смещения на единицу» — эта классическая шутка, приписываемая Леону Бамбрику (Leon Bambrick) и основанная на высказывании Фила Карлтона (Phil Karlton), содержит зерно истины: трудно придумать хорошие имена (формально называемые идентифи-каторами) для переменных, функций, классов и вообще чего угодно в програм- мировании. Компактные, содержательные имена важны для удобочитаемости вашей программы. Тем не менее это проще сказать, чем сделать. Если вы переез- жаете в новый дом, можно пометить все коробки надписью «Барахло» — надпись компактная, но не содержательная. Название книги по программированию «Как создавать собственные компьютерные игры на языке Python» — содержательное, но не компактное.Если вы пишете не «одноразовый» код, который вы не собираетесь сопровождать после однократного запуска программы, вам стоит подумать над выбором хороших имен в вашей программе. Предположим, вы просто присвоили переменным имена a, b и c, значит, вам в будущем придется потратить лишние усилия, чтобы запомнить, для чего предназначались эти переменные.Выбор имен — субъективный выбор, который делаете именно вы. Автоматизи- рованные средства форматирования (такие как Black из главы 3) не способны решить, как должны называться ваши переменные. В этой главе я расскажу, как выбрать «хорошие» имена и избегать «плохих». Как обычно, мои рекомендации не высечены в камне; руководствуйтесь здравым смыслом, чтобы решить, когда стоит применять их в вашем коде. 86 Глава 4.Выбор понятных именМЕТАСИНТАКСИЧЕСКИЕ ПЕРЕМЕННЫЕМетасинтаксические переменные обычно используются в учебниках или фраг- ментах кода, когда необходимо обобщенное имя переменной. В Python в тех переменных, для которых имена несущественны, часто используют имена spam, eggs, bacon и ham. По этой причине я применяю их в примерах кода в книге, но не берите их для реальных программ. Имена происходят из скетча «Спам» группы Monty Python (https://en.wikipedia.org/wiki/Spam_(Monty_Python)). Метасинтаксическим переменным также часто присваивают имена foo и bar. Они происходят от сленгового сокращения FUBAR, которое использовали солдаты армии США во времена Второй Мировой войны, когда хотели со- общить, что ситуация «[уделана] до полной неузнаваемости».Схемы регистра именТак как в идентификаторах Python различается регистр символов и они не могут содержать пробельные символы, программисты используют несколько схем в иден- тификаторах, состоящих из нескольких слов.Змеиный регистр (snake_case) разделяет слова символом подчеркивания, который напоминает ползущую между словами змею. В этом случае все буквы записываются в нижнем регистре, а константы часто записываются в верхнем змеином регистре (UPPER_SNAKE_CASE).Верблюжий регистр (camelCase) — слова записываются в нижнем регистре, но второе и следующие слова начинаются с заглавной. Эта схема в большин- стве случаев подразумевает, что первое слово начинается с буквы нижнего регистра. Буквы верхнего регистра напоминают верблюжьи горбы.Схема Pascal (PascalCase) — названа так, потому что применяется в языке программирования Pascal; аналогична схеме верблюжьего регистра, но первое слово в ней тоже начинается с заглавной. Выбор регистра относится к области форматирования кода, об этом я рассказывал в главе 3. На практике чаще всего встречаются змеиный и верблюжий регистры. Вы можете использовать любую схему, но в одном проекте — только одну, а не обе сразу.Соглашения об именах PEP 8В документе PEP 8 (глава 3) приведены некоторые правила формирования имен в Python. Длина имен87Все буквы должны быть буквами ASCII — то есть латинскими буквами верхнего и нижнего регистров без диакритических знаков.Имена модулей должны быть короткими и состоять только из букв нижнего регистра.Имена классов необходимо записывать в схеме Pascal.Имена констант следует записывать в верхнем змеином регистре.Имена функций, методов и переменных записывают в нижнем змеином регистре.Первый аргумент методов всегда должен называться self в нижнем регистре.Первый аргумент методов классов всегда должен называться cls в нижнем регистре.Приватные атрибуты классов всегда начинают с символа подчеркивания ( _ ).Публичные атрибуты классов никогда не начинают с символа подчеркива- ния ( _ ).При необходимости вы можете изменять или нарушать эти правила. Например, хотя английский язык доминирует в программировании, в идентификаторах можно использовать символы любых языков: команда コンピューター ='laptop' является синтаксически действительным кодом Python. Как вы видите, мои предпочтения в области имен переменных противоречат PEP 8, потому что я использую верблю- жий регистр вместо змеиного. PEP 8 содержит напоминание о том, что программист не обязан неуклонно следовать PEP 8. Важнейший фактор удобочитаемости — не выбор схемы, а последовательность в применении этой схемы.С разделом «Naming Conventions» документа PEP 8 можно ознакомиться по адресу https://www.python.org/dev/peps/pep-0008/#naming-conventions.Длина именОчевидно, имена не должны быть слишком короткими или слишком длинными. Длинные имена переменных утомительно вводить, а короткие могут быть непонят- ными или дезинформирующими. Но так как код читают чаще, чем пишут, лучше все-таки задавать более длинные имена переменных. Рассмотрим примеры.Слишком короткие именаСамая распространенная ошибка при выборе имен — суперкороткие имена. Они зачастую кажутся вам понятными, когда вы впервые их записываете, но вы можете забыть их точный смысл через несколько дней или недель. Рассмотрим несколько разновидностей коротких имен. 88 Глава 4.Выбор понятных именОдно- или двухбуквенное имя (например, g), вероятно, обозначает какое-то слово, начинающееся с этой буквы, но таких слов очень много. Сокращения и одно-двухбуквенные имена легко записать, но они плохо читаются. Это замечание относится и к следующему пункту.Сокращенные имена вида mon — могут означать monitor, month, monster и множество других слов.Имя из одного слова — например, start (начало) — может трактоваться по- разному: начало чего? При отсутствии уточнения другие люди вас вряд ли поймут.Одно- и двухбуквенные имена, сокращения или однословные имена могут быть понятны вам, но всегда следует помнить, что другие программисты (или же вы сами через несколько недель) вряд ли поймут их смысл.В отдельных случаях короткие имена переменных вполне допустимы. Например, имя i часто используется с переменными циклов for, перебирающих диапазоны чисел или индексов списка, а j и k (следующие за i в алфавитном порядке) ис- пользуются с вложенными циклами:>>> 1   2   3   4   5   6   7   8   9   10   ...   40

Встроенные комментарииВстроенные комментарии размещаются в конце строки: while True: # Пока игрок не введет допустимый ход. Комментарии219Встроенные комментарии коротки, потому что они должны укладываться в огра- ничения длины строки, указанные в руководстве по стилю. Это означает, что они могут оказаться слишком короткими, чтобы содержать достаточную информацию. Если вы решите использовать встроенные комментарии, убедитесь в том, что ком- ментарий описывает только непосредственно предшествующую ему строку кода. Если вашему встроенному комментарию требуется больше места или он описывает другие строки кода, разместите его в отдельной строке.Встроенные комментарии чаще всего применяют — и это весьма уместно — для по- яснения смысла переменных или другой информации, относящейся к переменным. Такие встроенные комментарии записываются в команде присваивания, которая создает переменную:TOTAL_DISKS = 5 # Чем больше дисков, тем сложнее головоломка.Другое типичное применение встроенных комментариев — добавление информации о значениях переменных при их создании:month = 2 # Месяцы пронумерованы от 0 (январь) до 11 (декабрь).catWeight = 4.9 # Вес в килограммах.website = 'inventwithpython.com' # Префикс "https://" не включается.Во встроенных комментариях не следует указывать тип данных переменной, потому что он должен быть очевиден из команды присваивания; исключение составляет такая разновидность комментариев, как аннотации типов (см. подраздел «Обратное портирование аннотаций типов» далее в этой главе, с. 232).Пояснительные комментарииКак правило, комментарии должны сообщать, почему код был написан именно так, а не иначе, что он делает или как он это делает. Даже при использовании правиль- ного стиля кода и полезных соглашений об именах, о которых я рассказывал в гла- вах 3 и 4, реальный код не способен объяснить, какие задачи ставил программист. Если вы сами написали код, то через несколько недель можете забыть какие-то подробности. Поэтому лучше сегодня дать содержательные комментарии, чтобы завтра вам не пришлось проклинать вас вчерашнего. Например, ниже приведен бесполезный комментарий, который объясняет, что делает код. Вместо того чтобы пояснить задачу кода, он констатирует очевидное:>>> currentWeekWages *= 1.5 # Заработок за текущую неделю умножается на 1.5Такой комментарий хуже бесполезного. Из кода и так очевидно, что переменная currentWeekWages умножается на 1.5, и если полностью убрать комментарий, это только упростит ваш код. Следующий комментарий гораздо лучше:>>> currentWeekWages *= 1.5 # Включить в расчет полуторную ставку. 220 Глава 11.Комментарии, doc-строки и аннотации типовЭтот комментарий объясняет смысл строки кода, а не пересказывает, как тот ра- ботает. Он предоставляет информацию, которую даже хорошо написанный код передать не сможет.Сводные комментарииПрактическая польза комментариев не ограничивается пояснением намерений про- граммиста. Краткие комментарии, резюмирующие работу нескольких строк кода, позволяют читателю бегло просмотреть исходник и получить общее представление о том, что он делает. Программисты часто вставляют пустые строки, чтобы отделить «абзацы» кода друг от друга. Сводные комментарии обычно занимают одну строку в начале таких абзацев. В отличие от однострочных комментариев, поясняющих отдельные строки кода, сводные комментарии на более высоком уровне абстракции описывают, что делает код.Например, из следующих четырех строк кода можно понять, что они присваивают переменной playerTurn значение, обозначающее другого игрока. Но короткий одно- строчный комментарий избавит читателя от необходимости читать и обдумывать код, чтобы понять смысл происходящего:# Ход передается другому игроку:if playerTurn == PLAYER_X: playerTurn = PLAYER_Oelif playerTurn == PLAYER_O: playerTurn = PLAYER_XЕсли вы включите эти сводные комментарии в свою программу, это значительно упростит ее чтение. Далее программист сможет внимательнее проанализировать те места, которые вызывают у него особый интерес. Сводные комментарии также препятствуют формированию ошибочных представлений о том, что делает код. Краткий сводный комментарий позволяет разработчику удостовериться, что он правильно понял, как работает код.Комментарии «полученный опыт»Когда я работал в компании, занимающейся разработкой программного обеспече- ния, однажды мне предложили адаптировать библиотеку построения графиков — требовалось, чтобы она в реальном времени обновляла миллионы точек данных на диаграмме. Используемая библиотека могла либо обновлять графики в реальном времени, либо поддерживать графики с миллионами точек данных, но не то и дру- гое одновременно. Я думал, что справлюсь с этой задачей за несколько дней. Через три недели я все еще был убежден, что до окончания работы рукой подать. Каждый день мне казалось, что решение совсем рядом, и на пятую неделю у меня появился рабочий прототип. Комментарии221За это время я много узнал о том, как работает библиотека, каковы ее возможности и ограничения. Поэтому я потратил несколько часов на то, чтобы оформить мои знания в комментарий длиной в страницу, который я включил в исходный код. Я был убежден, что каждый, кому потребуется внести изменения в работу про- граммы, столкнется с теми же (простыми на первый взгляд) проблемами, что и я, а написанная мной документация сэкономит им целые недели работы.Такие комментарии могут занимать несколько абзацев, из-за чего они кажутся неуместными в файлах с исходным кодом. Но содержащаяся в них информация бесценна для любого специалиста, который будет заниматься сопровождением программы. Не бойтесь включать в свой код длинные, подробные комментарии, которые объясняют, как он работает. Другие программисты могут не знать тонкости вашей реализации кода, они могут понять их неправильно или упустить их из виду. Если разработчика комментарии не интересуют, он с легкостью их пропустит, зато те, кому они нужны, будут благодарны.Как и в предыдущих случаях, этот вид комментариев не должен заменять докумен- тацию модуля или функции (для которой создаются doc-строки). Комментарии типа «полученный опыт» — не учебник и не сборник рецептов для пользователей програм- мы. Они предназначены для разработчиков, читающих исходный код. Так как мой комментарий относился к библиотеке с открытым кодом и мог пригодиться другим, я опубликовал его в ответе на общедоступном сайте https://stackoverflow.org, где он оказался доступен другим пользователям, оказавшимся в аналогичной ситуации.Комментарии об авторских правах и интеллектуальной собственностиНекоторые компании-разработчики или проекты с открытым кодом используют политику включения сведений об авторских правах и интеллектуальной собствен- ности, а также текстов лицензий в начало каждого файла с исходным кодом. Такие аннотации должны содержать всего несколько строк и выглядеть примерно так:"""Cat Herder 3.0 Copyright (C) 2021 Al Sweigart. All rights reserved.See license.txt for the full text."""Если возможно, включите ссылку на внешний документ или веб-сайт с полным текстом лицензии (вместо того, чтобы включать всю длинную лицензию в начало каждого файла с исходным кодом). Прокручивать несколько лишних экранов текста каждый раз, когда вы открываете файл с исходным кодом, утомительно, а публика- ция текста полной лицензии не обеспечивает дополнительной юридической защиты.Профессиональные комментарииНа моей первой работе в качестве программиста старший коллега, которого я очень уважал, отвел меня в сторону и объяснил, что поскольку мы иногда 222 Глава 11.Комментарии, doc-строки и аннотации типов предоставляем исходный код наших продуктов клиентам, очень важно выдер- живать профессиональный тон в комментариях. Как выяснилось, я написал «Какого черта?» в одном из комментариев, относящихся к особенно противной части кода. Я устыдился, немедленно извинился и отредактировал текст. С этого момента я пишу в комментариях (даже в персональных проектах) только про- фессиональные замечания.Возможно, вам захочется высказать свое раздражение или проявить остроумие в комментариях программы, но делать так не стоит. Вы не знаете, кто будет читать ваш код в будущем, а тональность текста легко интерпретируется неверно. Как я объяснял в разделе «Избегайте шуток, каламбуров и культурных отсылок», с. 91, лучше всего писать комментарии прямолинейно, четко и без юмора.Кодовые метки и комментарии TODOПрограммисты иногда оставляют короткие комментарии, напоминающие им о работе, которую еще предстоит сделать. Обычно их оформляют в виде кодовых меток — комментариев, которые предваряет метка, записанная в верхнем регистре (например, TODO). В идеале для памяток лучше использовать средства управления проектами, а не зарывать их в исходном коде. Но в небольших персональных про- ектах, в которых такие инструменты не используются, встречающиеся время от времени метки TODO могут стать полезным напоминанием. Пример:_chargeIonFluxStream() # TODO: Выяснить, почему каждый вторник происходит сбой.Для таких напоминаний часто используются следующие метки:TODO — общее напоминание о работе, которую необходимо выполнить;FIXME — эта часть кода работает не полностью;HACK — эта часть кода работает (возможно, по минимуму), но ее можно улучшить;XXX — общее предупреждение, часто весьма серьезное.За метками, записанными в верхнем регистре, следует давать более конкретные описания задачи или проблемы. Позднее вы сможете провести поиск меток в ис- ходном коде и найти фрагмент, который необходимо доработать. С другой стороны, у таких напоминаний есть и недостаток: о них легко забыть, если только вы не читаете ту часть кода, где они находятся. Кодовые метки не заменяют формальной системы отслеживания ошибок или программ отправки отчетов об ошибках. Если вы используете кодовые метки в своей программе, я рекомендую предельно упростить их: используйте только TODO и откажитесь от остальных. Doc-строки223Магические комментарии и кодировка исходных файловВозможно, вам встречались исходные файлы .py, в начале которых находились строки следующего вида:#!/usr/bin/env python3 ❶# -*- coding: utf-8 -*- ❷Магические комментарии в начале файла предоставляют информацию об интерпре- таторе или кодировке. Строка ❶ (о которой я упоминал в главе 2) сообщает вашей операционной системе, какой интерпретатор следует использовать для выполнения инструкций в файле.Второй магический комментарий в строке ❷ определяет кодировку. В данном случае строка определяет, что для этого исходного файла должна использоваться кодировка UTF-8. Включать эту строку почти всегда не обязательно, потому что большинство редакторов и IDE уже сохраняет файлы с исходным кодом в кодировке UTF-8, а все версии Python, начиная с Python 3.0, определяют UTF-8 как кодировку по умолчанию. Файлы в кодировке UTF-8 могут содержать любые символы, так что ничто не мешает вам включить в исходный файл .py английские, китайские или арабские символы.Если вам потребуется информация о Юникоде и кодировке строк, я рекомендую публикацию в блоге Неда Бэтчелдера (Ned Batchelder) «Pragmatic Unicode» по адресу https://nedbatchelder.com/text/unipain.html.Doc-строкиDoc-строки представляют собой многострочные комментарии, расположенные либо в начале файла .py с исходным кодом модуля, либо непосредственно после коман- ды class или def. Они содержат документацию об определяемом модуле, классе, функции или методе. Средства автоматизированного генерирования документации используют их для генерирования внешних файлов с документацией — например, справочных файлов или веб-страниц.Doc-строки должны быть оформлены в виде многострочных комментариев в трой- ных кавычках (вместо однострочных комментариев, начинающихся с решетки #). Doc-строки всегда используют утроенные двойные кавычки вместо утроенных одинарных кавычек. Например, ниже приведен фрагмент файла sessions.py из по- пулярного модуля requests:# -*- coding: utf-8 -*- ❶""" ❷requests.session 224 Глава 11.Комментарии, doc-строки и аннотации типовThis module provides a Session object to manage and persist settings across requests (cookies, auth, proxies)"""import os import sys--snip—class Session(SessionRedirectMixin): """A Requests session. ❸ Provides cookie persistence, connection-pooling, and configuration. Basic Usage:: >>> import requests >>> s = requests.Session() >>> s.get('https://httpbin.org/get') --snip-- def get(self, url, **kwargs): r"""Sends a GET request. Returns :class:`Response` object. ❹ :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. :rtype: requests.Response """--snip--Файл sessions.py содержит doc-строки для модуля ❷, класса Session❸ и метода get()класса Session❹. Обратите внимание: хотя doc-строка модуля должна быть первой строкой в модуле, она располагается после любых специальных комментариев — в частности, определения кодировки ❶Позднее вы можете прочитать doc-строки модуля, класса, функции или метода, проверяя атрибут __doc__ соответствующего объекта. Так, в следующем примере проверяются doc-строки для получения дополнительной информации о модуле sessions, классе Session и методе get():>>> from requests import sessions>>> sessions.__doc__'\nrequests.session\n\n\nThis module provides a Session object to manage and persist settings across\nrequests (cookies, auth, proxies).\n'>>> sessions.Session.__doc__"A Requests session.\n\n Provides cookie persistence, connection-pooling, and configuration.\n\n Basic Usage::\n\n >>> import requests\n --snip-->>> sessions.Session.get.__doc__'Sends a GET request. Returns :class:`Response` object.\n\n :param url: URL for the new :class:`Request` object.\n :param \\*\\*kwargs:--snip-- Doc-строки225Средства автоматизированного документирования могут пользоваться doc- строками для предоставления информации, соответствующей контексту. Одно из таких средств — встроенная функция Python help() — выводит doc-строку пере- данного ей объекта в более удобочитаемом формате, чем у необработанной строки __doc__. Данная возможность может оказаться полезной при экспериментах с ин- терактивной оболочкой, потому что вы можете немедленно получить информацию о любых модулях, классах и функциях, которые вы пытаетесь использовать:>>> from requests import sessions>>> help(sessions)Help on module requests.sessions in requests:NAME requests.sessionsDESCRIPTION requests.session This module provides a Session object to manage and persist settings-- More --Если doc-строка слишком велика, чтобы поместиться на экране, Python выводит подсказку --More-- в нижней части окна. Нажмите ENTER, чтобы прокрутить текст к следующей строке, пробел для вывода следующей страницы или Q для прекращения просмотра doc-строки.В общем случае doc-строка должна содержать одну строку текста с обобщающим описанием модуля, класса или функции, за которым следует пустая строка и более подробная информация. Для функций и методов она может включать информацию об их параметрах, возвращаемом значении и побочных эффектах. Doc-строки пи- шутся для других программистов, а не для пользователей программы, поэтому они должны содержать техническую информацию, а не обучающие советы.У doc-строк также есть второе ключевое преимущество: они интегрируют доку- ментацию в исходный код. Когда документация пишется отдельно от кода, о ней часто вообще забывают. С другой стороны, когда doc-строки размещены в начале модулей, классов и функций, информацию легко просматривать и обновлять.Возможно, вы не сможете сразу писать doc-строки, если работа над кодом, ко- торый вы хотите описывать, еще не завершена. В таком случае включите в doc- строку комментарий TODO с напоминанием. Например, следующая вымышленная функция reverseCatPolarity() содержит плохую doc-строку, которая утверждает очевидное:def reverseCatPolarity(catId, catQuantumPhase, catVoltage): """Reverses the polarity of a cat. 226 Глава 11.Комментарии, doc-строки и аннотации типов TODO Finish this docstring."""--snip--( """Меняет полярность у кота. TODO: Закончить эту doc-строку)Так как каждый класс, функция и метод должны содержать doc-строку, может по- явиться искушение написать минимальную документацию и двигаться дальше. Без комментария TODO слишком легко забыть о том, что doc-строку следует переписать.PEP 257 содержит дополнительную документацию о doc-строках по адресу https://www.python.org/dev/peps/pep-0257/.Аннотации типовВо многих языках программирования используется статическая типизация; это означает, что программист должен объявить типы данных всех переменных, пара- метров и возвращаемых значений в исходном коде. Такая возможность позволяет интерпретатору или компилятору проверить правильность применения всех объ- ектов до запуска программы. В Python используется динамическая типизация: переменные, параметры и возвращаемые значения могут иметь любой тип данных и даже менять типы данных во время выполнения программы. На динамических языках обычно проще программировать, потому что они требуют меньше формаль- ных определений, но зато в них отсутствуют средства предотвращения ошибок, присущие статическим языкам. Когда вы пишете строку кода Python — например, round('forty two'), вы можете не понять, что строка передается функции, полу- чающей только аргументы int и float, пока программа не будет запущена и не про- изойдет ошибка. Языки со статической типизацией выдают ранние предупреждения при присваивании значения или передаче аргумента неправильного типа.Аннотации типов в Python предоставляют необязательные средства статической типизации. В следующем примере аннотации типов выделены жирным шрифтом:def describeNumber(number1   ...   19   20   21   22   23   24   25   26   ...   40

echo "Test file" > deleteme.txtC:\Users\Al\wizcoin>git add deleteme.txtC:\Users\Al\wizcoin>git commit -m "Adding a file to test Git deletion."[master 441556a] Adding a file to test Git deletion. 1 file changed, 1 insertion(+) create mode 100644 deleteme.txtC:\Users\Al\wizcoin>git statusOn branch master nothing to commit, working tree clean Создание репозитория Git на вашем компьютере253Не удаляйте файлы командой del в Windows или командой rm в macOS и Linux. (А если вы это сделаете, файл можно восстановить командой git restore <имя_фай-ла> или просто перейти к команде git rm, чтобы удалить файл из репозитория.) Вместо этого воспользуйтесь командой git rm, чтобы удалить и проиндексировать файл deleteme.txt, как это делается в следующем примере:C:\Users\Al\wizcoin>git rm deleteme.txtrm deleteme.txt'Команда git rm удаляет файл из рабочей копии, но это еще не все. Как и git add, команда git rm индексирует файл. Удаление файла необходимо закрепить точно так же, как любые другие изменения:C:\Users\Al\wizcoin>git statusOn branch masterChanges to be committed: (use "git reset HEAD ..." to unstage) ❶ deleted: deleteme.txtC:\Users\Al\wizcoin>git commit -m "Deleting deleteme.txt from the repo to finish the deletion test."[master 369de78] Deleting deleteme.txt from the repo to finish the deletion test. 1 file changed, 1 deletion(-) delete mode 100644 deleteme.txtC:\Users\Al\Desktop\wizcoin>git statusOn branch master nothing to commit, working tree cleanХотя файл deleteme.txt был удален из рабочей копии, он все еще существует в исто- рии репозитория. В разделе «Восстановление старых изменений» этой главы рас- сказано, как восстановить удаленный файл или отменить изменение.Команда git rm работает только с файлами, находящимися в чистом, сохраненном, состоянии без каких-либо изменений. В противном случае Git предложит сохранить изменения или отменить их командой git resetHEAD <имя_файла>. (Вывод git status напомнит вам об этой команде ❶.) Такая процедура предотвращает случайное удаление несохраненных изменений.Переименование и перемещение файлов из репозиторияКак и при удалении файлов, вы не должны переименовывать или перемещать файлы в репозитории в обход Git. В противном случае Git решит, что вы просто удалили файл, и создаст новый файл с прежним содержимым. Вместо этого используйте команду git mv с последующей командой git commit 254 Глава 12.Git и организация программных проектовПереименуйте README.md в README.txt следующими командами:C:\Users\Al\wizcoin>git mv README.md README.txtC:\Users\Al\wizcoin>git statusOn branch masterChanges to be committed: (use "git reset HEAD ..." to unstage) renamed: README.md -> README.txtC:\Users\Al\wizcoin>git commit -m "Testing the renaming of files in Git."[master 3fee6a6] Testing the renaming of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.md => README.txt (100%)В этом случае история изменений README.txt также включает историю README.mdТакже можно воспользоваться командой git mv для перемещения файла в новую папку. Введите следующие команды, чтобы создать новую папку с именем movetest и переместить в нее файл README.txt:C:\Users\Al\wizcoin>mkdir movetestC:\Users\Al\wizcoin>git mv README.txt movetest/README.txtC:\Users\Al\wizcoin>git statusOn branch masterChanges to be committed: (use "git reset HEAD ..." to unstage) renamed: README.txt -> movetest/README.txtC:\Users\Al\wizcoin>git commit -m "Testing the moving of files in Git."[master 3ed22ed] Testing the moving of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.txt => movetest/README.txt (100%)Также можно совместить переименование файла с перемещением — для этого сле- дует передать git mv новое имя и местоположение файла. Вернем файл README.txt в корневой рабочий каталог с восстановлением исходного имени:C:\Users\Al\wizcoin>git mv movetest/README.txt README.mdC:\Users\Al\wizcoin>git statusOn branch masterChanges to be committed: (use "git reset HEAD ..." to unstage) renamed: movetest/README.txt -> README.mdC:\Users\Al\wizcoin>git commit -m "Moving the README file back to its original place and name."[master 962a8ba] Moving the README file back to its original place and name. Просмотр журнала коммитов255 1 file changed, 0 insertions(+), 0 deletions(-) rename movetest/README.txt => README.md (100%)Хотя файл README.md вернулся в исходную папку и имеет исходное имя, репози- торий Git запоминает изменения имени и местоположения. Историю изменений можно вызвать командой git log, о которой пойдет речь в следующем разделе.Просмотр журнала коммитовКоманда git log выводит список всех коммитов:C:\Users\Al\wizcoin>git logcommit 962a8baa29e452c74d40075d92b00897b02668fb (HEAD -> master)Author: Al Sweigart Date: Wed Sep 1 10:38:23 2021 -0700 Moving the README file back to its original place and name.commit 3ed22ed7ae26220bbd4c4f6bc52f4700dbb7c1f1Author: Al Sweigart Date: Wed Sep 1 10:36:29 2021 -0700 Testing the moving of files in Git.--snip--Команда способна выводить большой объем текста. Если журнал не помещается в окне терминала, текст можно прокрутить клавишами ↑ и ↓. Чтобы завершить просмотр, нажмите клавишу q.Если вы хотите вернуть файлы к более раннему коммиту, сначала следует найти хеш коммита — строку из 40 шестнадцатеричных цифр (0–9 и буквы A–F), которая служит уникальным идентификатором коммита. Например, последний коммит в нашем репозитории представлен хешем 962a8baa29e452c74d40075d92b00897b026 68fb. На практике обычно используются только первые семь знаков: 962a8baСо временем журнал может стать очень длинным. Ключ --oneline усекает вывод до сокращенных хешей и первой строки каждого сообщения коммита. Введите команду git log--oneline в командной строке:C:\Users\Al\wizcoin>git log --oneline962a8ba (HEAD -> master) Moving the README file back to its original place and name.3ed22ed Testing the moving of files in Git.15734e5 Deleting deleteme.txt from the repo to finish the deletion test.441556a Adding a file to test Git deletion.2a4c5b8 Added example code to README.md e1ae3a3 An initial add of the project files. 256 Глава 12.Git и организация программных проектовЕсли вывод остается слишком длинным, используйте ключ -n для ограничения вывода n последними коммитами. Введите команду git log--oneline-n3, чтобы просмотреть только три последних коммита:C:\Users\Al\wizcoin>git log --oneline -n 3962a8ba (HEAD -> master) Moving the README file back to its original place and name.3ed22ed Testing the moving of files in Git.15734e5 Deleting deleteme.txt from the repo to finish the deletion test.Чтобы вывести содержимое файла на момент конкретного коммита, можно за- дать команду git show <хеш>:<имя_файла>. Впрочем, графические средства Git предоставляют более удобный интерфейс для просмотра журнала, чем командная строка Git.Восстановление старых измененийДопустим, вы хотите вернуться к более ранней версии своего исходного кода, потому что в программе была допущена ошибка или вы случайно удалили файл. Система контроля версий позволяет вернуть рабочую копию к состоянию более раннего коммита. Конкретная команда зависит от состояния файлов в рабочей копии.Помните, что системы контроля версий только добавляют информацию. Даже при удалении файла из репозитория Git запомнит его, чтобы его можно было восстано- вить в будущем. Отмена изменения в действительности добавляет новое изменение, которое возвращает файл к его состоянию при предыдущем коммите. Подробная информация о различных видах отмены доступна на https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/.Отмена несохраненных локальных измененийЕсли вы внесли в файл несохраненные изменения, но хотите вернуть его к версии в последнем коммите, выполните команду git restore <имя_файла>. В следующем примере мы изменяем файл README.md, но не индексируем и не сохраняем его: C:\Users\Al\wizcoin>git status On branch masterChanges not staged for commit: (use "git add ..." to update what will be committed) (use "git restore ..." to discard changes in working directory) modified: README.md no changes added to commit (use "git add" and/or "git commit -a")C:\Users\Al\wizcoin>git restore README.md Восстановление старых изменений257C:\Users\Al\wizcoin>git statusOn branch masterYour branch is up to date with 'origin/master'.nothing to commit, working tree cleanПосле выполнения команды git restoreREADME.md содержимое файла README.md становится таким, как в последнем коммите. Фактически это операция отмены из- менений, внесенных в файл (который еще не был проиндексирован или сохранен). Но будьте внимательны: вы уже не сможете отменить эту «отмену», чтобы вернуть последние изменения. Также можно выполнить команду git checkout, чтобы от- менить все изменения во всех файлах рабочей копии.Деиндексирование проиндексированного файлаЕсли вы проиндексировали измененный файл командой git add, а теперь хотите исключить его из индексированного состояния, чтобы он не был включен в следу- ющий коммит, выполните команду git restore--staged <имя_файла>:C:\Users\Al>git restore --staged README.mdUnstaged changes after reset:M spam.txtФайл README.md остается измененным, как это было до его индексирования ко- мандой git add, но он не находится в индексированном состоянии.Отмена последних коммитовДопустим, вы сделали несколько бесполезных коммитов и теперь хотите вернуться к предыдущему коммиту. Чтобы отменить конкретное число последних коммитов (например, 3), используйте команду git revert-nHEAD3..HEAD. Вместо 3 можно указать любое количество коммитов. Предположим, вы отслеживаете изменения в детективном романе, который вы пишете, и у вас имеется следующий журнал Git со всеми коммитами и сообщениями:C:\Users\Al\novel>git log --onelinede24642 (HEAD -> master) Changed the setting to outer space.2be4163 Added a whacky sidekick.97c655e Renamed the detective to 'Snuggles'.8aa5222 Added an exciting plot twist.2590860 Finished chapter 1.2dece36 Started my novel.В какой-то момент вы решаете, что хотите начать заново с сюжетного поворота с хешем 8aa5222. Это означает, что нужно отменить изменения трех последних коммитов: de24642, 2be4163 и 97c655e. Выполните команду git revert-nHEAD3.. 258 Глава 12.Git и организация программных проектовHEAD, чтобы отменить эти изменения, а затем выполните команды git add и git commit-m"<сообщение>" для сохранения контента, как и для любого другого изменения:C:\Users\Al\novel>git revert -n HEAD3..HEADC:\Users\Al\novel>git add .C:\Users\Al\novel>git commit -m "Starting over from the plot twist."[master faec20e] Starting over from the plot twist. 1 file changed, 34 deletions(-)C:\Users\Al\novel>git log --onelinefaec20e (HEAD -> master) Starting over from the plot twist.de24642 Changed the setting to outer space.2be4163 Added a whacky sidekick.97c655e Renamed the detective to 'Snuggles'.8aa5222 Added an exciting plot twist.2590860 Finished chapter 1.2dece36 Started my novel.Репозитории Git обычно только добавляют информацию, поэтому при отмене ком- митов они остаются в истории коммитов. Если потребуется «отменить отмену», вы можете снова вернуться к нужному состоянию командой git revertВозврат к конкретному коммиту для отдельного файлаТак как коммиты отражают состояние всего репозитория, а не отдельных файлов, то вам потребуется другая команда, если вы захотите отменить изменения для от- дельного файла. Допустим, я веду репозиторий Git для небольшого программного проекта. Я создал файл eggs.py, добавил в него функции spam() и bacon(), а затем переименовал bacon() в cheese(). Журнал репозитория будет выглядеть примерно так:C:\Users\Al\myproject>git log --oneline895d220 (HEAD -> master) Adding email support to cheese().df617da Renaming bacon() to cheese().ef1e4bb Refactoring bacon().ac27c9e Adding bacon() function.009b7c0 Adding better documentation to spam().0657588 Creating spam() function.d811971 Initial add.Но я решил, что я хочу вернуться к файлу до добавления функции bacon(), но не изменять любые другие файлы в репозитории. Можно воспользоваться командой git show <хеш>: <имя_файла> для вывода этого файла на момент последнего конкретного коммита. Команда выглядит примерно так: Восстановление старых изменений259C:\Users\Al\myproject>git show 009b7c0:eggs.py <содержимое eggs.py на момент сохранения 009b7c0>При помощи команды git checkout <хеш> -- <имя_файла> можно вернуть со- держимое eggs.py к этой версии и сохранить измененный файл обычным способом. Команда git checkout изменяет только рабочую копию. Вам останется только про- индексировать и сохранить эти коррективы, как и любые другие:C:\Users\Al\myproject>git checkout 009b7c0 -- eggs.pyC:\Users\Al\myproject>git add eggs.pyC:\Users\Al\myproject>git commit -m "Rolled back eggs.py to 009b7c0"[master d41e595] Rolled back eggs.py to 009b7c0 1 file changed, 47 deletions(-)C:\Users\Al\myproject>git log --onelined41e595 (HEAD -> master) Rolled back eggs.py to 009b7c0 895d220 Adding email support to cheese().df617da Renaming bacon() to cheese().ef1e4bb Refactoring bacon().ac27c9e Adding bacon() function.009b7c0 Adding better documentation to spam().0657588 Creating spam() function.d811971 Initial add.Файл eggs.py был возвращен к прежнему состоянию, а оставшаяся часть репози- тория осталась неизменной.Перезапись истории коммитовЕсли вы случайно сохранили файл, содержащий конфиденциальную информа- цию (пароли, ключи API, номера кредитных карт), недостаточно вычеркнуть эту информацию и создать новый коммит. Каждый, кто имеет доступ к репозиторию на вашем компьютере или к удаленному репозиторию, сможет вернуться к версии, содержащей эту информацию.Удалить информацию из репозитория так, чтобы ее было невозможно вос- становить, непросто, но возможно. Подробно рассказывать об этом здесь я не буду, но вы можете воспользоваться либо командой git filter-branch or, либо программой BFG Repo-Cleaner (этот вариант считается предпочтительным). Оба варианта описаны на https://help.github.com/en/articles/removing-sensitive-data-from-a-repository.Простейшая превентивная мера — разместить конфиденциальную информацию в файле с именем secrets.txt, conidential.py или что-нибудь в этом роде. Файл включа- ется в .gitignore, чтобы он никогда не был сохранен в репозитории. Ваша программа 260 Глава 12.Git и организация программных проектов может прочитать конфиденциальную информацию из файла — это лучше, чем раз- мещать такую информацию непосредственно в исходном коде.GitHub и команда git pushХотя репозитории Git могут существовать на вашем компьютере, многие бесплат- ные веб-сайты позволяют размещать клоны ваших репозиториев в интернете, чтобы другие люди могли легко загрузить ваши проекты и участвовать в работе над ними. Самый большой из таких сайтов — GitHub. Если вы сохраните клон своего проекта в интернете, коллеги смогут дополнять ваш код, даже когда компьютер, на котором вы работаете, отключен. Кроме того, клонированная копия фактически выполняет роль резервной копии.ПРИМЕЧАНИЕЧтобы избежать путаницы с терминами: Git — система контроля версий, которая под- держивает репозиторий и включает команду git. GitHub — веб-сайт для размещения репозиториев Git в интернете.Рис. 12.6. Создание нового репозитория на GitHub GitHub и команда git push261Зайдите на сайт https://github.com и зарегистрируйтесь для получения бесплатной учетной записи. На домашней странице GitHub или на вкладке Repositories страницы вашего профиля щелкните на кнопке New, чтобы создать новый проект. Введите имя репозитория wizcoin и описание проекта — об этом я рассказывал в разделе «Соз- дание новых проектов Python с использованием Cookiecutter» на с. 236 (рис. 12.6). Пометьте репозиторий как общедоступный (Public) и снимите флажок Initialize this repository with a README, потому что мы импортируем существующий репозиторий. Щелкните на кнопке Create repository. Все эти действия эквивалентны выполнению команды git init на веб-сайте GitHub.Веб-страница для ваших репозиториев будет располагаться по адресу https://github.com/<имя_пользователя>/<имя_репозитория>. Мой репозиторий wizcoin раз- мещается на https://github.com/asweigart/wizcoin.Отправка существующего репозитория на GitHubЧтобы отправить существующий репозиторий из командной строки, введите сле- дующие команды:C:\Users\Al\wizcoin>git remote add origin https://github.com/<пользователь_github>/wizcoin.gitC:\Users\Al\wizcoin>git push -u origin masterUsername for 'https://github.com': <пользователь_github>Password for 'https://@github.com': <пароль_github>Counting objects: 3, done.Writing objects: 100% (3/3), 213 bytes | 106.00 KiB/s, done.Total 3 (delta 0), reused 0 (delta 0)To https://github.com/<ваш_github>/wizcoin.git * [new branch] master -> masterBranch 'master' set up to track remote branch 'master' from 'origin'.Команда git remote add origin https://github.com/<пользователь_github>/wizcoin.git добавляет GitHub как удаленный репозиторий, соответствующий ва- шему локальному репозиторию. После этого вы можете отправить все изменения, внесенные в локальном репозитории, в удаленный командой git push-u origin master. Следующие отправки из локального репозитория вы сможете осуществлять простой командой git push. Отправка копий на GitHub после каждого коммита — хорошая тактика, гарантирующая синхронизацию удаленного репозитория на GitHub с вашим локальным репозиторием, но она не обязательна.Загрузив веб-страницу репозитория на GitHub, вы должны получить информацию о файлах и коммитах. Конечно, это далеко не все, что можно узнать о GitHub — включая и то, как принимать действия других людей в ваших репозиториях посред- ством pull-запросов. Эта тема, наряду с другими расширенными возможностями GitHub, выходит за рамки книги. 262 Глава 12.Git и организация программных проектов1   ...   23   24   25   26   27   28   29   30   ...   40

import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10)>>> tipJar = wizcoin.WizCoin(0, 0, 37)>>> purse + tipJar Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'WizCoin' and 'WizCoin'Вместо того чтобы писать метод addWizCoin() для класса WizCoin, вы можете ис- пользовать dunder-метод __add__() и заставить объекты WizCoin работать с опера- тором +. Добавьте следующий фрагмент в конец файла wizcoin.py: 370 Глава 17.ООП в Python: свойства и dunder-методы--snip-- def __add__(self, other): ❶ """Суммирует денежные величины двух объектов WizCoin.""" if not isinstance(other, WizCoin): ❷ return NotImplemented return WizCoin(other.galleons + self.galleons, other.sickles + ❸ self.sickles, other.knuts + self.knuts)Когда объект WizCoin стоит в левой части оператора +, Python вызывает метод __add__()❶ и передает значение в правой части оператора + в параметре other(Параметру можно назначить любое имя, но традиционно используется имя other.)Помните, что методу __add__() можно передать объект любого типа, поэтому в ме- тод необходимо включить проверку типа ❷. Например, бессмысленно прибавлять целое число или число с плавающей точкой к объекту WizCoin, потому что мы не знаем, к какому атрибуту его следует прибавить — galleons, sickles или knutsМетод __add__() создает новый объект WizCoin с атрибутами, равными сумме galleons, sickles и knuts объектов self и other❸. Так как все три атрибута содержат целые числа, их можно суммировать оператором +. Теперь оператор + для класса WizCoin перегружен, и его можно использовать с объектами WizCoinПерегрузка оператора + позволяет создавать более понятный код. Например, вве- дите следующий фрагмент в интерактивной оболочке:>>> import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10) # Создает объект WizCoin.>>> tipJar = wizcoin.WizCoin(0, 0, 37) # Создает другой объект WizCoin.>>> purse + tipJar # Создает новый объект WizCoin с суммой.WizCoin(2, 5, 47)Если в other передается объект неправильного типа, dunder-метод должен не вы- давать исключение, а возвращать встроенное значение NotImplemented. Например, в следующем фрагменте в other передается целое число:>>> import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10)>>> purse + 42 # Объекты WizCoin и целые числа не могут суммироваться.Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'WizCoin' and 'int'Возвращение NotImplemented приказывает Python попробовать вызвать другие ме- тоды для выполнения этой операции (за подробностями обращайтесь к подразделу «Отраженные числовые dunder-методы» этой главы). Во внутренней реализации Python вызывает метод __add__() со значением 42 для параметра other, но этот метод тоже возвращает NotImplemented, что заставляет Python выдать исключение TypeError Dunder-методы Python371И хотя операции прибавления или вычитания целых чисел из объектов WizCoin не имеют смысла, будет разумно разрешить умножение объектов WizCoin на положи- тельные целые числа; для этого определяется dunder-метод __mul__(). Добавьте следующий фрагмент в конец файла wizcoin.py:--snip-- def __mul__(self, other): """Умножает количество монет на неотрицательное целое число.""" if not isinstance(other, int): return NotImplemented if other < 0: # Умножение на отрицательное целое число приведет # к отрицательному количеству монет, что недопустимо. raise WizCoinException('cannot multiply with negative integers') return WizCoin(self.galleons * other, self.sickles * other, self.knuts * other)Этот метод __mul__() позволяет умножать объекты WizCoin на положительные це- лые числа. Если other является целым числом, то это тип данных, которые ожидает получить метод __mul__(), и возвращать NotImplemented не нужно. Но если целое число — отрицательное, то умножение объекта WizCoin на него приведет к отрица- тельному количеству монет в объекте WizCoin. Так как это противоречит нашему подходу к проектированию класса, мы выдаем исключение WizCoinException с со- держательным сообщением об ошибке.ПРИМЕЧАНИЕНе изменяйте объект self в математическом dunder-методе. Метод всегда должен созда- вать и возвращать новый объект. Оператор + и другие числовые операторы всегда должны давать в результате новый объект, вместо того чтобы изменять значение объекта на месте.Чтобы увидеть dunder-метод __mul__() в действии, введите следующий фрагмент в интерактивной оболочке:>>> import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10) # Создать объект WizCoin.>>> purse * 10 # Умножить объект WizCoin на целое число.WizCoin(20, 50, 100)>>> purse * -2 # Умножение на отрицательное целое число приводит к ошибке.Traceback (most recent call last): File "", line 1, in File "C:\Users\Al\Desktop\wizcoin.py", line 86, in __mul__ raise WizCoinException('cannot multiply with negative integers')wizcoin.WizCoinException: cannot multiply with negative integersВ табл. 17.1 приведен полный список числовых dunder-методов. Как правило, вам не требуется полный список этих методов для ваших классов. Вы сами решаете, какие методы для вас актуальны. 372 Глава 17.ООП в Python: свойства и dunder-методыТаблица 17.1. Числовые dunder-методыDunder-методОперацияОператор или встроенная функция__add__()Сложение+__sub__()Вычитание-__mul__()Умножение*__matmul__()Матричное умножение (в Python 3.5 и выше)@__truediv__()Деление/__floordiv__()Целочисленное деление//__mod__()Остаток%__divmod__()Деление с остатком divmod()__pow__()Возведение в степень**, pow()__lshift__()Сдвиг влево>>__rshift__()Сдвиг вправо<<__and__()Поразрядная операция AND&__or__()Поразрядная операция OR|__xor__()Поразрядная исключающая опе- рация OR^__neg__()ОтрицаниеУнарный -, как в -42__pos__()ТождествоУнарный +, как в +42__abs__()Абсолютное значение (модуль)abs()__invert__()Поразрядное инвертирование__complex__()Комплексная форма числа complex()__int__()Целая форма числа int()__float__()Форма числа с плавающей точкой float()__bool__()Логическая форма bool()__round__()Округление round()__trunc__()Целая часть math.trunc()__floor__()Округление в меньшую сторону math.floor()__ceil__()Округление в большую сторону math.ceil() Dunder-методы Python373Некоторые из этих методов актуальны для нашего класса WizCoin. Попробуйте напи- сать собственные реализации методов __sub__(), __pow__(), __int__(), __float__()и __bool__(). Пример реализации доступен на https://autbor.com/wizcoinfull. Полное описание числовых dunder-методов в документации Python доступно на https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types.Числовые dunder-методы позволяют использовать объекты ваших классов со встроенными математическими операторами Python. Если вы пишете методы с именами вида multiplyBy(), convertToInt() и т. д., описывающими задачу, которая выполняется существующим оператором или встроенной функцией, используйте числовые dunder-методы (а также отраженные dunder-методы и dunder-методы присваивания на месте, описанные в следующих двух разделах).Отраженные числовые dunder-методыPython вызывает числовые dunder-методы, когда объект находится в левой части математического оператора. Но если объект располагается в правой части мате- матического оператора, то вызываются отраженные числовые dunder-методы (их также называют обратными числовыми dunder-методами).Отраженные числовые dunder-методы полезны, потому что программисты, ис- пользующие ваш класс, не всегда будут записывать объект в левой части оператора, что может привести к непредвиденному поведению. Для примера рассмотрим, что произойдет, если purse содержит объект WizCoin, а Python вычисляет выражение 2*purse, когда purse находится в правой части оператора.1. Так как 2 является целым числом, вызывается метод __mul__() класса int, которому в параметре other передается purse2. Метод __mul__() класса int не знает, как обрабатывать объекты WizCoin, по- этому он возвращает NotImplemented3. Пока Python не выдает ошибку TypeError. Так как purse содержит объект WizCoin, вызывается метод __rmul__() класса WizCoin, которому в параметре other передается 2 4. Если __rmul__() возвращает NotImplemented, Python выдает ошибку TypeError. В противном случае __rmul__() возвращает объект, полученный в результате вычисления выражения 2*purseА вот выражение purse*2, в котором purse находится в левой части оператора, работает иначе.1. Так как purse содержит объект WizCoin, вызывается метод __mul__() класса WizCoin, которому в параметре other передается 2 374 Глава 17.ООП в Python: свойства и dunder-методы2. Метод __mul__() создает новый объект WizCoin и возвращает его.3. Этот возвращенный объект — то, что возвращает выражение purse*2Числовые dunder-методы и отраженные числовые dunder-методы имеют одина- ковый код, если они обладают свойством коммутативности. Коммутативные операции (такие как сложение) дают одинаковый результат при записи в прямом и обратном направлении: 3+2 — то же самое, что 2+3Но существуют и другие операции, которые коммутативными не являются: 3—2и 2—3 дают разные результаты. Любая коммутативная операция может просто вы- зывать исходный числовой dunder-метод каждый раз, когда вызывается отраженный числовой dunder-метод. Например, добавьте следующий фрагмент в конец файла wizcoin.py, чтобы определить отраженный числовой dunder-метод для операции умножения:--snip-- def __rmul__(self, other): """Умножает количество монет на неотрицательное целое число.""" return self.__mul__(other)Умножение целого числа на объект WizCoin коммутативно: 2*purse — то же самое, что purse*2. Вместо того чтобы копировать и вставлять код из __mul__(), мы про- сто вызываем self.__mul__() и передаем параметр otherПосле обновления файла wizcoin.py проверьте, как работает отраженный dunder- метод умножения. Для этого введите следующий фрагмент в интерактивную обо- лочку:>>> import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10) >>> purse * 10 # Вызывает __mul__() с параметром `other`, равным 10.WizCoin(20, 50, 100)>>> 10 * purse # Вызывает __rmul__() с параметром `other`, равным 10.WizCoin(20, 50, 100)Помните, что в выражении 10*purse Python сначала вызывает метод __mul__()класса int, чтобы узнать, могут ли целые числа умножаться на объекты WizCoinКонечно, встроенный класс int ничего не знает о создаваемых нами классах, по- этому он возвращает NotImplemented. Это значение сигнализирует Python о том, чтобы следующим для обработки операции был вызван метод __rmul__() класса WizCoin (если он существует). Если вызовы __mul__() класса int и __rmul__() класса WizCoin вернут NotImplemented, Python выдает исключение TypeErrorТолько объекты WizCoin способны суммироваться друг с другом. Это гарантирует, что метод __add__() первого объекта WizCoin обработает операцию, поэтому реали- зовать __radd__() не нужно. Например, в выражении purse+tipJar метод __add__() Dunder-методы Python375для объекта purse вызывается с передачей tipJar в параметре other. Так как этот вызов не возвращает NotImplemented, Python не пытается вызвать метод __radd__()объекта tipJar с передачей purse в параметре otherВ табл. 17.2 приведен полный список доступных отраженных dunder-методов.Таблица 17.2. Отраженные числовые dunder-методыDunder-методОперацияОператор или встроенная функция__radd__()Сложение+__rsub__()Вычитание-__rmul__()Умножение*__rmatmul__()Матричное умножение (в Python 3.5 и выше)@__rtruediv__()Деление/__rfloordiv__()Целочисленное деление//__rmod__()Остаток%__rdivmod__()Деление с остатком divmod()__rpow__()Возведение в степень**, pow()__rlshift__()Сдвиг влево>>__rrshift__()Сдвиг вправо<<__rand__()Поразрядная операция AND&__ror__()Поразрядная операция OR|__rxor__()Поразрядная исключающая операция OR^Полное описание отраженных dunder-методов доступно в документации Python на https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types.Dunder-методы присваивания на месте (in-place)Числовые и отраженные dunder-методы всегда создают новые объекты (вместо из- менения объектов на месте). Dunder-методы присваивания на месте, вызываемые расширенными операторами присваивания (такими как += и *=), изменяют объект на месте без создания нового объекта. (У этого правила есть исключение, о котором я расскажу в конце раздела.) Имена таких dunder-методов начинаются с i (напри- мер, __iadd__() и __imul__() для операторов += и *= соответственно). 376 Глава 17.ООП в Python: свойства и dunder-методыНапример, при выполнении кода Python purse*=2 мы не предполагаем, что метод __imul__() класса WizCoin создаст и вернет новый объект WizCoin с вдвое большим количеством монет, а затем присвоит его переменной purse. Вместо этого метод __imul__() изменяет существующий объект WizCoin в purse, чтобы он содержал вдвое большее количество монет. Это тонкое, но важное отличие, которое необхо- димо учитывать, если ваши классы должны перегружать расширенные операторы присваивания.Наши объекты WizCoin уже перегружают операторы + и *, поэтому определим dunder-методы __iadd__() и __imul__(), чтобы они также перегружали операторы += и *=. В выражениях purse+=tipJar и purse*=2 вызываются методы __iadd__()и __imul__(), при этом в параметре other передаются tipJar и 2 соответственно. Добавьте следующий фрагмент в конец файла wizcoin.py:--snip-- def __iadd__(self, other): """Прибавляет монеты из другого объекта WizCoin к этому объекту.""" if not isinstance(other, WizCoin): return NotImplemented # Объект `self` изменяется на месте: self.galleons += other.galleons self.sickles += other.sickles self.knuts += other.knuts return self # Dunder-методы присваивания на месте # почти всегда возвращают self. def __imul__(self, other): """Умножает galleons, sickles и knuts этого объекта на неотрицательное целое число.""" if not isinstance(other, int): return NotImplemented if other < 0: raise WizCoinException('cannot multiply with negative integers') # Класс WizCoin создает изменяемые объекты. НЕ СОЗДАВАЙТЕ новый # объект, как в следующем закомментированном коде: # return WizCoin(self.galleons * other, self.sickles * other, # self.knuts * other) # Объект `self` изменяется на месте: self.galleons *= other self.sickles *= other self.knuts *= other return self # Dunder-методы присваивания на месте # почти всегда возвращают self.Объекты WizCoin могут использовать оператор += с другими объектами WizCoin и оператор *= с положительными целыми числами. Заметим, что после проверки Dunder-методы Python377параметра other методы присваивания на месте изменяют объект self, вместо того чтобы создавать новый объект WizCoin. Чтобы увидеть, как расширенные опера- торы присваивания изменяют объекты WizCoin на месте, введите следующий код в интерактивной оболочке:>>> import wizcoin>>> purse = wizcoin.WizCoin(2, 5, 10)>>> tipJar = wizcoin.WizCoin(0, 0, 37)>>> purse + tipJar ❶WizCoin(2, 5, 46) ❷>>> purseWizCoin(2, 5, 10)>>> purse += tipJar ❸>>> purseWizCoin(2, 5, 47)>>> purse *= 10 ❹>>> purseWizCoin(20, 50, 470)Оператор +❶ вызывает dunder-методы __add__() или __radd__() для создания и воз- вращения новых объектов ❷. Исходные объекты, с которыми работал оператор +, остаются без изменений. Dunder-методы присваивания на месте ❸ и ❹ должны изменять объект на месте, при условии что объект является изменяемым (то есть это объект, значение которого может изменяться). Исключение делается для неиз- меняемых объектов, так как такие объекты по определению не могут изменяться. В таком случае dunder-метод присваивания на месте должен создать и вернуть новый объект, как и числовые и отраженные dunder-методы.Мы не сделали атрибуты galleons, sickles и knuts доступными только для чтения; это означает, что они могут изменяться. Таким образом, объекты WizCoin являются изменяемыми. Большинство классов, которые вы напишете, будут создавать изме- няемые объекты, поэтому вам стоит проектировать dunder-методы присваивания на месте, так чтобы они изменяли объект на месте.Если вы не реализуете dunder-метод присваивания на месте, Python вызовет число- вой dunder-метод. Например, если в классе WizCoin отсутствует метод __imul__(), выражение purse*=10 вызовет __mul__() и присвоит возвращаемое значение пере- менной purse. Так как объекты WizCoin являются изменяемыми, это неожиданное поведение способно порождать коварные ошибки.1   ...   32   33   34   35   36   37   38   39   40

Глава 14. Проекты для тренировки. Я расскажу о нескольких полезных методах, а затем с их помощью мы напишем пару игр командной строки: головоломку с перемещением дисков «Ханойская башня» и классическую игру «Четыре в ряд» для двух игроков.
Часть III. Объектно-ориентированный Python
Глава 15. Объектно-ориентированное программирование и классы. Здесь мы определим роль объектно-ориентированного программирования (ООП), которое часто понимают неправильно. Многие разработчики злоупотребляют средствами ООП в своем коде, потому что считают, что так поступают все, но это лишь усложняет исходный код. Вы научитесь использовать классы, но что еще важнее, вы узнаете, когда следует и когда не следует их применять.

Путешествие в мир программирования
23
Глава 16. Объектно-ориентированное программирование и наследование.
Наследование классов и его полезность для повторного использования кода.
Глава 17. ООП в Python: свойства и dunder-методы. Средства объектно- ориентированного проектирования, специфические для Python: свойства, dunder-методы и перегрузка операторов.
Путешествие в мир программирования
Попытки новичка стать профессиональным программистом часто выглядят чем-то вроде попытки напиться из пожарного шланга. При таком изобилии ресурсов вы начинаете беспокоиться, что тратите время на неэффективные учебники.
После того как вы прочтете эту книгу (или даже во время чтения), я рекомендую ознакомиться со следующими вводными материалами.
«Python Crash Course» (No Starch Press, 2019) Эрика Мэтиза (Eric Matthes)
1
— книга для начинающих, но благодаря ее ориентации на конкретные проекты даже опытный программист почувствует вкус использования библиотек
Python Pygame, matplotlib и Django.
В своей книге «Impractical Python Projects» (No Starch Press, 2018) Ли Воган
(Lee Vaughan)
2
проектным методом совершенствует ваши навыки Python.
Под его руководством вы построите несколько занимательных программ, которые станут для вас отличной тренировкой.
«Serious Python» (No Starch Press, 2018) Джульена Данжу (Julien Danjou)
3
описывает, что надо предпринять любителю-энтузиасту для превращения в квалифицированного разработчика, который применяет передовые прак- тики и пишет хорошо масштабируемый код.
Впрочем, технические аспекты Python — всего лишь одна из его сильных сторон.
Язык программирования сформировал разностороннее сообщество, усилиями которого была создана удобная, доступная документация и система поддержки, превосходящая любую другую экосистему. На ежегодной конференции PyCon наряду со многими региональными конференциями предлагаются многочислен- ные лекции для программистов разных уровней квалификации. Организаторы
1
Мэтиз Э. Изучаем Python: программирование игр, визуализация данных, веб-прило жения.
3-е изд. — СПб.: Питер.
2
Воган Л. «Непрактичный» Python. Занимательные проекты для тех, кто хочет поумнеть.
3
Данжу Дж. Путь Python. Черный пояс по разработке, масштабированию, тестированию и развертыванию. — СПб.: Питер.


24
Введение
PyCon предоставляют бесплатный доступ к этим материалам на https://pyvideo.
org/. На странице
Tags можно легко найти доклады по темам, интересующим вас.
Если вы захотите глубже изучить расширенные возможности синтаксиса и стан- дартной библиотеки Python, я рекомендую следующие книги:
«Effective Python» (Addison-Wesley Professional, 2019) Бретта Слаткина
(Brett Slatkin)
1
— впечатляющая подборка передовых практик и языковых средств Python;
«Python Cookbook» (O’Reilly Media, 2013) Дэвида Бизли (David Beazley) и Брайана К. Джонса (Brian K. Jones)
2
— обширный список фрагментов кода, которые помогут обновить репертуар любого начинающего разработчика
Python;
«Fluent Python» (O’Reilly Media, 2021) Лучано Рамальо (Luciano Ramalho)
3
— капитальный труд для исследования тонкостей языка Python. И хотя почти
800-страничный том выглядит устрашающе, вы не пожалеете о потраченном времени.
Желаю удачи в вашем путешествии в мир программирования. Итак, за дело!
1
Слаткин Б. Секреты Python. 59 рекомендаций по написанию эффективного кода.
2
Бизли Д., Джонс Б. Python. Книга рецептов.
3
Рамальо Л. Python. К вершинам мастерства.

ЧАСТЬ I
ПЕРВЫЕ ШАГИ

1
Обработка ошибок и обращение
за помощью
Пожалуйста, не наделяйте компьютеры человеческими ка- чествами; их это очень сильно раздражает. Когда компьютер выдает сообщение об ошибке, это происходит вовсе не по- тому, что он на вас обиделся. Компьютеры — самые сложные инструменты, с которыми большинству из нас придется рабо- тать в жизни, и все же это всего лишь инструменты.
Тем не менее легко обвинить в своих ошибках инструменты. Так как учиться про- граммировать многим из нас в основном приходится самостоятельно, часто мы чувствуем себя неудачниками — приходится неоднократно обращаться к интернету за информацией, хотя мы изучаем Python уже несколько месяцев. Но даже про- фессиональные разработчики обращаются к интернету или документации, чтобы найти ответы на вопросы по поводу программного обеспечения.
Если вы не располагаете финансовыми или какими-то другими ресурсами, чтобы нанять преподавателя, который ответит на ваши вопросы, выбора не остается: ком- пьютер, поисковые системы в интернете и сила духа. К счастью, на ваши вопросы почти наверняка уже кто-нибудь отвечал. Умение самостоятельно искать ответы для программиста гораздо важнее знания любых алгоритмов или структур данных.
В этой главе мы расскажем, как развить этот важнейший навык.
Как понять сообщения об ошибках Python
Когда программисты сталкиваются с лавиной технической информации в сообще- ниях об ошибках, им прежде всего хочется полностью ее проигнорировать. Но эти


Как понять сообщения об ошибках Python
27
сообщения содержат ответ, что же не так с вашей программой. Процесс поиска информации состоит из двух этапов: анализа трассировки и поиска текста сообще- ния об ошибке в интернете.
Анализ трассировки
Программы Python аварийно завершаются, когда в коде возникает исключение, не обработанное командой except
. В таком случае Python выдает сообщение об исключении и трассировку (также называемую трассировкой стека). Трассировка показывает, в какой точке вашей программы произошло исключение и последова- тельность вызовов функций, которые к этой точке привели.
Чтобы потренироваться в чтении трассировки, введите следующую программу
(она содержит ошибку) и сохраните ее под именем abcTraceback.py
. Номера строк приведены только для удобства, они не являются частью программы.
1. def a():
2. print('Start of a()')
3. b() # Вызов b().

4.
5. def b():
6. print('Start of b()')
7. c() # Вызов c().

8.
9. def c():
10. print('Start of c()')
11. 42 / 0 # Порождает ошибку деления на нуль.

12.
13. a() # Вызов a().
В этой программе функция a()
вызывает b()

, которая вызывает функцию c()

Внутри c()
выражение
42
/
0

вызывает ошибку деления на нуль. Если вы запу- стите эту программу, результат должен выглядеть так:
Start of a()
Start of b()
Start of c()
Traceback (most recent call last):
File "abcTraceback.py", line 13, in
a() # Call a().
File "abcTraceback.py", line 3, in a b() # Call b().
File "abcTraceback.py", line 7, in b c() # Call c().
File "abcTraceback.py", line 11, in c
42 / 0 # Порождает ошибку деления на нуль.
ZeroDivisionError: division by zero

28
Глава 1.Обработка ошибок и обращение за помощью
Проанализируем трассировку строку за строкой, начиная с этой:
Traceback (most recent call last):
Сообщение указывает, что далее идет трассировка. Текст most recent call last означает, что вызовы функций перечисляются по порядку, начиная с первой и за- канчивая самой последней.
В следующей строке приводится вызов первой функции в трассировке:
File "abcTraceback.py", line 13, in
a() # Вызов a().
Следующие две строки содержат сводку кадра; в них выводится информация, храня- щаяся внутри объекта кадра. При вызове функции данные локальных переменных, а также точка в коде, в которую должно быть возвращено управление после вызова функции, сохраняются в объекте кадра. В объектах кадра хранятся локальные переменные и другие данные, связанные с вызовами функций. Объекты кадров создаются при вызове функции и уничтожаются при возвращении управления.
В трассировке выводится сводка для каждого кадра на пути к фатальному сбою. Мы видим, что вызов функции в строке 13 находится в строке abcTraceback.py
, а текст

сообщает, что строка находится в глобальной области видимости. Далее строка 13 выводится с отступом из двух пробелов.
В следующих четырех строках выводятся сводки следующих двух кадров:
File "abcTraceback.py", line 3, in a b() # Call b().
File "abcTraceback.py", line 7, in b c() # Call c().
Из текста line
3,
in a
мы видим, что функция b()
была вызвана в строке 3 внутри функции a()
, что привело к вызову функции c()
в строке 7 внутри функции b()
Обратите внимание: вызовы print()
в строках 2, 6 и 10 в трассировке не выводят- ся, несмотря на то что они были выполнены до вызовов функций. В трассировку включаются только строки с вызовами функций, приведших к исключению.
В последней сводке кадра выводится строка, которая стала причиной необработан- ного исключения. За ней следует имя и сообщение исключения:
File "abcTraceback.py", line 11, in c
42 / 0 # Порождает ошибку деления на нуль.
ZeroDivisionError: division by zero
Обратите внимание: номер строки в трассировке указывает, где была обнаружена ошибка. Источник ошибки находится где-то до этой строки.


Как понять сообщения об ошибках Python
29
Сообщения об ошибках часто оказываются слишком короткими и невразуми- тельными; три слова «деление на нуль» ничего не скажут тому, кто не знает, что деление на нуль невозможно с точки зрения математики, и это распространенная программная ошибка. В этой программе найти ошибку несложно. Если взглянуть на строку кода в сводке кадра, становится ясно, что ошибка деления на нуль воз- никла из-за выражения
42
/
0
Но рассмотрим более сложный случай. Введите следующий фрагмент в текстовом редакторе и сохраните его под именем zeroDivideTraceback.py
:
def spam(number1, number2):
return number1 / (number2 - 42)
spam(101, 42)
При запуске этой программы результат должен выглядеть так:
Traceback (most recent call last):
File "zeroDivideTraceback.py", line 4, in
spam(101, 42)
File "zeroDivideTraceback.py", line 2, in spam return number1 / (number2 - 42)
ZeroDivisionError: division by zero
Сообщение об ошибке то же, но деление на нуль в return number1
/
(number2
-
42)
не столь очевидно. По оператору
/
можно заключить, что выполняется деление, а выражение
(number2
-
42)
должно быть равно 0. Отсюда можно сделать вывод, что в функции spam()
происходит сбой, когда параметр number2
равен 42.
Иногда трассировка может сообщить, что ошибка находится в строке, расположен- ной после причины ошибки. Например, в следующей программе в первой строке отсутствует закрывающая круглая скобка:
print('Hello.'
print('How are you?')
Но из сообщения об ошибке для этой программы следует, что проблема находится во второй строке:
File "example.py", line 2
print('How are you?')
^
SyntaxError: invalid syntax
Дело в том, что интерпретатор Python не заметил синтаксическую ошибку до того, как была прочитана вторая строка. Трассировка может сообщить, где обнаружена проблема, но это место не всегда то самое, где находится реальная причина ошибки.

30
Глава 1.Обработка ошибок и обращение за помощью
Если сводка кадра не дает достаточной информации для обнаружения ошибки или если истинная причина ошибки находится в предыдущей строке, которая не приводится в трассировке, вам придется выполнять программу в пошаговом режиме в отладчике или просматривать сообщения в журнале. Все это потребует значительного времени. Поиск сообщения об ошибке в интернете иногда намного быстрее предоставит необходимую информацию о проблеме.
Поиск сообщений об ошибках
Часто сообщения об ошибках настолько коротки, что они даже не содержат полных предложений. Программисты регулярно сталкиваются с ними, поэтому они должны быть напоминаниями, а не исчерпывающими объяснениями. Если вы встречаете сообщение об ошибке впервые, скопируйте его в поисковую систему. С большой долей вероятности вы получите подробное описание того, что означает ошибка и каковы ее вероятные причины.
На рис. 1.1 показаны результаты поиска по строке
python "ZeroDivisionError:
division by zero". Заключение сообщения об ошибке в кавычки помогает найти точную фразу, а добавление слова python способствует сужению поиска.
Рис. 1.1. Копирование сообщения об ошибке в поисковую систему ускорит поиск объяснений и решений


Предотвращение ошибок при помощи статического анализатора
31
Поиск сообщений об ошибках — вполне нормальное дело. Трудно ожидать, чтобы разработчик запомнил все возможные сообщения об ошибках для языка програм- мирования. Для профессионалов поиск ответов в интернете давно стал частью повседневной работы.
Возможно, из поиска стоит исключить фрагменты, относящиеся непосредственно к вашему коду. Для примера рассмотрим следующее сообщение об ошибке:
>>> print(employeRecord)
Traceback (most recent call last):
File "", line 1, in
NameError: name 'employeRecord' is not defined

>>> 42 - 'hello'
Traceback (most recent call last):
File "", line 1, in
TypeError: unsupported operand type(s) for -: 'int' and 'str'

В этом примере допущена опечатка в имени переменной employeRecord
, что приво- дит к ошибке

. Так как идентификатор employeRecord в сообщении
NameError:
name
'employeRecord'
is not defined связан с вашим кодом, при поиске стоит использо- вать строку python
"NameError:
name"
"is not defined"
. В последней строке часть 'int'
and
'str'
в сообщении об ошибке

явно относится к значениям
42
и 'hello'
, так что усечение строки поиска до python
"TypeError:
unsupported operand type(s)
for"
позволит исключить фрагменты, относящиеся к вашему коду. Если поиск не даст полезных результатов, попробуйте включить полное сообщение об ошибке.
Предотвращение ошибок при помощи
статического анализатора
Лучший способ исправления ошибок — не допускать их. Статические анализаторы
(linters) — приложения, которые анализируют исходный код и предупреждают вас о потенциальных ошибках. Хотя статический анализатор не обнаруживает все ошибки, статический анализ (проверка исходного кода без его выполнения) помогает выявить типичные ошибки, возникающие из-за опечаток. (В главе 11 рассказано, как использовать рекомендации типов для статического анализа.) Мно- гие текстовые редакторы и интегрированные среды разработки (IDE) включают статический анализатор, который работает в фоновом режиме и способен выявлять проблемы в реальном времени (рис. 1.2).
Почти моментальные уведомления от статического анализатора сильно повышают эффективность программирования. Без них вам пришлось бы запустить программу, увидеть произошедший сбой, прочитать трассировку, а затем найти строку в исход- ном коде, чтобы исправить ошибку. А если вы допустите несколько опечаток, то вам придется запускать цикл «запуск — исправление» для каждой из них, поскольку