ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 03.12.2023
Просмотров: 401
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
144
Глава 7.Жаргон программистов она содержала ссылку не на объект 'hello'
, а на объект 'goodbye'
. Чтобы убе- диться в этом, воспользуемся функцией id()
для вывода идентичностей двух объектов:
>>> spam = 'hello'
>>> id(spam)
40718944
>>> spam = 'goodbye'
>>> id(spam)
40719224
Эти два объекта обладают разными идентичностями (
40718944
и
40719224
), потому что это два разных объекта. Но у переменных, ссылающихся на изменяемые объек- ты, значения могут изменяться на месте (in-place). Например, введите следующий фрагмент в интерактивной оболочке:
>>> spam = ['cat', 'dog']
>>> id(spam)
33805576
>>> spam.append('moose')
❶
>>> spam[0] = 'snake'
❷
>>> spam
['snake', 'dog', 'moose']
>>> id(spam)
33805576
Метод append()
❶
и присваивание элементу по индексу
❷
изменяют значение списка на месте. И хотя значение списка изменилось, его идентичность осталась прежней (
33805576
). Но при выполнении конкатенации списка с использованием оператора
+
вы создаете новый объект (с новой идентичностью), который заменяет старый список:
>>> spam = spam + ['rat']
>>> spam
['snake', 'dog', 'moose', 'rat']
>>> id(spam)
33840064
Конкатенация списков создает новый список с новой идентичностью. Когда это происходит, старый список будет со временем удален из памяти сборщиком мусора. За информацией о том, какие методы и операции изменяют объекты на месте, а какие их перезаписывают, обращайтесь к документации Python. Можно запомнить хорошее практическое правило: если вы видите литерал в исходном коде (например,
['rat']
в приведенном примере), то, скорее всего, Python создаст новый объект. Метод, который вызывается для объекта (например, append()
), часто изменяет объект на месте.
Определения
145
Присваивание проще выполняется для объектов с неизменяемыми типами данных
(целые числа, строки, кортежи и т. д.). Например, введите следующий фрагмент в интерактивной оболочке:
>>> bacon = 'Goodbye'
>>> id(bacon)
33827584
>>> bacon = 'Hello'
❶
>>> id(bacon)
33863820
>>> bacon = bacon + ', world!'
❷
>>> bacon
'Hello, world!'
>>> id(bacon)
33870056 3 >>> bacon[0] = 'J'
Traceback (most recent call last):
File "
TypeError: 'str' object does not support item assignment
Строки являются неизменяемыми, так что вы не сможете изменить их значение. Хотя все выглядит так, будто значение строки в переменной bacon меняется с 'Goodbye'
на 'Hello'
❶
, в действительности она замещается новым объектом строки с новой иден- тичностью. Аналогичным образом выражение, использующее конкатенацию строк, создает новый строковый объект
❷
с новой идентичностью. Попытки модифициро- вать строку «на месте» посредством присваивания элементу запрещены в Python 3.
Значение кортежа определяется как совокупность содержащихся в нем объектов и порядка этих объектов. Кортежи представляют собой неизменяемые последо- вательности объектов, в которых значения заключаются в круглые скобки. Это означает, что перезапись элементов кортежа невозможна:
>>> eggs = ('cat', 'dog', [2, 4, 6])
>>> id(eggs)
39560896
>>> id(eggs[2])
40654152
>>> eggs[2] = eggs[2] + [8, 10]
Traceback (most recent call last):
File "
TypeError: 'tuple' object does not support item assignment
Однако изменяемый список в неизменяемом кортеже все равно можно изменить на месте:
>>> eggs[2].append(8)
>>> eggs[2].append(10)
>>> eggs
('cat', 'dog', [2, 4, 6, 8, 10])
146
Глава 7.Жаргон программистов
>>> id(eggs)
39560896
>>> id(eggs[2])
40654152
И хотя это экзотический случай, о нем важно помнить. Кортеж по-прежнему со- держит ссылки на те же объекты (рис. 7.3). Но если кортеж содержит изменяемый объект и этот объект изменяет свое значение, то значение кортежа также изменится.
Я, как и почти все программисты, пишущие на Python, называю кортежи неизме- няемыми. Но вопрос о том, можно ли назвать некоторые кортежи изменяемыми, зависит от определения. Эта тема более подробно рассматривается в моем докладе на конференции PyCascades 2019 «The Amazing Mutable, Immutable Tuple» (https://
invpy.com/amazingtuple/). Или же загляните в главу 2 книги «Fluent Python»
(O’Reilly Media, 2015) Лучано Рамальо (Luciano Ramalho).
Список изменяется
Кортеж не изменяется
Рис. 7.3. Хотя набор объектов в кортеже неизменяем, сами объекты могут быть изменяемыми
Индексы, ключи и хеш-коды
Списки и словари Python являются значениями, которые могут содержать на- боры других значений. Для обращения к этим значениям используется оператор
индексирования, который состоит из пары квадратных скобок (
[]
) и целого числа, называемого индексом; оно указывает, к какому значению вы хотите обратиться.
Чтобы понять, как работает индексирование списков, введите следующий фрагмент в интерактивной оболочке:
>>> spam = ['cat', 'dog', 'moose']
>>> spam[0]
'cat'
>>> spam[-2]
'dog'
Определения
147
В этом примере число
0
— индекс. Первый индекс равен 0, а не 1, потому что в Python
(как и в большинстве языков) нумерация индексов начинается с 0. Языки, в которых нумерация индексов начинается с 1, встречаются нечасто: самые заметные приме- ры — Lua и R. Python также поддерживает отрицательные индексы;
–1
обозначает последний элемент списка,
–2
— предпоследний элемент и т. д. Отрицательный индекс spam[–n]
можно рассматривать как эквивалент spam[len(spam)
- n]
ПРИМЕЧАНИЕ
Компьютерный теоретик, певец и автор песен Стэн Келли-Бутл (Stan Kelly-Bootle) однаж- ды пошутил: «Должны ли индексы массивов начинаться с 0 или 1? Мое компромиссное предложение 0,5 было отвергнуто без должного рассмотрения».
Оператор индексирования также может использоваться со списковым литералом, хотя в реальном коде все эти квадратные скобки кажутся непонятными и избы- точными:
>>> ['cat', 'dog', 'moose'][2]
'moose'
Индексирование также применяют не только со списками, но и с другими значе- ниями — например, со строками для получения отдельных символов:
>>> 'Hello, world'[0]
'H'
В словарях Python информация структурирована в виде пар «ключ — значение»:
>>> spam = {'name': 'Zophie'}
>>> spam['name']
'Zophie'
И хотя индексы списков ограничиваются целыми числами, оператор индексирования у словарей Python использует ключ, которым может быть любой хешируемый объ- ект. Хеш-код представляет собой целое число, своего рода отличительный признак некоторого значения. Хеш-код объекта никогда не изменяется на протяжении его жизненного цикла, и объекты с одинаковыми значениями должны иметь одинаковые хеш-коды. Строка 'name'
в этом экземпляре является ключом для значения 'Zophie'
Функция hash()
возвращает хеш-код объекта, если объект является хешируемым.
Неизменяемые объекты (строки, целые числа, числа с плавающей точкой, кортежи) могут быть хешируемыми. Списки (а также другие изменяемые объекты) хешируе- мыми не являются. Введите следующий фрагмент в интерактивной оболочке:
>>> hash('hello')
-1734230105925061914
>>> hash(42)
148
Глава 7.Жаргон программистов
42
>>> hash(3.14)
322818021289917443
>>> hash((1, 2, 3))
2528502973977326415
>>> hash([1, 2, 3])
Traceback (most recent call last):
File "
TypeError: unhashable type: 'list'
Хотя подробности выходят за рамки книги, хеш-код ключа используется для поиска элементов в структурах данных (словарях и множествах). Это объясняет, почему изменяемый список не может использоваться в ключах словарей:
>>> d = {}
>>> d[[1, 2, 3]] = 'some value'
Traceback (most recent call last):
File "
TypeError: unhashable type: 'list'
Хеш-код отличается от идентичности. Два разных объекта с одинаковыми значе- ниями имеют разные идентичности, но одинаковые хеш-коды. Например, введите следующий фрагмент в интерактивной оболочке:
>>> a = ('cat', 'dog', 'moose')
>>> b = ('cat', 'dog', 'moose')
>>> id(a), id(b)
(37111992, 37112136)
>>> id(a) == id(b)
❶
False
>>> hash(a), hash(b)
(-3478972040190420094, -3478972040190420094)
>>> hash(a) == hash(b)
❷
True
Кортежи, на которые ссылаются a
и b
, имеют разные идентичности
❶
, но так как они имеют одинаковые значения, из этого следует, что их хеш-коды идентичны
❷
Обратите внимание: кортеж является хешируемым, если он содержит только хе- шируемые элементы. Так как ключами словарей могут быть только хешируемые элементы, кортеж, содержащий нехешируемый список, не может использоваться в качестве ключа. Введите следующий фрагмент в интерактивной оболочке:
>>> tuple1 = ('cat', 'dog')
>>> tuple2 = ('cat', ['apple', 'orange'])
>>> spam = {}
>>> spam[tuple1] = 'a value'
❶
>>> spam[tuple2] = 'another value'
❷
Traceback (most recent call last):
File "
TypeError: unhashable type: 'list'
Определения
149
Обратите внимание: кортеж tuple1
является хешируемым
❶
, но tuple2
содержит нехешируемый список
❷
, а следовательно, хешируемым не является.
Контейнеры, последовательности, отображения и разновидности множеств
Смысл терминов «контейнер», «последовательность» и «отображение» в Python не всегда применим к другим языкам программирования. В Python контейнером называется объект любого типа данных, который может содержать несколько других объектов. Из всех видов контейнеров в Python чаще всего используются списки и словари.
Последовательность (sequence) представляет собой объект любого контейнерного типа данных с упорядоченными значениями, к которым можно обращаться по целым индексам. Строки, кортежи, списки и байтовые объекты относятся к типам данных последовательностей. Объекты этих типов могут обращаться к значениям по целым индексам в операторе индексирования (квадратные скобки
[
и
]
), а так- же могут передаваться функции len()
. Под упорядоченностью мы понимаем, что в последовательности есть первое значение, второе значение и т. д. Например, сле- дующие два списковых значения равными не считаются, потому что их элементы следуют в разном порядке:
>>>
1 ... 10 11 12 13 14 15 16 17 ... 40
[1, 2, 3] == [3, 2, 1]
False
Отображение (mapping) представляет собой объект любого контейнерного типа данных, использующий ключи вместо индексов. Отображение может быть упоря- доченным или неупорядоченным. Словари в Python 3.4 и более ранних версиях не упорядочены, то есть в них не было первой или последней пары «ключ — значение»:
>>> spam = {'a': 1, 'b': 2, 'c': 3, 'd': 4} # Запускается из CPython 3.5.
>>> list(spam.keys())
['a', 'c', 'd', 'b']
>>> spam['e'] = 5
>>> list(spam.keys())
['e', 'a', 'c', 'd', 'b']
В ранних версиях Python постоянный порядок следования элементов в словарях не гарантировался. В результате неупорядоченной природы словарей два словар- ных литерала, записанных с разным порядком пар «ключ — значение», все равно считались равными:
>>> {'a': 1, 'b': 2, 'c': 3} == {'c': 3, 'a': 1, 'b': 2}
True
Но начиная с CPython 3, словари поддерживают порядок вставки своих пар
«ключ — значение»:
150
Глава 7.Жаргон программистов
>>> spam = {'a': 1, 'b': 2, 'c': 3, 'd': 4} # Запускается из CPython 3.6.
>>> list(spam)
['a', 'b', 'c', 'd']
>>> spam['e'] = 5
>>> list(spam)
['a', 'b', 'c', 'd', 'e']
Это особенность интерпретатора CPython 3.6, отсутствующая в других интерпре- таторах Python 3.6. Все интерпретаторы Python 3.7 поддерживают упорядоченные словари, которые стали стандартными для языка Python в версии 3.7. Однако упорядоченность словаря не означает, что к его элементам можно обращаться по целочисленным индексам: spam[0]
не будет обозначать первый элемент в упоря- доченном словаре (если только по совпадению первый элемент не будет иметь ключ
0
). Упорядоченные словари также считаются равными, если они содержат одинаковые пары «ключ — значение», даже если пары «ключ — значение» в этих словарях следуют в разном порядке.
Модуль collections содержит много других видов отображений, включая
Orde- redDict
,
ChainMap
,
Counter и
UserDict
. Они описаны в электронной документации по адресу https://docs.python.org/3/library/collections.html.
Dunder-методы, или магические методы
Dunder-методы, называемые также магическими, — это специальные методы в Python, которые используются для перезагрузки операторов. Их имена начинают- ся и заканчиваются двумя символами подчеркивания (
__
). (Dunder — сокращение от Double UNDERscore.) Самый известный специальный метод
__init__()
ини- циализирует объекты. В Python определено несколько десятков dunder-методов, они подробно рассматриваются в главе 17.
Модули и пакеты
Модуль (module) представляет собой программу Python, которая может импорти- роваться другими программами Python, чтобы те могли воспользоваться кодом модуля. Модули, входящие в поставку Python, образуют стандартную библиотеку
Python, но вы также можете создавать собственные модули. Если сохранить про- грамму Python, например под именем spam.py
, другие программы смогут выполнить команду import spam
, чтобы получить доступ к функциям, классам и переменным верхнего уровня программы spam.py
Пакет (package) представляет собой набор модулей. Чтобы создать его, следует разместить в папке файл с именем
__init__.py
. Имя папки становится именем па- кета. Пакеты могут содержать несколько модулей (то есть файлов
.py
) или других пакетов (других папок, содержащих файлы
__init__.py
).
Определения
151
За дополнительными объяснениями и подробностями о модулях и пакетах обра- щайтесь к официальной документации Python по адресу https://docs.python.org/3/
tutorial/modules.html.
Вызываемые объекты и первоклассные объекты
В Python можно вызывать не только функции и методы. Любой объект, реализу- ющий оператор вызова — круглые скобки
()
, называется вызываемым объектом
(callable). Например, если у вас имеется команда def hello():
, код можно рассма- тривать как переменную с именем hello
, содержащую объект функции. При при- менении оператора вызова к этой переменной будет вызвана функция, хранящаяся в переменной: hello()
Классы относятся к концепциям ООП. Класс является примером вызываемого объ- екта, который не является функцией или методом. Например, класс date в модуле datetime вызывается с использованием оператора вызова, как в примере datetime.
date(2020,
1,
1)
. При вызове объекта класса выполняется метод
__init__()
этого класса. В главе 15 мы поговорим о классах более подробно.
В Python функции являются первоклассными объектами (first-class objects). Это означает, что их можно сохранять в переменных, передавать в аргументах при вызове функций, возвращать при вызове функций и делать все остальное, что можно сделать с объектом. Рассматривайте команду def как присваивание объекта функции переменной. Например, можно создать функцию spam()
, которую затем можно вызвать:
>>> def spam():
... print('Spam! Spam! Spam!')
>>> spam()
Spam! Spam! Spam!
Также можно присвоить объект функции spam()
другим переменным. Когда вы вызываете переменную, которой был присвоен объект функции, Python выполняет эту функцию:
>>> eggs = spam
>>> eggs()
Spam! Spam! Spam!
Таким образом создаются псевдонимы (aliases) — другие имена для существующих функций. Они часто используются в тех ситуациях, когда возникает необходимость в переименовании функции. Но старое имя используется в большом объеме суще- ствующего кода, и изменение всего этого кода потребовало бы слишком серьезной работы.
152
Глава 7.Жаргон программистов
Первоклассные функции чаще всего используются для передачи функций другим функциям. Например, можно определить функцию callTwice()
, которая дважды вызывает переданную ей функцию:
>>> def callTwice(func):
... func()
... func()
>>> callTwice(spam)
Spam! Spam! Spam!
Spam! Spam! Spam!
С таким же успехом можно было просто дважды вызвать spam()
в исходном коде.
Но функции callTwice()
можно передать любую функцию во время выполнения, вместо того чтобы заранее включать повторный вызов функции в исходный код.
Частые ошибки при использовании терминов
Технический жаргон достаточно неоднозначен — особенно для терминов взаимо- связанных, но имеющих разные определения. Ситуация усугубляется тем, что языки, операционные системы и разные области компьютерной теории могут раз- ными терминами обозначать одни и те же понятия или одинаковыми терминами — разные понятия. Чтобы ясно и однозначно общаться с другими программистами, необходимо понимать различия между терминами, о которых речь пойдет далее.
Команды и выражения
Выражениями (expressions) называются инструкции, состоящие из операторов и зна- чений, результатом вычисления которых является одно значение. Таким значением может быть переменная (которая содержит значение) или вызов функции (которая возвращает значение). Таким образом,
2
+
2
является выражением, при вычислении которого будет получено одно значение
4
. С другой стороны, len(myName)
>
4
и myName.
isupper()
or myName
==
'Zophie'
также являются выражениями. Значение само по себе также является выражением, результатом вычисления которого является оно само.
Практически все остальные инструкции в Python являются командами (statements).
К их числу относятся команды if
, команды for
, команды def
, команды return и т. д.
Команды не вычисляются в конкретное значение. Некоторые команды могут вклю- чать выражения — как, например, команда присваивания spam
=
2
+
2
или команда if myName
==
'Zophie':
В Python 3 используется функция print()
, а в Python 2 вместо нее существовала команда print
. Может показаться, что различия сводятся к добавлению круглых скобок, но важно заметить, что функция print()
в Python 3 имеет возвращаемое