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

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

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

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

Добавлен: 03.12.2023

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

Скачиваний: 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


Частые ошибки при использовании терминов
153
значение (которое всегда равно
None
), может передаваться в аргументе другим функциям и может присваиваться переменным. С командами все эти действия невозможны. Впрочем, круглые скобки допустимо использовать и в Python 2, как показывает следующий пример в интерактивной оболочке:
>>> print 'Hello, world!' # Выполняется в Python 2
Hello, world!
>>> print('Hello, world!') # Выполняется в Python 2

Hello, world!
И хотя все выглядит как вызов функции

, на самом деле это команда print со строковым значением, заключенным в круглые скобки, — по аналогии с тем, как присваивание spam
=
(2
+
2)
эквивалентно spam
=
2
+
2
. В Python 2 и 3 можно пере- дать несколько значений команде print или функции print()
соответственно.
В Python 3 это выглядит так:
>>> print('Hello', 'world') # Выполняется в Python 3
Hello world
Но использование того же кода в Python 2 будет интерпретировано как передача кортежа с двумя строковыми значениями в команде print
, в результате чего вы получите следующий вывод:
>>> print('Hello', 'world') # Выполняется в Python 2
('Hello', 'world')
Между командой и выражением, состоящим из вызова функции, существуют не- очевидные, но реальные различия.
Блок, секция и тело
Термины «блок», «секция» и «тело» часто используются как синонимы для обозна- чения группы инструкций Python. Блок (block) начинается с отступа и завершается там, где отступ возвращается к предыдущему уровню. Например, код, следующий за командой if или for
, называется блоком команды. Новый блок должен следовать за командами, завершающимися двоеточием (такими как if
, else
, for
, while
, def
, class и т. д.).
Впрочем, Python допускает однострочные блоки. Это допустимый, хотя и не реко- мендуемый синтаксис Python:
if name == 'Zophie': print('Hello, kitty!')
В блок команды if можно включить несколько команд, разделяя их точкой с за- пятой
;
:
if name == 'Zophie': print('Hello, kitty!'); print('Do you want a treat?')

