ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 03.12.2023
Просмотров: 402
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Частые ошибки при использовании терминов
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 "
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 "
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