Математический анализ. 3е издание
Скачать 4.86 Mb.
|
293 чения (т. е. ссылка на объект), благодаря этому отсутствует необхо димость предварительного объявления переменных. Иногда (но не всегда) в результате операции присваивания создаются элементы структур данных (например, элементы в словарях, некоторые атри буты объектов). После выполнения операции присваивания всякий раз, когда имя переменной будет встречено в выражении, оно заме щается объектом, на который ссылается эта переменная. • Прежде чем переменную можно будет использовать, ей должно быть присвоено значение. Нельзя использовать переменную, кото рой еще не было присвоено значение. В этом случае интерпретатор возбуждает исключение вместо того, чтобы вернуть какоелибо зна чение по умолчанию, – если бы он возвращал значение по умолча нию, это только осложнило бы поиск опечаток в программном коде. • Инструкции import, from, def, class, for, а также передача аргумен! тов функциям являются неявными инструкциями присваивания. В этом разделе мы сосредоточим все свое внимание на инструкции = , однако в языке Python присваивание может выполняться в самых разных контекстах. Например, далее мы увидим, что импорт моду ля, определение функции или класса, указание переменной в цикле for и передача аргументов функции неявно выполняют присваива ние. Операция присваивания выполняется одинаково в любом мес те, где бы она ни происходила, поэтому во всех этих контекстах про сто выполняется связывание имен со ссылками на объекты. Формы инструкции присваивания Несмотря на то, что в языке Python присваивание является универсаль ным и повсеместным понятием, в этой главе мы впервые сосредоточимся на инструкциях присваивания. В табл. 11.1 приводятся различные фор мы инструкции присваивания, которые встречаются в языке Python. Таблица 11.1. Формы инструкции присваивания Первая форма из табл. 11.1 является наиболее распространенной: она связывает переменную (или элемент структуры данных) с единствен ным объектом. Другие формы имеют особое назначение и являются необязательными, хотя многие и находят их очень удобными: Операция Интерпретация spam = 'Spam' Каноническая форма spam, ham = 'yum', 'YUM' Присваивание кортежей (позиционное) [spam, ham] = ['yum', 'YUM'] Присваивание списков (позиционное) a, b, c, d = 'spam' Присваивание последовательностей, обобщенное spam = ham = 'lunch' Групповое присваивание одного значения spams += 42 Комбинированная инструкция присваивания (эквивалентно инструкции spams = spams + 42) 294 Глава 11. Присваивание, выражения и print Присваивание кортежей и списков Вторая и третья формы в таблице являются родственными. Когда слева от оператора = указывается кортеж или список, интерпрета тор связывает объекты справа с именами слева, согласно их место положениям, выполняя присваивание слева направо. Например, во второй строке таблицы с именем spam ассоциируется строка 'yum', а с именем ham ассоциируется строка 'YUM'. Внутри интерпретатор Python сначала создает элементы кортежа справа, поэтому часто эта операция называется присваиванием неупакованного кортежа. Присваивание последовательностей В недавних версиях Python операции присваивания кортежей и спи сков были обобщены в то, что теперь называется операцией при сваивания последовательностей – любая последовательность имен может быть связана с любой последовательностью значений, и ин терпретатор свяжет элементы согласно их позициям. Фактически в последовательностях мы можем смешивать разные типы. Инст рукция присваивания в третьей строке табл. 11.1, например, свя зывает кортеж имен со строкой символов: имени a присваивается символ 's', имени b присваивается символ 'p' и т. д. Групповое присваивание одного значения Пятая строка в табл. 11.1 демонстрирует форму группового при сваивания. В этой форме интерпретатор присваивает ссылку на один и тот же объект (самый правый объект) всем целям, располо женным левее. Инструкция в таблице присваивает обоим именам spam и ham ссылку на один и тот же объект, строку 'lunch'. Результат будет тот же, как если бы были выполнены две инструкции: ham = 'lunch' и spam = ham, поскольку здесь ham интерпретируется как ори гинальный объектстрока (т. е. не отдельная копия этого объекта). Комбинированное (дополняющее) присваивание Последняя строка в табл. 11.1 – это пример комбинированной инст рукции присваивания – краткая форма, которая объединяет в себе выражение и присваивание. Например, инструкция spam += 42 дает тот же результат, что и инструкция spam = spam + 42, единственное отличие состоит в том, что комбинированная форма имеет более компактный вид и обычно выполняется быстрее. Для каждого двухместного оператора в языке Python существует своя комбини рованная инструкция присваивания. Присваивание последовательностей В этой книге мы уже использовали инструкцию присваивания в кано нической форме. Ниже приводится несколько примеров инструкций присваивания последовательностей в действии: % python >>> nudge = 1 Инструкции присваивания 295 >>> wink = 2 >>> A, B = nudge, wink # Присваивание кортежей >>> A, B # Что равносильно A = nudge; B = wink (1, 2) >>> [C, D] = [nudge, wink] # Присваивание списков >>> C, D (1, 2) Обратите внимание: в третьей инструкции этого примера в действитель ности присутствует два кортежа, просто мы опустили охватывающие их круглые скобки. Интерпретатор Python сопоставляет значения элемен тов кортежа справа от оператора присваивания с переменными в кор теже слева и выполняет присваивание значений в одной инструкции. Операция присваивания кортежей дает возможность использовать прием, который представлен в упражнениях ко второй части книги. Так как в процессе выполнения инструкции интерпретатор создает временный кортеж, где сохраняются оригинальные значения пере менных справа, данная форма присваивания может использоваться для реализации обмена значений переменных без создания временной переменной – кортеж справа автоматически запоминает предыдущие значения переменных: >>> nudge = 1 >>> wink = 2 >>> nudge, wink = wink, nudge # Кортежи: обмен значениями >>> nudge, wink # То же, что и T = nudge; nudge = wink; wink = T (2, 1) В конечном итоге формы присваивания кортежей и списков были обобщены, чтобы обеспечить возможность указывать любые типы по следовательностей справа при условии, что они будут иметь ту же дли ну. Допускается присваивать кортеж значений списку переменных, строки символов – кортежу переменных и т. д. В любом случае интер претатор свяжет элементы последовательности справа с переменными в последовательности слева согласно их позициям в направлении сле ва направо: >>> [a, b, c] = (1, 2, 3) # Кортеж значений присваивается списку переменных >>> a, c (1, 3) >>> (a, b, c) = "ABC" # Строка символов присваивается кортежу переменных >>> a, c ('A', 'C') С технической точки зрения в правой части инструкции присваивания последовательностей допускается указывать не только последователь ности, но и любые объекты, обеспечивающие возможность итераций по элементам. Эту, еще более общую концепцию, мы будем рассматри вать в главах 13 и 17. 296 Глава 11. Присваивание, выражения и print Дополнительные варианты инструкции присваивания последовательностей Одно замечание – даже при том, что допускается смешивать разные типы последовательностей по обе стороны оператора =, обе последова тельности должны иметь одно и то же число элементов, в противном случае мы получим сообщение об ошибке: >>> string = 'SPAM' >>> a, b, c, d = string # Одинаковое число элементов с обеих сторон >>> a, d ('S', 'M') >>> a, b, c = string # В противном случае выводится сообщение об ошибке ...текст сообщения об ошибке опущен... ValueError: too many values to unpack (Перевод: ValueError: слишком много распакованных значений) В общем случае нам необходимо получить срез. Существует несколько вариантов извлечения среза, чтобы исправить дело: >>> a, b, c = string[0], string[1], string[2:] # Элементы и срез >>> a, b, c ('S', 'P', 'AM') >>> a, b, c = list(string[:2]) + [string[2:]] # Срезы и конкатенация >>> a, b, c ('S', 'P', 'AM') >>> a, b = string[:2] # То же самое, только проще >>> c = string[2:] >>> a, b, c ('S', 'P', 'AM') >>> (a, b), c = string[:2], string[2:] # Вложенные последовательности >>> a, b, c ('S', 'P', 'AM') Последний пример демонстрирует, что мы можем присваивать даже вложенные последовательности, и интерпретатор распаковывает их части в соответствии с их представлением, как и ожидается. В данном случае выполняется присваивание кортежа из двух элементов, где первый элемент – это вложенная последовательность (строка), что точ но соответствует следующему случаю: >>> ((a, b), c) = ('SP', 'AM') # Связывание производится в соответствии >>> a, b, c # с формой и местоположением ('S', 'P', 'AM') Интерпретатор связывает первую строку справа ('SP') с первым корте жем слева ((a, b)), присваивая каждому его элементу по одному сим волу, а затем выполняет присваивание второй строки целиком ('AM') переменной c. В этом случае вложенная последовательность слева, имеющая форму объекта, должна соответствовать объекту справа. Инструкции присваивания 297 Присваивание вложенных последовательностей – это достаточно сложная операция, которая редко встречается на практике, но такой способ присваивания может оказаться удобным для присваивания части структуры данных известной формы. Например, данный прием может также использоваться в списках аргументов функций, потому что передача аргументов выполняется присваиванием (как будет пока зано в следующей части книги). Кроме того, операция присваивания последовательности с распаков кой дает начало еще одному распространенному обороту программиро вания на языке Python – присваиванию последовательности целых чи сел множеству переменных: >>> red, green, blue = range(3) >>> red, blue (0, 2) В этом примере три переменные инициализируются целочисленными значениями 0, 1 и 2 соответственно (это эквивалент перечислимых ти пов данных в языке Python, которые, возможно, вам приходилось встречать в других языках программирования). Чтобы понять проис ходящее, вы должны знать, что встроенная функция range генерирует список последовательных целых чисел: >>> range(3) # Попробуйте list(range(3)) в Python 3.0 [0, 1, 2] Поскольку функция range часто используется в циклах for, мы еще по говорим о ней в главе 13. Другой случай применения операции при сваивания кортежей – разделение последовательности на начальную и остальную части в циклах, как показано ниже: >>> L = [1, 2, 3, 4] >>> while L: ... front, L = L[0], L[1:] ... print front, L 1 [2, 3, 4] 2 [3, 4] 3 [4] 4 [] Присваивание кортежа в цикле здесь можно было бы заменить двумя следующими строками, но часто бывает удобнее объединить их в одну строку: ... front = L[0] ... L = L[1:] Обратите внимание: в этом примере список используется в качестве сте ка – структуры данных, поведение которой реализуют методы списков append и pop. В данном случае эффект, который дает операция присваи вания кортежа, можно было бы получить инструкцией front = L.pop(0), 298 Глава 11. Присваивание, выражения и print но это будет операция непосредственного изменения объекта. О циклах while и о других (часто лучших) способах обхода последовательностей с помощью циклов for вы узнаете больше в главе 13. Групповое присваивание При групповом присваивании объект, расположенный справа, при сваивается всем указанным переменным. В следующем примере трем переменным a, b и c присваивается строка 'spam': >>> a = b = c = 'spam' >>> a, b, c ('spam', 'spam', 'spam') Эта инструкция эквивалентна (но записывается компактнее) следую щим трем инструкциям присваивания: >>> c = 'spam' >>> b = c >>> a = b Групповое присваивание и разделяемые ссылки Имейте в виду, что в этом случае существует всего один объект, разде ляемый всеми тремя переменными (все они указывают на один и тот же объект в памяти). Такой способ присваивания хорошо подходит для не изменяемых объектов, например для инициализации нескольких счет чиков нулевым значением (не забывайте, что в языке Python перемен ная должна быть инициализирована, прежде чем к ней можно будет об ратиться, поэтому вам всегда придется устанавливать начальные значе ния в счетчиках, прежде чем они смогут использоваться для счета): >>> a = b = 0 >>> b = b + 1 >>> a, b (0, 1) Здесь изменение переменной b затронет только переменную b, потому что числа не допускают возможность непосредственного изменения. Если присваиваемый объект является неизменяемым, совершенно не важно, как много ссылок на него будет создано. Но, как обычно, следует проявлять осторожность, выполняя присваива ние переменным изменяемых объектов, таких как списки или словари: >>> a = b = [] >>> b.append(42) >>> a, b ([42], [42]) На этот раз, поскольку a и b ссылаются на один и тот же объект, непо средственное добавление значения к объекту через переменную b будет воздействовать и на переменную a. В действительности это всего лишь другой пример взаимовлияния разделяемых ссылок, с которым мы Инструкции присваивания 299 впервые встретились в главе 6. Чтобы избежать этой проблемы, ини циализацию изменяемыми объектами следует производить в отдель ных инструкциях, чтобы в каждой из них создавался новый пустой объект с помощью отдельных литеральных выражений: >>> a = [] >>> b = [] >>> b.append(42) >>> a, b ([], [42]) Дополняющее присваивание Начиная с версии Python 2.0, в языке появился набор дополнительных инструкций присваивания, перечисленных в табл. 11.2. Известные как инструкции дополняющего присваивания и заимствованные из языка C, они по существу являются лишь более компактной формой за писи. Они комбинируют в себе выражение и операцию присваивания. Например, следующие две формы записи практически эквивалентны: X = X + Y # Традиционная форма записи X += Y # Новая, комбинированная форма записи Таблица 11.2. Комбинированные инструкции присваивания Комбинированные операции присваивания существуют для любого поддерживаемого двухместного оператора. Например, ниже приводит ся два способа прибавления 1 к переменной: >>> x = 1 >>> x = x + 1 # Традиционная форма записи >>> x 2 >>> x += 1 # Дополняющая >>> x 3 Если дополняющую форму применить к строкам, будет выполнена операция конкатенации. Таким образом, вторая строка ниже эквива лентна более длинной инструкции S = S + "SPAM": >>> S = "spam" >>> S += "SPAM" # Выполняется операция конкатенации >>> S 'spamSPAM' Как показано в табл. 11.2, для каждого двухместного оператора (т. е. для оператора, слева и справа от которого располагаются значения, участвующие в операции) в языке Python существует своя инструкция X += Y X &= Y X = Y X |= Y X *= Y X ^= Y X /= Y X >>= Y X %= Y X <<= Y X **= Y X //= Y 300 Глава 11. Присваивание, выражения и print дополняющего присваивания. Например, инструкция X *= Y выполняет умножение и присваивание, X >>= Y – сдвиг вправо и присваивание, и т. д. Инструкция X //= Y (деление с округлением вниз) была добавле на в версии Python 2.2. Дополняющие присваивания обладают следующими преимуществами: 1 • Уменьшается объем ввода с клавиатуры. Нужно ли мне продолжать? • Левая часть инструкции должна быть получена всего один раз. В ин струкции «X += Y» X может оказаться сложным выражением, которое в комбинированной форме должно быть вычислено всего один раз. В более длинной форме запись «X = X + Y» X появляется дважды и по этому данное выражение должно быть вычислено дважды. Вследст вие этого дополняющие присваивания выполняются обычно быстрее. • Автоматически выбирается оптимальный алгоритм выполнения. Для объектов, поддерживающих возможность непосредственного изменения, дополняющие присваивания автоматически выполня ются непосредственно на самих объектах вместо выполнения более медленной операции копирования. И последний момент, который требует дополнительных разъяснений. Дополняющие присваивания в случае операций, выполняемых непо средственно над объектами, могут служить для оптимизации изменяе мых объектов. Вспомним, что списки могут расширяться разными способами. Чтобы добавить в список единственный элемент, мы мо жем выполнить операцию конкатенации или вызвать метод append: >>> L = [1, 2] >>> L = L + [3] # Конкатенация: более медленная >>> L [1, 2, 3] >>> L.append(4) # Более быстрая, но изменяет сам объект >>> L [1, 2, 3, 4] А чтобы добавить несколько элементов, мы можем либо снова выпол нить операцию конкатенации, либо вызвать метод extend: 2 >>> L = L + [5, 6] # Конкатенация: более медленная >>> L [1, 2, 3, 4, 5, 6] >>> L.extend([7, 8]) # Более быстрая, но изменяет сам объект 1 Программисты C/C++, конечно, заметят, что несмотря на появление в языке Python таких инструкций, как X +=Y, в нем до сих пор отсутствуют операто ры инкремента и декремента (например, X++, X). Однако эти операторы во обще не соответствуют модели языка Python, в котором отсутствует возмож ность непосредственно изменять неизменяемые объекты, такие как числа. 2 Как предлагалось в главе 6, для этого также можно было бы использовать операцию присваивания срезу (например, L[len(L):] = [11,12,13]), но этот прием работает практически так же, как метод extend. |