154
Глава 7.Жаргон программистов
Однострочный синтаксис не может использоваться с другими командами, которым требуется новый блок. Следующий фрагмент не является допустимым в Python:
if name == 'Zophie': if age < 2: print('Hello, kitten!')
Он недопустим, потому что, если в следующей строке будет располагаться команда else
, мы не сможем однозначно сказать, к какой команде if она относится.
В официальной документации Python предпочтение отдается термину секция
(clause) вместо «блок» (https://docs.python.org/3/reference/compound_stmts.html).
Следующий код является секцией:
if name == 'Zophie':
print('Hello, kitty!')
print('Do you want a treat?')
Команда if является заголовком секции, а два вызова print()
, вложенных в if
, являются телом (body) секции. В официальной документации Python термином
«блок» обозначается фрагмент кода Python, выполняемый как единое целое — например, модуль, функция или определение класса (https://docs.python.org/3/
reference/executionmodel.html).
Переменные и атрибуты
Переменные (variables) — имена, ссылающиеся на объекты. Атрибутом (attribute), согласно официальной документации, называется «любое имя, следующее за точ- кой» (https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces).
Атрибуты связываются с объектами (имя перед точкой). Например, введите сле- дующий фрагмент в интерактивной оболочке:
>>> import datetime
>>> spam = datetime.datetime.now()
>>> spam.year
2018
>>> spam.month
1
В этом примере spam
— переменная, содержащая объект datetime
(возвращаемый вызовом datetime.datetime.now()
), а year и month
— атрибуты этого объекта.
Даже в случае, скажем, sys.exit()
функция exit()
считается атрибутом объекта модуля sys
В других языках атрибуты называются полями (fields), свойствами (properties) или
компонентными переменными (member variables).


Частые ошибки при использовании терминов
155
Функции и методы
Функция (function) — совокупность кода, выполняемого при вызове. Методом
(method) называется функция (или вызываемый объект, см. следующий раздел), свя- занная с классом, — по аналогии с тем, как атрибут является переменной, связанной с объектом. К функциям относятся встроенные функции или функции, связанные с модулем. Например, введите следующий фрагмент в интерактивной оболочке:
>>> len('Hello')
5
>>> 'Hello'.upper()
'HELLO'
>>> import math
>>> math.sqrt(25)
5.0
В этом примере len()
— функция, а upper()
— метод строк. Методы также считаются атрибутами объектов, с которыми они связываются. Обратите внимание: точка не обязательно означает, что перед вами именно метод, а не функция. Функция sqrt()
связывается с именем math
, но это модуль, а не класс.
Итерируемые объекты и итераторы
Циклы for языка Python весьма гибки. Команда for i
in range(3):
выполняет блок кода трижды. Не стоит полагать, что вызов range(3)
— это такой способ при- казать циклу for
«выполнить код три раза», принятый в Python. Вызов range(3)
возвращает объект диапазона, по аналогии с тем, как вызов list('cat')
возвращает объект списка. Оба объекта являются примерами итерируемых объектов (iterables).
Итерируемые объекты используются в циклах for
. Введите следующий фрагмент в интерактивной оболочке, чтобы увидеть, как цикл for перебирает объект диа- пазона и объект списка:
>>> for i in range(3):
... print(i) # тело цикла for
0 1
2
>>> for i in ['c', 'a', 't']:
... print(i) # тело цикла for
c a
t

156
Глава 7.Жаргон программистов
К категории итерируемых объектов также относятся все разновидности последо- вательностей (например, диапазоны, списки, кортежи и строки), а также ряд объ- ектов-контейнеров (словари, множества и объекты файлов).
Тем не менее в этих примерах циклов for происходит нечто большее. Во внутрен- ней реализации Python вызывает встроенные функции iter()
и next()
для цикла for
. При использовании цикла for итерируемые объекты передаются встроенной функции iter()
, которая возвращает объекты-итераторы (iterators). И если итерируемый объект содержит элементы, итератор следит за тем, какой элемент будет использован в цикле следующим. При каждой итерации цикла объект- итератор передается встроенной функции next()
для получения следующего элемента в итерируемом объекте. Вы можете вызвать функции iter()
и next()
вручную, чтобы увидеть, как работают циклы for
. Введите следующий фрагмент в интерактивной оболочке, чтобы выполнить те же инструкции, что и в преды- дущем примере цикла:
>>> iterableObj = range(3)
>>> iterableObj
range(0, 3)
>>> iteratorObj = iter(iterableObj)
>>> i = next(iteratorObj)
>>> print(i) # тело цикла for
0
>>> i = next(iteratorObj)
>>> print(i) # тело цикла for
1
>>> i = next(iteratorObj)
>>> print(i) # тело цикла for
2
>>> i = next(iteratorObj)
Traceback (most recent call last):
File "", line 1, in
StopIteration

Обратите внимание: если вызвать next()
после того, как был возвращен последний элемент в итерируемом объекте, Python выдает исключение
StopIteration

. Вме- сто того чтобы аварийно завершать программы с сообщением об ошибке, циклы for в Python перехватывают это исключение, чтобы знать, когда следует остановить перебор.
Итератор способен перебрать все элементы итерируемого объекта только один раз, так же, как можно только один раз вызвать open()
и readlines()
для чтения со- держимого файла, после чего вам придется открывать файл заново для чтения его содержимого. Если потребуется снова перебрать итерируемый объект, необходимо снова вызвать iter()
для создания другого итератора. Вы можете создать столько объектов-итераторов, сколько потребуется; каждый объект будет отслеживать


Частые ошибки при использовании терминов
157
следующий возвращаемый элемент независимо от других. Чтобы понять, как это работает, введите следующий фрагмент в интерактивной оболочке:
>>> iterableObj = list('cat')
>>> iterableObj
['c', 'a', 't']
>>> iteratorObj1 = iter(iterableObj)
>>> iteratorObj2 = iter(iterableObj)
>>> next(iteratorObj1)
'c'
>>> next(iteratorObj1)
'a'
>>> next(iteratorObj2)
'c'
Помните, что итерируемые объекты передаются в аргументах функции iter()
, тогда как объект, возвращаемый вызовами iter()
, является итератором. Объек- ты-итераторы передаются функции next()
. Когда вы создаете собственные типы данных командами class
, вы можете реализовать специальные методы
__iter__()
и
__next__()
, чтобы ваши объекты можно было использовать в циклах for
Синтаксические ошибки, ошибки времени выполнения
и семантические ошибки
Известно много способов классификации ошибок. Но на верхнем уровне ошибки программирования можно разделить на три вида: синтаксические, ошибки времени выполнения и семантические.
Cинтаксис (syntax) — это набор правил для создания инструкций, действительных в заданном языке программирования. Синтаксическая ошибка (например, про- пущенная круглая скобка, точка вместо запятой или другая опечатка) немедленно приводит к ошибке
SyntaxError
. Синтаксические ошибки также называются ошиб-
ками разбора (parsing errors); они происходят, когда интерпретатор Python не может разобрать текст исходного кода в действительные инструкции. В естественном языке такие ошибки можно сравнить с неправильными грамматическими кон- струкциями или бессмысленными цепочками слов. Компьютерам нужны предельно точные инструкции; они не могут прочитать мысли программиста, чтобы понять, что должна делать программа. Из-за этого программа с синтаксической ошибкой даже не запустится.
Ошибка времени выполнения (runtime error) возникает, когда работающей програм- ме не удается выполнить некоторую операцию — скажем, открыть несуществую- щий файл или разделить число на нуль. В естественном языке ошибку времени выполнения можно сравнить с невыполнимой инструкцией типа «нарисуй квадрат с тремя сторонами». Если ошибка времени выполнения не будет исправлена,


158
Глава 7.Жаргон программистов программа аварийно завершается с выдачей трассировки. Но ошибки времени выполнения можно перехватить командами try
- except
, которые выполняют код обработки ошибок. Например, введите следующий фрагмент в интерактивной оболочке:
>>>
1   ...   11   12   13   14   15   16   17   18   ...   40

slices = 8
>>> eaters = 0
>>> print('Each person eats', slices / eaters, 'slices.')
Этот код при запуске выдает трассировку:
Traceback (most recent call last):
File "
", line 1, in
print('Each person eats', slices / eaters, 'slices.')
ZeroDivisionError: division by zero
Полезно помнить, что номер строки в трассировке относится только к точке, в ко- торой интерпретатор Python обнаружил ошибку. Истинная причина ошибки может находиться в предыдущей строке кода или даже намного ранее в программе.
Синтаксические ошибки в исходном коде перехватываются интерпретатором до запуска программы, но синтаксические ошибки также происходят во время вы- полнения. Функция eval()
может получить строку кода Python и выполнить его, что приводит к ошибке
SyntaxError во время выполнения. Например, в команде eval('print("Hello,
world)')
пропущена закрывающая двойная кавычка, но про- грамма не обнаружит этот факт, пока не будет вызвана функция eval()
Семантические (semantic) ошибки, также называемые логическими (logical), коварнее предыдущих. Семантические ошибки не порождают сообщений об ошибках или сбоев, но компьютер выполняет инструкции способом, который не предполагался программистом. На естественном языке семантическую ошибку можно сравнить с распоряжением: «Купи в магазине пакет молока, а если есть яйца — купи десяток».
Тогда компьютер купит 11 пакетов молока, потому что в магазине есть яйца. Хоро- шо это или плохо, но компьютеры делают в точности то, что вы им приказываете.
Например, введите следующий фрагмент в интерактивной оболочке:
>>> print('The sum of 4 and 2 is', '4' + '2')
Вы получите следующий результат:
The sum of 4 and 2 is 42
Очевидно,
42
не является правильным ответом. Но стоит заметить, что программа была выполнена без сбоев. Так как оператор Python
+
складывает целые числа и вы- полняет конкатенацию строковых значений, ошибочное использование строковых значений '4'
и '2'
вместо целых чисел привело к такой работе программы.

Частые ошибки при использовании терминов
159
Параметры и аргументы
Параметры (parameters) — имена переменных, заключенные в круглые скобки в команде def
. Аргументами (arguments) называются значения, передаваемые при вызове функции, которые затем присваиваются параметрам. Например, введите следующий фрагмент в интерактивной оболочке:
>>> def greeting(name, species):

... print(name + ' is a ' + description)
>>> greeting('Zophie', 'cat')

Zophie is a cat
В команде def имена name и species
— параметры

. В вызове функции 'Zophie'
и 'cat'
— аргументы

. Эти два термина часто путают. Помните, что параметры и аргументы — всего лишь другие названия для переменных и значений, когда они используются в этом контексте.
Явные и неявные преобразования типов
Объект одного типа можно преобразовать в объект другого типа. Например, int('42')
преобразует строку '42'
в целое число
42
. На самом деле строковый объект '42'
не преобразуется, так как функция int()
создает новый объект целого числа на основании исходного объекта. Явные преобразования правильнее на- зывать приведением типа (casting), хотя программисты все равно часто называют этот процесс преобразованием (converting) объекта.
Python часто выполняет неявные преобразования типов (coercion) — например, когда вычисление выражения
2
+
3.0
дает результат
5.0
. Значения (такие как
2
и
3.0
) преобразуются к общему типу данных, с которым может работать оператор.
Неявные преобразования часто приводят к непредвиденным результатам. Логиче- ские значения
True и
False в Python могут быть преобразованы в целые числа
1
и
0
соответственно. Хотя в реальном коде логические значения никогда не записыва- ются в таком виде, это означает, что выражение
True
+
False
+
True эквивалентно
1
+
0
+
1
и дает результат
2
. Зная этот факт, можно подумать, что передача списка ло- гических значений функции sum()
позволит эффективно подсчитать значения
True в списке. Но как выясняется, вызов метода count()
для списка работает быстрее.
Свойства и атрибуты
Во многих языках термины «свойство» (property) и «атрибут» (attribute) ис- пользуются как синонимы, но в Python эти слова имеют разный смысл. Атрибут, как объяснялось в подразделе «Переменные и атрибуты» на с. 154, представляет

160
Глава 7.Жаргон программистов собой имя, связанное с объектом. К числу атрибутов относятся компонентные переменные и методы объекта.
В других языках, таких как Java, классы могут содержать get- и set-методы. Вместо того чтобы напрямую присваивать атрибуту (возможно, недействительное) значе- ние, программа должна вызвать set-метод для этого атрибута.
Код в set-методе может гарантировать, что компонентной переменной будут при- сваиваться только действительные значения. Get-метод читает значение атрибута.
Если атрибуту, допустим, присвоено имя accountBalance
, set- и get-методам обычно присваиваются имена setAccountBalance()
и getAccountBalance()
соответственно.
В Python свойства позволяют программистам использовать get- и set-методы с более чистым синтаксисом. В главе 17 я более подробно расскажу о свойствах Python.
Байт-код и машинный код
Исходный код компилируется в так называемый машинный код (machine code) — инструкции, которые могут непосредственно выполняться процессором. Ма- шинный код состоит из инструкций из встроенного набора команд (instruction set) процессора. Откомпилированная программа, состоящая из машинного кода, называется двоичным файлом (binary). Для таких уважаемых языков, как C, су- ществуют специальные программы — компиляторы; они преобразуют исходный файл с кодом C в двоичные файлы почти для любых существующих процессоров.
Но если вы хотите, чтобы Python работал на таком же наборе процессоров, на на- писание компилятора Python для каждого процессора потребуется масса времени.
Также существует другой механизм преобразования исходного кода в код, который может выполняться машиной. Вместо машинного кода, который выполняется не- посредственно процессором, можно создать байт-код (bytecode). Байт-код, также называемый портируемым (portable) кодом, или p-кодом, выполняется не напрямую процессором, а специальной программой — интерпретатором. Байт-код Python состоит из инструкций, хотя эти инструкции не выполняются ни одним реально существующим процессором. Вместо этого интерпретатор выполняет байт-код.
Байт-код Python сохраняется в файлах
.pyc
, которые иногда встречаются среди исходных файлов
.py
. Интерпретатор CPython, написанный на C, может компили- ровать исходный код Python в байт-код Python, а затем выполнять эти инструкции.
(Это относится и к виртуальной машине Java [JVM], выполняющей байт-код языка
Java.) Так как интерпретатор CPython написан на C, он может быть откомпилирован для любого процессора, для которого уже существует компилятор C.
Если вам захочется больше узнать обо всем этом, есть отличный источник — до- клад Скотта Сэндерсона (Scott Sanderson) и Джо Джевника (Joe Jevnik) «Playing with Python Bytecode» на конференции PyCon 2016 (https://youtu.be/mxjv9KqzwjI).

Частые ошибки при использовании терминов
161
Сценарии и программы, языки сценариев и языки программирования
Различия между сценарием (script) и программой, и даже языком сценариев и языком программирования, весьма туманны и субъективны. Можно с полным основанием утверждать, что все сценарии являются программами, а все языки сце- нариев являются языками программирования. Тем не менее языки сценариев иногда считаются более простыми, или «неполноценными», языками программирования.
Один из признаков, отличающих сценарии от программ, — способ выполнения кода.
Сценарии, написанные на языках сценариев, интерпретируются непосредственно из исходного кода, тогда как программы, написанные на языках программирова- ния, компилируются в двоичные файлы. Однако Python обычно рассматривается как язык сценариев, хотя при запуске программы Python существует этап компи- ляции в байт-код. При этом Java обычно языком сценариев не считается, хотя он и генерирует байт-код вместо двоичных файлов с машинным кодом, как и Python.
С технической точки зрения языки не компилируются и не интерпретируются; правильнее сказать, что существуют компилируемые и интерпретируемые реали- зации языка, и компилятор или интерпретатор можно создать для любого языка.
Эти различия вызывают споры, но в конечном итоге они не настолько важны. Языки сценариев вовсе не обязательно обладают меньшей мощью, а с компилируемыми языками не всегда труднее работать.
Библиотеки, фреймворки, SDK, ядра и API
Использование кода, написанного другими людьми, сильно экономит время. Такой код часто доступен в виде библиотек, фреймворков, SDK, ядер или API. И здесь надо понимать различия между ними.
Библиотека (library) — общий термин для подборки кодов, написанных третьей стороной. Библиотека может содержать функции, классы или другие фрагменты кода, предназначенные для использования разработчиками. Библиотека Python может быть реализована в виде пакета, набора пакетов и даже отдельного модуля.
Библиотеки часто предназначены для конкретного языка. Разработчикам не обяза- тельно знать, как работает код библиотеки; им достаточно знать, как вызывать код из библиотеки или взаимодействовать с ним. Стандартная библиотека (например, стандартная библиотека Python) представляет собой программную библиотеку, которая должна быть доступна для всех реализаций языка программирования.
Фреймворком (framework) называется подборка кода, работающая по принципу инверсии управления; разработчик пишет функции, которые вызываются фрейм- ворком по мере надобности (вместо вызова функций фреймворка из кода разработ- чика). Инверсия управления часто описывается фразой «не звоните нам, мы сами вам позвоним». Например, при написании кода для фреймворка веб-приложений

162
Глава 7.Жаргон программистов разработчику приходится создавать для веб-страниц функции, которые будут вы- зываться фреймворком при поступлении веб-запроса.
SDK (Software Development Kit — комплект разработки ПО) — это программные библиотеки, документация и программные средства, упрощающие создание при- ложений для конкретной операционной системы или платформы. Например,
Android SDK и iOS SDK используются для создания мобильных приложений для
Android и iOS соответственно. JDK (Java Development Kit) — SDK для создания приложений для JVM.
Ядро, или движок (engine), — крупная автономная система, которой могут управ- лять внешние программы разработчика. Разработчики обычно вызывают функции ядра для выполнения больших сложных задач. Примеры — игровые и физические движки, рекомендательные системы, ядра баз данных, ядра для шахматной игры и поисковые системы.
Интерфейс прикладного программирования, или API (Application Programming
Interface), — интерфейс для работы с библиотекой, SDK, фреймворком или ядром, предназначенный для внешнего использования. API указывает, как вызывать функции или обращаться с запросами к библиотеке для получения доступа к ее ресурсам. Создатели библиотеки (обычно) публикуют документацию API. Многие популярные социальные сети и веб-сайты предоставляют HTTP API, для того чтобы их услугами могли пользоваться программы (а не только люди с веб-браузерами).
Используя такие API, можно писать программы, которые, например, автоматически публикуют сообщения в Facebook или читают потоки сообщений Twitter.
Итоги
Даже программист с многолетним опытом не всегда знает все термины из области программирования. Но крупные программные продукты обычно создаются коман- дами разработчиков, а не отдельными людьми. Таким образом, однозначность терминологии играет важную роль при работе в команде.
Теперь вы знаете, что программы Python строятся из идентификаторов, переменных, литералов, ключевых слов и объектов, а каждый объект Python характеризуется значением, типом данных и идентичностью. И хотя каждый объект обладает типом данных, также существуют более широкие категории типов: контейнеры, после- довательности, отображения, множества, встроенные типы и типы, определяемые пользователем.
Некоторые понятия (скажем, значения, переменные и функции) могут по-разному называться в особых контекстах — например, элементы, параметры, аргументы и методы. Также программисты нередко путают некоторые термины друг с другом.

Дополнительные ресурсы
163
В повседневном программировании путаница в таких терминах, как свойства/
атрибуты, блок/тело, исключение/ошибка, тонкостях различий между библиоте- ками, фреймворками, SDK, ядрами и API не создаст особых проблем. Иногда при неправильном выборе термина ваш код будет нормально работать, но вы будете выглядеть непрофессионально; например, новички часто считают синонимами такие понятия, как команда и выражение, функция и метод, параметр и аргумент.
А вот такие термины, как итерируемый объект и итератор, синтаксическая ошибка и семантическая ошибка, байт-код и машинный код, имеют разный смысл. Никогда не путайте их, если только вы не хотите запутать своих коллег.
При этом смысл терминов может изменяться в зависимости от языка и даже от программиста. Со временем и по мере накопления опыта (и количества обраще- ний в интернете) вы начнете более уверенно чувствовать себя при использовании жаргона.
Дополнительные ресурсы
В официальном глоссарии Python (https://docs.python.org/3/glossary.html) приведе- ны короткие, но полезные определения, используемые в экосистеме Python. В офи- циальной документации Python (https://docs.python.org/3/reference/datamodel.html) объекты Python описываются более подробно.
В докладе Нины Захаренко (Nina Zakharenko) «Memory Management in Python —
The Basics» на конференции PyCon 2016 (https://youtu.be/F6u5rhUQ6dU) объ- ясняются многие аспекты работы сборщика мусора в Python. Официальная до- кументация Python (https://docs.python.org/3/library/gc.html) содержит больше информации о работе сборщика мусора.
Также полезно ознакомиться с обсуждением перехода на упорядоченные словари в Python 3.6 в списке рассылки Python (https://mail.python.org/pipermail/python-
dev/2016-September/146327.html).

8
Часто встречающиеся
ловушки Python
Хотя Python и является моим любимым языком програм- мирования, он не лишен недостатков. У каждого языка есть свои раздражающие особенности (в некоторых языках их больше, чем в других), и Python не исключение. Неопытные программисты, работающие на Python, должны знать некоторые часто встречающиеся ловушки и уметь избегать их.
Как правило, такие знания люди получают на собственном опыте, а я собрал ин- формацию о них в этой главе. Если вы будете знать историю таких ловушек, вам будет легче понять, почему Python иногда ведет себя немного странно.
В этой главе я расскажу, как изменяемые объекты, такие как списки и словари, на- чинают вести себя неожиданно при изменении их содержимого. Вы узнаете, почему метод sort()
не сортирует элементы строго по алфавиту, а числа с плавающей точ- кой могут содержать ошибки округления, почему операторы проверки неравенства
!=
has демонстрируют непредсказуемое поведение при сцеплении, а при записи кортежей, содержащих только один элемент, необходимо включать завершающую запятую. Ну и конечно, из этой главы вы узнаете, как обойти все эти ошибки.
Не добавляйте и не удаляйте элементы из списка
в процессе перебора
Добавление или удаление элементов из списка в процессе его перебора в цикле for или while с большой вероятностью приведет к ошибке. Рассмотрим следующий сценарий: вы хотите перебрать список строк с описаниями предметов одежды

Не добавляйте и не удаляйте элементы из списка в процессе перебора
165
и позаботиться о том, чтобы список содержал четное количество носков; для этого каждый раз, когда в списке обнаруживается один носок, в список добавляется пара для него. Задача кажется тривиальной; нужно перебрать строки из списка и при обнаружении в строке текста 'sock'
(например,
'red sock'
) в список будет добав- ляться еще один элемент 'red sock'
Но этот код работать не будет. Программа входит в бесконечный цикл, который приходится прерывать нажатием клавиш
Ctrl-C
:
>>> clothes = ['skirt', 'red sock']
>>> for clothing in clothes: # Перебрать список.
... if 'sock' in clothing: # Найти строки, содержащие 'sock'.
... clothes.append(clothing) # Добавить парный элемент.
... print('Added a sock:', clothing) # Сообщить пользователю.
Added a sock: red sock
Added a sock: red sock
Added a sock: red sock
Added a sock: red sock
Traceback (most recent call last):
File "", line 3, in
KeyboardInterrupt
Процесс выполнения этого кода наглядно представлен по адресу https://autbor.
com/addingloop/.
Проблема в том, что при добавлении в список clothes элемента 'red sock'
в списке появляется новый, третий элемент, который тоже должен быть включен в перебор:
['skirt',
'red sock',
'red sock']
. Цикл for достигает второго элемента 'red sock'
при следующей итерации, и он присоединяет вторую строку 'red sock'
. Список пре- образуется к виду
['skirt',
'red sock',
'red sock',
'red sock']
, и в нем появляется еще одна строка, которая должна быть включена в перебор. Все это продолжается, как показано на рис. 8.1, и в результате вы получаете бесконечный поток сообще- ний 'Added a
sock.
'. Цикл останавливается только тогда, когда на компьютере будет исчерпана вся свободная память и программа Python аварийно завершится или вы прервете программу клавишами
Ctrl-C
Вывод: не добавляйте элементы в список во время перебора. Вместо этого создайте отдельный список для содержимого измененного списка, такой как newClothes в этом примере:
>>>
1   ...   12   13   14   15   16   17   18   19   ...   40