Шпаргалка по регулярным выражениям

image_pdf

Шпаргалка по регулярным выражениям

Квантификаторы

  Аналог Пример Описание
? {0,1} a? одно или ноль вхождений «а»
+ {1,} a+ одно или более вхождений «а»
* {0,} a* ноль или более вхождений «а»

Модификаторы

Символ «минус» (-) меред модификатором (за исключением U) создаёт его отрицание.

  Описание
g глобальный поиск (обрабатываются все совпадения с шаблоном поиска)
i игнорировать регистр
m многострочный поиск. Поясню: по умолчанию текст это одна строка, с модификатором есть отдельные строки, а значит ^— начало строки в тексте, $— конец строки в тексте.
s текст воспринимается как одна строка, спец символ «точка» (.) будет вкючать и перевод строки
u используется кодировка UTF-8
U инвертировать жадность
x игнорировать все неэкранированные пробельные и перечисленные в классе символы

Спецсимволы

  Аналог Описание
()   подмаска, вложенное выражение
[]   групповой символ
{a,b}   количество вхождений от «a» до «b»
|   логическое «или», в случае с односимвольными альтернативами используйте []
\   экранирование спец символа
.   любой сивол, кроме перевода строки
\d [0-9] десятичная цифра
\D [^\d] любой символ, кроме десятичной цифры
\f   конец (разрыв) страницы
\n   перевод строки
\pL   буква в кодировке UTF-8 при использовании модификатора u
\r   возврат каретки
\s [ \t\v\r\n\f] пробельный символ
\S [^\s] любой символ, кроме промельного
\t   табуляция
\w [0-9a-z_] любая цифра, буква или знак подчеркивания
\W [^\w] любой символ, кроме цифры, буквы или знака подчеркивания
\v   вертикальная табуляция

Спецсимволы внутри символьного класса

  Пример Описание
^ [^da] отрицание, любой символ кроме «d» или «a»
[a-z] интервал, любой симво от «a» до «z»

Позиция внутри строки

  Пример Соответствие Описание
^ ^a aaa aaa начало строки
$ a$ aaa aaa конец строки
\A \Aa aaa aaa
aaa aaa
начало текста
\z a\z aaa aaa
aaa aaa
конец текста
\b a\b
\ba
aaa aaa
aaa aaa
граница слова, утверждение: предыдущий символ словесный, а следующий — нет, либо наоборот
\B \Ba\B aaa aaa отсутствие границы слова
\G \Ga aaa aaa Предыдущий успешный поиск, поиск остановился на 4-й позиции — там, где не нашлось a

Скачать в PDF, PNG.

Якоря

Якоря в регулярных выражениях указывают на начало или конец чего-либо. Например, строки или слова. Они представлены определенными символами. К примеру, шаблон, соответствующий строке, начинающейся с цифры, должен иметь следующий вид:

^[0-9]+

Здесь символ ^обозначает начало строки. Без него шаблон соответствовал бы любой строке, содержащей цифру.

Символьные классы

Символьные классы в регулярных выражениях соответствуют сразу некоторому набору символов. Например, \dсоответствует любой цифре от 0 до 9 включительно, \wсоответствует буквам и цифрам, а
\W— всем символам, кроме букв и цифр. Шаблон, идентифицирующий буквы, цифры и пробел, выглядит
так:

\w\s

POSIX

POSIX — это относительно новое дополнение семейства регулярных выражений. Идея, как и в случае с символьными
классами, заключается в использовании сокращений, представляющих некоторую группу символов.

Утверждения

Поначалу практически у всех возникают трудности с пониманием утверждений, однако познакомившись с ними ближе, вы
будете использовать их довольно часто. Утверждения предоставляют способ сказать: «я хочу найти в этом документе
каждое слово, включающее букву “q”, за которой не следует “werty”».

[^\s]*q(?!werty)[^\s]*

Приведенный выше код начинается с поиска любых символов, кроме пробела ([^\s]*), за которыми следует
q. Затем парсер достигает «смотрящего вперед» утверждения. Это автоматически делает предшествующий
элемент (символ, группу или символьный класс) условным — он будет соответствовать шаблону, только если
утверждение верно. В нашем случае, утверждение является отрицательным (?!), т. е. оно будет верным,
если то, что в нем ищется, не будет найдено.

Итак, парсер проверяет несколько следующих символов по предложенному шаблону (werty). Если они найдены,
то утверждение ложно, а значит символ qбудет «проигнорирован», т. е. не будет соответствовать шаблону.
Если же wertyне найдено, то утверждение верно, и с qвсе в порядке. Затем продолжается
поиск любых символов, кроме пробела ([^\s]*).

Кванторы

Кванторы позволяют определить часть шаблона, которая должна повторяться несколько раз подряд. Например, если вы
хотите выяснить, содержит ли документ строку из от 10 до 20 (включительно) букв «a», то можно использовать этот
шаблон:

a{10,20}

По умолчанию кванторы — «жадные». Поэтому квантор +, означающий «один или больше раз», будет
соответствовать максимально возможному значению. Иногда это вызывает проблемы, и тогда вы можете сказать квантору
перестать быть жадным (стать «ленивым»), используя специальный модификатор. Посмотрите на этот код:

".*"

Этот шаблон соответствует тексту, заключенному в двойные кавычки. Однако, ваша исходная строка может быть вроде
этой:

<a href="helloworld.htm" title="Привет, Мир">Привет, Мир</a>

Приведенный выше шаблон найдет в этой строке вот такую подстроку:

"helloworld.htm" title="Привет, Мир"

Он оказался слишком жадным, захватив наибольший кусок текста, который смог.

".*?"

Этот шаблон также соответствует любым символам, заключенным в двойные кавычки. Но ленивая версия (обратите внимание
на модификатор ?) ищет наименьшее из возможных вхождений, и поэтому найдет каждую подстроку в двойных
кавычках по отдельности:

"helloworld.htm" "Привет, Мир"

Экранирование в регулярных выражениях

Регулярные выражения используют некоторые символы для обозначения различных частей шаблона. Однако, возникает
проблема, если вам нужно найти один из таких символов в строке, как обычный символ. Точка, к примеру, в регулярном
выражении обозначает «любой символ, кроме переноса строки». Если вам нужно найти точку в строке, вы не можете просто
использовать «.» в качестве шаблона — это приведет к нахождению практически всего. Итак, вам
необходимо сообщить парсеру, что эта точка должна считаться обычной точкой, а не «любым символом». Это делается с
помощью знака экранирования.

Знак экранирования, предшествующий символу вроде точки, заставляет парсер игнорировать его функцию и считать обычным
символом. Есть несколько символов, требующих такого экранирования в большинстве шаблонов и языков. Вы можете найти
их в правом нижнем углу шпаргалки («Мета-символы»).

Шаблон для нахождения точки таков:

\.

Другие специальные символы в регулярных выражениях соответствуют необычным элементам в тексте. Переносы строки и
табуляции, к примеру, могут быть набраны с клавиатуры, но вероятно собьют с толку языки программирования. Знак
экранирования используется здесь для того, чтобы сообщить парсеру о необходимости считать следующий символ
специальным, а не обычной буквой или цифрой.

Спецсимволы экранирования в регулярных выражениях

Выражение Соответствие
\ не соответствует ничему, только экранирует следующий за ним символ. Это нужно, если вы хотите ввести метасимволы !$()*+.<>?[\]^{|}в качестве их буквальных значений.
\Q не соответствует ничему, только экранирует все символы вплоть до \E
\E не соответствует ничему, только прекращает экранирование, начатое \Q

Подстановка строк

Подстановка строк подробно описана в следующем параграфе «Группы и диапазоны», однако здесь следует упомянуть о
существовании «пассивных» групп. Это группы, игнорируемые при подстановке, что очень полезно, если вы хотите
использовать в шаблоне условие «или», но не хотите, чтобы эта группа принимала участие в подстановке.

Группы и диапазоны

Группы и диапазоны очень-очень полезны. Вероятно, проще будет начать с диапазонов. Они позволяют указать набор
подходящих символов. Например, чтобы проверить, содержит ли строка шестнадцатеричные цифры (от 0 до 9 и от A до F),
следует использовать такой диапазон:

[A-Fa-f0-9]

Чтобы проверить обратное, используйте отрицательный диапазон, который в нашем случае подходит под любой символ, кроме
цифр от 0 до 9 и букв от A до F:

[^A-Fa-f0-9]

Группы наиболее часто применяются, когда в шаблоне необходимо условие «или»; когда нужно сослаться на часть шаблона
из другой его части; а также при подстановке строк.

Использовать «или» очень просто: следующий шаблон ищет «ab» или «bc»:

(ab|bc)

Если в регулярном выражении необходимо сослаться на какую-то из предшествующих групп, следует использовать
\n, где вместо nподставить номер нужной группы. Вам может понадобиться шаблон,
соответствующий буквам «aaa» или «bbb», за которыми следует число, а затем те же три буквы. Такой шаблон реализуется
с помощью групп:

(aaa|bbb)[0-9]+\1

Первая часть шаблона ищет «aaa» или «bbb», объединяя найденные буквы в группу. За этим следует поиск одной или более
цифр ([0-9]+), и наконец \1. Последняя часть шаблона ссылается на первую группу и ищет то
же самое. Она ищет совпадение с текстом, уже найденным первой частью шаблона, а не соответствующее ему. Таким
образом, «aaa123bbb» не будет удовлетворять вышеприведенному шаблону, так как \1будет искать «aaa»
после числа.

Одним из наиболее полезных инструментов в регулярных выражениях является подстановка строк. При замене текста можно
сослаться на найденную группу, используя $n. Скажем, вы хотите выделить в тексте все слова «wish»
жирным начертанием. Для этого вам следует использовать функцию замены по регулярному выражению, которая может
выглядеть так:

replace(pattern, replacement, subject)

Первым параметром будет примерно такой шаблон (возможно вам понадобятся несколько дополнительных символов для этой
конкретной функции):

([^A-Za-z0-9])(wish)([^A-Za-z0-9])

Он найдет любые вхождения слова «wish» вместе с предыдущим и следующим символами, если только это не буквы или цифры.
Тогда ваша подстановка может быть такой:

$1<b>$2</b>$3

Ею будет заменена вся найденная по шаблону строка. Мы начинаем замену с первого найденного символа (который не буква
и не цифра), отмечая его $1. Без этого мы бы просто удалили этот символ из текста. То же касается конца
подстановки ($3). В середину мы добавили HTML тег для жирного начертания (разумеется, вместо него вы
можете использовать CSS или <strong>), выделив им вторую группу, найденную по шаблону
($2).

Модификаторы шаблонов

Модификаторы шаблонов используются в нескольких языках, в частности, в Perl. Они позволяют изменить работу парсера.
Например, модификатор iзаставляет парсер игнорировать регистры.

Регулярные выражения в Perl обрамляются одним и тем же символом в начале и в конце. Это может быть любой символ (чаще
используется «/»), и выглядит все таким образом:

/pattern/

Модификаторы добавляются в конец этой строки, вот так:

/pattern/i

Мета-символы

Наконец, последняя часть таблицы содержит мета-символы. Это символы, имеющие специальное значение в регулярных
выражениях. Так что если вы хотите использовать один из них как обычный символ, то его необходимо экранировать. Для
проверки наличия скобки в тексте, используется такой шаблон:

\(

Автор Шпаргалки на одном печатном листе формата A4 — Dave Child.

davechild_regular-expressions

 
Скачать

Интегрированные среды разработки и редакторы кода для Python

image_pdf

Для простых вещей написать отличный код на Python, используя IDLE или Python Shell, очень даже можно, однако, большие программные проекты эти инструменты быстро приведут к пропасти разочарования. Использование IDE или даже просто хорошего редактора кода делает программирование увлекательным, но какой для вас лучший?

Не бойтесь! Этот урок исключительно для того, чтобы помочь, объяснить и демистифицировать массу доступных вариантов. Я не смогу предложить то, что лучше всего вам подходит и соответствует вашему стилю работы, но смогу объяснить плюсы и минусы каждого из них, помогу принять обоснованное решение при выборе. Что-бы было проще, разделим весь список на две большие категории инструментов: 1) такие, которые созданы исключительно для разработки на Python, и 2) универсальные, созданные для общей разработки, но которые, однако, эффективны с Python.

Здесь, для каждого инструмента я отвечу на пару вопросов «Почему Да» и «Почему Нет». Наконец, ни один из представленных здесь не является взаимоисключающим и попробовать все с очень небольшими издержками вы сможете самостоятельно.
Читать далее «Интегрированные среды разработки и редакторы кода для Python»

Основные типы данных в Python

image_pdf

Архитектура приложений на Python — модули и пакеты

image_pdf

Содержание

Здесь вы познакомитесь с модулями (modules) и пакетами (packages) Python, с двумя механизмами модульного программирования (modular programming).

Модульное программирование есть процесс разбиения большой и громоздкой задачи на отдельные, более маленькие, управляемые подзадачи и модули. Все это по научному называется декомпозицией. Далее отдельные модули могут быть скомпонованы вместе, как строительные блоки, для создания более крупного приложения, решающего вашу задачу.

У такого, модульного подхода при проектировании кода больших приложений есть сразу несколько преимуществ:

  • Простота: Вместо того, чтобы думать о всей проблеме в целом, обычно, в модуле фокусируются на решении одной, относительно небольшой, части программы. Работая над одним модулем, сужается область размышлений, что делает разработку проще и менее подверженной ошибкам.
  • Модифицируемость: Обычно, модули имеют логические границы между различными задачами проблемы в целом. Если в модулях свести к минимуму взаимозависимости, то снижается вероятность того, что модификации одного модуля окажут влияние на другие части программы. Возможно, вы даже сможете вносить изменения в модуль, не зная ничего о приложении, для которого он написан. Таким образом, над одним приложением может работать большая группа программистов, что есть совместная разработка.
  • Повторное использование кода: Функциональность, определенная в одном модуле, может быть легко использована повторно (через соответствующий интерфейс) другими приложениями, что избавляет от необходимости дублирования.
  • Область действия: Обычно, в модуле определяется отдельное пространство имен, что помогает избежать коллизий между идентификаторами в разных областях программы. (Один из тезисов Дзен Python гласит Пространства имён — отличная штука! Будем делать их больше!)

Функции, модули и пакеты есть все конструкции в Python, которые способствуют модульному программированию на Python.

Модули в Python: обзор

На самом деле в Python есть три способа определения модуля:

  1. Модуль может быть написан на самом Python.
  2. Модуль может быть написан на C и динамически подгружен во время исполнения, как модуль re (regular expression).
  3. Модуль, встроенный в интерпретатор, как инструмент itertools.

Во всех трёх случаях доступ к модулю предоставляется одинаково — с помощью оператора import.

Здесь основное внимание будет уделено модулям, написанным на Python. Крутая штука в модулях, написанных на Python, заключается в том, что их чрезвычайно просто сделать. Все, что нужно, так это просто создать файл, который содержит допустимый код Python и дать ему имя с расширением .py. Это всё! Никакого специального синтаксиса или танцев с бубном не требуется.

Например, у вас есть файл mod.py со следующим кодом:

s = "Если товарищ Наполеон говорит, то это должно быть правильно."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

В mod.py определены следующие объекты:

  • s (строка)
  • a (список)
  • foo() (функция)
  • Foo (класс)

Предполагая, что файл mod.py расположен в правильном месте, о котором расскажем позже, доступ к этим объектам модуля можно получить импортируя модуль следующим образом:

>>> import mod
>>> print(mod.s)
Если товарищ Наполеон говорит, то это должно быть правильно.
>>> mod.a
[100, 200, 300]
>>> mod.foo(['quux', 'corge', 'grault'])
arg = ['quux', 'corge', 'grault']
>>> x = mod.Foo()
>>> x
<mod.Foo object at 0x03C181F0>

Правильное место для модуля

Продолжая разговор о вышеприведённом примере, посмотрим, что делает Python, выполняя оператор:

import mod

Когда интерпретатор выполняет оператор import, то он ищет файл mod.py в следующих каталогах в порядке приоритетности:

  • Текущий каталог, т.е. тот каталог, из которого был запущен наш скрипт с оператором import, если он запущен в интерактивном режиме.
  • В списке каталогов, определенном в установленной переменной окружения PYTHONPATH. (Формат PYTHONPATH зависит от операционной системы, но всегда похож на переменную окружения ОС PATH.)
  • В списоке каталогов, определённых и настроенных во время установки Python.

В результате, в переменной окружения sys.path модуля sys содержится список каталогов для поиска импортируемого модуля:

>>> import sys
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages']

Примечание: Значение в sys.path зависит от установки и на вашем компьютере, наверняка, будет отличаться от того, что написано выше.

Таким образом, чтобы убедиться, что ваш модуль найден, вам необходимо выполнить одно из следующих действий:

  • Разместить файл mod.py в текущем каталоге, если вы работаете в интерактивном режиме.
  • Изменить переменную окружения PYTHONPATH таким образом, что бы она содержала название каталога, где расположен файл mod.py
    • или: Разместить файл mod.py в одном из каталогов уже описанном в переменной PYTHONPATH.
  • Разместить файл модуля mod.py в одном из каталогов, которые определены при установке интерпретатора в вашей операционной системе.

На самом деле есть еще одна дополнительная возможность — можно поместить файл модуля в любой каталог по своему выбору, а затем изменить sys.path во время выполнения, чтобы он содержал этот каталог. Например, можно поместить mod.py в каталог C:\Users\john и затем выполнить следующие операторы:

>>> sys.path.append(r'C:\Users\john')
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Users\\john']
>>> import mod

После того, как модуль импортирован, можно определить его местоположение с помощью атрибута __file__ модуля:

>>> import mod
>>> mod.__file__
'C:\\Users\\john\\mod.py'

>>> import re
>>> re.__file__
'C:\\Python36\\lib\\re.py'

Часть каталога __file__ должна быть одной из каталогов в sys.path.

Оператор import

Содержимое модуля становится доступным вызывающему объекту после выполнения оператора import, который может быть записан в нескольких форматах. Их мы сейчас и обсудим.

import <module_name>

Простейшая форма уже показана выше:

import <module_name>

Заметим, что это не делает напрямую содержимое модуля доступным для вызывающей стороны. Каждый модуль имеет свою собственную таблицу частных определений, которая служит глобальной таблицей определений всех объектов в модуле. Таким образом, модуль создает отдельное пространство имен, как уже отмечалось.

Оператор import <module_name> только размещает <module_name> в таблице определений вызвавшего объекта. Объекты остаются в собственной таблице определений модуля.

Получить оступ к объектам модуля можно только, используя префикс <module_name>, так называемая точечная нотация, так, как показано ниже.

После выполнения оператора import mod размещается в локальной таблице определений. Таким образом, mod становится видимым в локальном контексте вызвавшего его кода:

>>> import mod
>>> mod
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

Но s и foo остаются в собственной таблице определений модуля и не доступны в локальном контексте вызвавшего скрипта:

>>> s
NameError: name 's' is not defined
>>> foo('quux')
NameError: name 'foo' is not defined

Для доступа в локальном контексте имена объектов, определенных в модуле, должны иметь префикс mod:

>>> mod.s
'Когда говорит товарищ Наполеон, то это должно быть правильно.'
>>> mod.foo('quux')
arg = quux

В одном операторе import можно указать несколько модулей, разделенных запятыми:

import <module_name>[, <module_name> ...]

from <module_name> import <name(s)>

Альтернативный формат записи оператора import позволяет импортировать отдельные объекты из модуля непосредственно в таблицу определений вызывающего скрипта:

from <module_name> import <name(s)>

После выполнения приведенного выше оператора в среде вызывающего скрипта на <name(s)> можно ссылаться без префикса <module_name> :

>>> from mod import s, foo
>>> s
'Когда говорит товарищ Наполеон, то это должно быть правильно.'
>>> foo('quux')
arg = quux

>>> from mod import Foo
>>> x = Foo()
>>> x
<mod.Foo object at 0x02E3AD50>

Поскольку эта форма импорта помещает имена объектов непосредственно в таблицу определений вызывающего скрипта, то все объекты с таким же именем будут перезаписаны:

>>> a = ['foo', 'bar', 'baz']
>>> a
['foo', 'bar', 'baz']

>>> from mod import a
>>> a
[100, 200, 300]

Можно даже сделать import всего из модуля одним махом, без разбора:

from <module_name> import *

Это поместит имена всех объектов из<module_name> в локальную таблицу определений, за сключением тех, которые начинаются с символа подчеркивания (_).

Например:

>>> from mod import *
>>> s
'Когда говорит товарищ Наполеон, то это должно быть правильно.'
>>> a
[100, 200, 300]
>>> foo
<function foo at 0x03B449C0>
>>> Foo
<class 'mod.Foo'>

В крупномасштабном производственном коде это не рекомендуется, птому как немного опасно, ожним махом вводить имена в локальную таблицу определений. Если вы не знаете их всех хорошо и не уверены, что конфликта не будет, то есть хороший шанс непреднамеренно перезаписать существующие имена. Однако, такой синтаксис очень удобен, когда вы просто возитесь с интерактивным интерпретатором при тестировании или отладке, потому что он быстро дает вам доступ ко всему, что может предложить модуль, без большого набора текста.

from <module_name> import <name> as <alt_name>

Кроме того, можно сделать import отдельных объекты, вводя их в локальную таблицу определений с альтернативными именами:

from <module_name> import <name> as <alt_name>[, <name> as <alt_name> …]

Это позволяет размещать имена непосредственно в локальной таблице определений, избегая конфликтов с ранее существующими именами:

>>> s = 'foo'
>>> a = ['foo', 'bar', 'baz']

>>> from mod import s as string, a as alist
>>> s
'foo'
>>> string
'Когда говорит товарищ Наполеон, то это должно быть правильно.'
>>> a
['foo', 'bar', 'baz']
>>> alist
[100, 200, 300]

import <module_name> as <alt_name>

Вы также можете импортировать весь модуль под другим именем:

import <module_name> as <alt_name>
>>> import mod as my_module
>>> my_module.a
[100, 200, 300]
>>> my_module.foo('qux')
arg = qux

Содержимое модуля может быть импортировано из функции. В этом случае import не происходит до тех пор, пока функция не будет вызвана:

>>> def bar():
...     from mod import foo
...     foo('corge')
...

>>> bar()
arg = corge

Однако, синтаксис Python 3 не допускает оператора import * внутри описания функции:

>>> def bar():
...     from mod import *
...
SyntaxError: import * only allowed at module level

Наконец, оператор try с выражением except ImportError позволяет избежать рекорректного import:

>>> try:
...     # Non-existent module
...     import baz
... except ImportError:
...     print('Module not found')
...

Module not found
>>> try:
...     # Existing module, but non-existent object
...     from mod import baz
... except ImportError:
...     print('Object not found in module')
...

Object not found in module

Функция dir()

Встроенная функция dir() возвращает список всех имен, определенных в пространстве. Если при вызове отсутствуют аргументы, то она создает алфавитно упорядоченный список имен в текущей локальной таблице определений:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> qux = [1, 2, 3, 4, 5]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux']

>>> class Bar():
...     pass
...
>>> x = Bar()
>>> dir()
['Bar', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux', 'x']

Обратите внимание, что при первом вызове dir() перечисляются несколько имен, которые определяются автоматически и уже при запуске интерпретатора находятся в пространстве имен. По мере определения новых имен (quux, Bar, x) они появляются при последующих вызовах dir().

Это бывает полезно для понимания того, что именно было добавлено в пространство имен с помощью инструкции import:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> import mod
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod']
>>> mod.s
'Когда говорит товарищ Наполеон, то это должно быть правильно.'
>>> mod.foo([1, 2, 3])
arg = [1, 2, 3]

>>> from mod import a, Foo
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod']
>>> a
[100, 200, 300]
>>> x = Foo()
>>> x
<mod.Foo object at 0x002EAD50>

>>> from mod import s as string
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod', 'string', 'x']
>>> string
'Когда говорит товарищ Наполеон, то это должно быть правильно.'

При задании в качестве аргумента имени модуля, dir() перечислит имена, определенные в этом модуле:

>>> import mod
>>> dir(mod)
['Foo', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'a', 'foo', 's']
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from mod import *
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'foo', 's']

Выполнение модуля как скрипта

Любой файл с расширением .py, содержащий модуль, по существу является скриптом Python и нет никаких причин, по которым он не может быть выполнен самостоятельно.

Здесь снова есть mod.py как это было определено выше:
mod.py

s = "Когда говорит товарищ Наполеон, то это должно быть правильно."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

Этот скрипт можно запустить:

C:\Users\john\Documents>python mod.py
C:\Users\john\Documents>

Там нет ошибок и, видимо, это сработало, но, конечно, не очень интересно. Так, как здесь написано, только определяет объекты, но ничего не делает с ними и не генерирует никакого вывода.

Давайте изменим вышеупомянутый модуль Python, чтобы он генерировал некоторый вывод при запуске в виде скрипта:

mod.py

s = "Когда говорит товарищ Наполеон, то это должно быть правильно."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

print(s)
print(a)
foo('quux')
x = Foo()
print(x)

Теперь должно быть немного интереснее:

C:\Users\john\Documents>python mod.py
Когда говорит товарищ Наполеон, то это должно быть правильно.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x02F101D0>

К сожалению, теперь он также генерирует вывод при импорте в виде модуля:

>>> import mod
Когда говорит товарищ Наполеон, то это должно быть правильно.
[100, 200, 300]
arg = quux
<mod.Foo object at 0x0169AD50>

Это, вероятно, не то, что вы хотите. Обычно модуль генерирует выходные данные при импорте.

Было бы неплохо, если бы можно было различать, когда файл загружается как модуль и когда он запускается как отдельный скрипт?

Просите, и дано вам будет.

Когда файл .py импортируется как модуль, Python устанавливает специальную переменную dunder в значение имени модуля __name__. Однако, если файл запускается как отдельный скрипт, то __name__ (творчески) устанавливается в строку '__main__'. Используя этот факт, можно определить, что происходит во время выполнения, и соответственно изменить поведение:

mod.py

s = "Когда говорит товарищ Наполеон, то это должно быть правильно."
a = [100, 200, 300]

def foo(arg):
    print(f'arg = {arg}')

class Foo:
    pass

if (__name__ == '__main__'):
    print('Executing as standalone script')
    print(s)
    print(a)
    foo('quux')
    x = Foo()
    print(x)

Теперь, если вы запускаете его, как скрипт, то получите результат:

C:\Users\john\Documents>python mod.py
Executing as standalone script
Когда говорит товарищ Наполеон, то это должно быть правильно.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x03450690>

Но если вы импортируете его, как модуль, то:

>>> import mod
>>> mod.foo('grault')
arg = grault

Модули часто разрабатываются с возможностью запуска в качестве отдельного сценария для тестирования функциональности, содержащейся в модуле. Это называется модульное тестирование. Например, вы создали модуль fact.py, где вычисляете factorial следующим образом:

fact.py

def fact(n):
    return 1 if n == 1 else n * fact(n-1)

if (__name__ == '__main__'):
    import sys
    if len(sys.argv) > 1:
        print(fact(int(sys.argv[1])))

Файл может рассматриваться как модуль, а функция fact() импортируется:

>>> from fact import fact
>>> fact(6)
720

Но он также может быть запущен как отдельный, передавая целочисленный аргумент в командной строке для тестирования:

C:\Users\john\Documents>python fact.py 6
720

Перезагрузка модуля

Из соображений эффективности модуль загружается только один раз за сеанс интерпретатора. Это хорошо для определений функций и классов, которые обычно составляют основную часть содержимого модуля. Но модуль также может содержать исполняемые операторы, обычно для инициализации. Помните, что эти операторы будут выполняться только при первом импорте модуля.

Рассмотрим следующий файл mod.py:

mod.py

a = [100, 200, 300]
print('a =', a)
>>> import mod
a = [100, 200, 300]
>>> import mod
>>> import mod

>>> mod.a
[100, 200, 300]

Оператор print() не выполняется при последующем импорте. (В этом отношении ни один из них не является оператором присваивания, но, как показывает окончательное отображение значения mod.a, это не имеет значения. Как только присвоение выполнено, оно придерживается.)

Если вы вносите изменения в модуль и вам необходимо его перезагрузить, вам нужно либо перезапустить интерпретатор, либо использовать функцию с именем reload() из модуля importlib:

>>> import mod
a = [100, 200, 300]

>>> import mod

>>> import importlib
>>> importlib.reload(mod)
a = [100, 200, 300]
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

Пакеты Python

Предположим, вы разработали очень большое приложение, которое включает в себя множество модулей. По мере роста количества модулей становится сложно отслеживать их все, если они размещаются в одном месте. Это особенно верно, если они имеют похожие имена или функциональность. Вам могут потребоваться средства их группировки и организации.

Пакеты позволяют иерархически структурировать пространство имен модуля с использованием точечной нотации. Точно так же, как модули помогают избежать коллизий между именами глобальных переменных, пакеты помогают избежать коллизий между именами модулей.

Создать пакет довольно просто, поскольку он использует внутреннюю иерархическую структуру файлов операционной системы. Рассмотрим следующую договоренность:

Пакет
Пакет

Здесь есть каталог с именем pkg, который содержит два модуля: mod1.py и mod2.py. Содержимое модулей:

mod1.py

def foo():
    print('[mod1] foo()')

class Foo:
    pass

mod2.py

def bar():
    print('[mod2] bar()')

class Bar:
    pass

Учитывая эту структуру, если каталог pkg находится в месте, где его можно найти (в одном из каталогов, содержащихся в sys.path), вы можете обратиться к двум модули с точечной нотацией (pkg.mod1, pkg.mod2) и импортируйте их с уже использованном синтаксисом:

import <module_name>[, <module_name> ...]
>>> import pkg.mod1, pkg.mod2
>>> pkg.mod1.foo()
[mod1] foo()
>>> x = pkg.mod2.Bar()
>>> x
<pkg.mod2.Bar object at 0x033F7290>
from <module_name> import <name(s)>
>>> from pkg.mod1 import foo
>>> foo()
[mod1] foo()
from <module_name> import <name> as <alt_name>
>>> from pkg.mod2 import Bar as Qux
>>> x = Qux()
>>> x
<pkg.mod2.Bar object at 0x036DFFD0>

Вы также можете импортировать модули с этими операторами:

from <package_name> import <modules_name>[, <module_name> ...]
from <package_name> import <module_name> as <alt_name>
>>> from pkg import mod1
>>> mod1.foo()
[mod1] foo()

>>> from pkg import mod2 as quux
>>> quux.bar()
[mod2] bar()

Технически вы также можете импортировать пакет:

>>> import pkg
>>> pkg
<module 'pkg' (namespace)>

Но это мало что дает. Хотя, строго говоря, это синтаксически правильный оператор Python, он не делает ничего полезного. В частности, он не помещает ни один из модулей в локальное пространство имен pkg:

>>> pkg.mod1
Traceback (most recent call last):
  File "<pyshell#34>", line 1, in <module>
    pkg.mod1
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod1.foo()
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    pkg.mod1.foo()
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod2.Bar()
Traceback (most recent call last):
  File "<pyshell#36>", line 1, in <module>
    pkg.mod2.Bar()
AttributeError: module 'pkg' has no attribute 'mod2'

Чтобы фактически импортировать модули или их содержимое, вам нужно использовать одну из форм, показанных выше.

Инициализация пакета

Если файл с именем __init__.py присутствует в каталоге пакета, он вызывается при импорте пакета или модуля в пакете. Так, что можно использовать его для выполнения кода инициализации пакета, например, для инициализации данных уровня пакета.

Например, следующий файл __init__.py:
__init__.py

print(f'Invoking __init__.py for {__name__}')
A = ['quux', 'corge', 'grault']

Давайте добавим этот файл в каталог pkg из приведенного выше примера:

Архитектура пакета
Архитектура пакета

Теперь, когда пакет импортирован, глобальный список A инициализируется:

>>> import pkg
Invoking __init__.py for pkg
>>> pkg.A
['quux', 'corge', 'grault']

Модуль в пакете может обращаться к глобальным переменным, импортируя их по очереди:

mod1.py

def foo():
    from pkg import A
    print('[mod1] foo() / A = ', A)

class Foo:
    pass
>>> from pkg import mod1
Invoking __init__.py for pkg
>>> mod1.foo()
[mod1] foo() / A =  ['quux', 'corge', 'grault']

__init__.py также можно использовать для автоматического импорта модулей из пакета. Например, ранее вы видели, что оператор import pkg помещает только имя pkg в таблицу локальных определений вызвавшего кода и не импортирует какие-либо модули. Но если __init__.py в каталоге pkg содержит следующее:

__init__.py

print(f'Invoking __init__.py for {__name__}')
import pkg.mod1, pkg.mod2

затем при выполнении import pkg модули mod1 и mod2 импортируются автоматически:

>>> import pkg
Invoking __init__.py for pkg
>>> pkg.mod1.foo()
[mod1] foo()
>>> pkg.mod2.bar()
[mod2] bar()

Примечание:

Большая часть документации Python гласит, что файл __init__.py должен присутствовать в каталоге пакета при его создании. Когда-то это было правдой. Раньше было так, что само присутствие __init__.py означало, что для Python пакет определен. Файл может содержать код инициализации или даже быть пустым, но он должен быть.

Начиная с Python 3.3, были представлены неявные пакеты пространства имен. Они позволяют создавать пакеты без какого-либо файла __init__.py. Конечно, он может быть, если требуется инициализация пакета. Но теперь это не обязательно.

Импорт * из пакета

Для дальнейшего обсуждения расширим ранее определенный пакет и теперь он содержать некоторые дополнительные модули:

Иллюстрация иерархической структуры файлов пакета
Иллюстрация иерархической структуры файлов пакета

В каталоге pkg теперь расположены 4 файла:

mod1.py

def foo():
    print('[mod1] foo()')

class Foo:
    pass

mod2.py

def bar():
    print('[mod2] bar()')

class Bar:
    pass

mod3.py

def baz():
    print('[mod3] baz()')

class Baz:
    pass

mod4.py

def qux():
    print('[mod4] qux()')

class Qux:
    pass

(Необычно, не так ли?)

Вы уже видели, что когда для модуля используется import *, то, как всегда, из модуля в локальную таблицу определений импортируются все объекты, кроме тех объектов, чьи имена начинаются с подчеркивания:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg.mod3 import *

>>> dir()
['Baz', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'baz']
>>> baz()
[mod3] baz()
>>> Baz
<class 'pkg.mod3.Baz'>

Аналогичное утверждение для пакета таково:

from <package_name> import *

Что это значит?

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

Хммм. Немного. Вы могли ожидать (предполагая, что у вас были какие-то ожидания), что Python погрузится в каталог пакетов, найдет все модули и импортирует их все. Но, как вы видите, по умолчанию это не то, что происходит.

Вместо этого Python следует этому соглашению: если файл __init__.py в каталоге package содержит список с именем __all__, который принимается за список модулей, которые должны быть импортированы, когда встречается оператор from <package_name> import *.

В данном примере Предположим, что вы создаете __init__.py в каталоге pkg вот так:

pkg/__init__.py

__all__ = [
        'mod1',
        'mod2',
        'mod3',
        'mod4'
        ]

Теперь from pkg import * импортирует все четыре модуля:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod1', 'mod2', 'mod3', 'mod4']
>>> mod2.bar()
[mod2] bar()
>>> mod4.Qux
<class 'pkg.mod4.Qux'>

Использование import * по-прежнему не считается потрясающей формой, пригодной больше для пакетов, чем для модулей. Но эта возможность, по крайней мере, дает создателю пакета некоторый контроль над тем, что происходит, когда указан import *. (На самом деле, он предоставляет возможность полностью его запретить, просто отказываясь вообще определять __all__. Как вы видели, поведение пакетов по умолчанию — ничего не импортировать.)

Кстати, __all__ также можно определить в модуле и служит для той же цели: контролировать то, что импортируется с помощью import *. Например, измените mod1.py следующим образом:

pkg/mod1.py

__all__ = ['foo']

def foo():
    print('[mod1] foo()')

class Foo:
    pass

Теперь оператор import * из pkg.mod1 будет импортировать только то, что содержится в __all__:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']

>>> from pkg.mod1 import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'foo']

>>> foo()
[mod1] foo()
>>> Foo
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    Foo
NameError: name 'Foo' is not defined

Функция foo() теперь определена в локальном пространстве имён, однако, класс Foo нет так, как его описание отсутствует в __all__.

Таким образом, __all__ используется как пакетами, так и модулями для управления тем, что импортируется оператором import *. Но поведение по умолчанию отличается:

  • Для пакетов, когда __all__ не определено, import * не импортирует ничего./li>
  • Для модулей, когда __all__ не определено, import * импортируется всё, кроме имён, как вы уже догадались, начиающихся с подчёруивания.

Подпакеты

Пакеты могут содержать вложенные подпакеты и глубина вложенности не ограничена. Например, изменим каталог package следующим образом:

Иллюстрация иерархии подпакетов
Иллюстрация иерархии подпакетов

Четыре модуля (mod1.py, mod2.py, mod3.py и mod4.py) определены как ранее. Но теперь вместо объединения в каталог pkg они разделены на два каталога подпакетов: sub_pkg1 и sub_pkg2.

Импорт по-прежнему работает так же, как показано ранее. Синтаксис аналогичен, но дополнительная точечная нотация используется для отделения имени пакета от имени подпакета:

>>> import pkg.sub_pkg1.mod1
>>> pkg.sub_pkg1.mod1.foo()
[mod1] foo()

>>> from pkg.sub_pkg1 import mod2
>>> mod2.bar()
[mod2] bar()

>>> from pkg.sub_pkg2.mod3 import baz
>>> baz()
[mod3] baz()

>>> from pkg.sub_pkg2.mod4 import qux as grault
>>> grault()
[mod4] qux()

Кроме того, модуль в одном подпакете может ссылаться на объекты в дочернем подпакете (в том случае, если один из братьев содержит некоторые необходимые вам функции). Например, предположим, что вы хотите импортировать и выполнить функцию foo() (определенную в модуле mod1) из модуля mod3. Вы можете использовать абсолютный импорт:

pkg/sub__pkg2/mod3.py

def baz():
    print('[mod3] baz()')

class Baz:
    pass

from pkg.sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
[mod1] foo()
>>> mod3.foo()
[mod1] foo()

Илиможно использовать относительный импорт, когда .. относится к пакету на уровень вые. Из mod3.py, который находится в подпакете sub_pkg2,

  • .. находит родительский пакет (pkg) и
  • ..sub_pkg1 переходит в подпакет sub_pkg1 родительского пакета.

pkg/sub__pkg2/mod3.py

def baz():
    print('[mod3] baz()')

class Baz:
    pass

from .. import sub_pkg1
print(sub_pkg1)

from ..sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
<module 'pkg.sub_pkg1' (namespace)>
[mod1] foo()

Заключение

В этом уроке вы узнали:

  • Как создать модуль Python.
  • Места, где интерпретатор Python ищет модуль.
  • Как получить доступ к объектам, определенном в модуле, используя оператор import.
  • Как создать модуль, который будет выполняться как отдельный скрипт
  • Как организовать модули в пакеты (packages) и подпакеты (subpackages).
  • Как управлять инициализацией пакета.

Надеемся, что теперь вы лучше понимаете, как получить доступ к функциям, доступным во многих сторонних и встроенных модулях для Python.

Кроме того, если вы разрабатываете свое собственное приложение, создание собственных модулей и пакетов поможет организовать и модульно оформить код, что упрощает кодирование, обслуживание и отладку.

Если вы хотите узнать больше, обратитесь к следующей документации на Python.org:

Счастливого пайтонинга!

С использованием материалов Python Modules and Packages – An Introduction

Python REST API с Flask, Connexion и SQLAlchemy — часть 2

image_pdf

В первой части этой серии вы использовали Flask и Connexion для создания REST API, обеспечивающего операции CRUD для простой структуры в памяти, называемой PEOPLE. Это помогло продемонстрировать, как модуль Connexion помогает вам создавать хороший REST API вместе с интерактивной документацией.

Читать далее «Python REST API с Flask, Connexion и SQLAlchemy — часть 2»

Начинаем писать на Python

image_pdf

В этом уроке

Надеюсь, что вы уже успели зарядить свой компьютер работающим интерпретатором Python 3. Если нужна помощь в настройке Python, обратитесь к уроку этого курса Заряжаем свой компьютер.

Вот что вы узнаете в этом уроке: Теперь, когда у вас есть работающая среда Python, вы увидите, как на самом деле выполнять код Python и запускать программы Python. К концу этой статьи вы узнаете, как:

  • использовать Python в интерактивном режиме, вводя код непосредственно в интерпретаторе;
  • выполнить код, содержащийся в файле сценария, из командной строки;
  • работать в интегрированной среде разработки (IDE) Python

Читать далее «Начинаем писать на Python»

Переменные Python

image_pdf

Содержание

В предыдущем уроке «Основные типы данных в Python» вы увидели, как можно создавать значения различных типов данных Python. Но пока что все показанные значения были буквальными или постоянными значениями:

>>> print(5.3)
5.3

В более сложном коде, неизбежно потребуются данные, которые могут изменяться по мере выполнения программы.

Вот что вы узнаете из этого урока: вы узнаете, как каждый элемент данных в программе Python можно описать с помощью абстрактного термина object, и вы узнаете как манипулировать объектами, используя символические имена, называемые переменными.

Назначение переменных

Думайте о переменной как об имени, прикрепленном к определенному объекту. В Python переменные не нужно объявлять или определять заранее, как это имеет место во многих других языках программирования. Чтобы создать переменную, вы просто назначаете ей значение, а затем начинаете использовать его. Назначение выполняется одним знаком равенства (=):

>>> n = 300

Это читается или интерпретируется как «n присваивается значение 300». Как только это будет сделано, n можно использовать в выражении или выражении, и его значение будет подставлено:

>>> print(n)
300

Так же, как буквальное значение может отображаться непосредственно из приглашения интерпретатора в сеансе REPL без необходимости в print(), как и переменная:

>>> n
300

Позже, если вы измените значение n и будете использовать его снова, вместо него будет подставлено новое значение:

>>> n = 1000
>>> print(n)
1000
>>> n
1000

Python также позволяет присваивать цепочки, что позволяет присваивать одно и то же значение нескольким переменным одновременно:

>>> a = b = c = 300
>>> print(a, b, c)
300 300 300

Приведенное выше связанное назначение присваивает 300 переменным a, b и c одновременно.

Типы переменных в Python

Во многих языках программирования переменные статически типизированы. Это означает, что переменная первоначально объявляется имеющей определенный тип данных, и любое значение, присвоенное ей в течение срока ее службы, всегда должно иметь этот тип.

Переменные в Python не подпадают под это ограничение. В Python переменной может быть присвоено значение одного типа, а затем позднее переназначено значение другого типа:

>>> var = 23.5
>>> print(var)
23.5

>>> var = "Now I'm a string"
>>> print(var)
Now I'm a string

Ссылки на объекты

Что на самом деле происходит, когда вы делаете Назначение параметров? Это важный вопрос в Python, потому что ответ несколько отличается от того, что вы найдете во многих других языках программирования.

Python — это чрезвычайно объектно-ориентированный язык. Фактически, практически каждый элемент данных в программе на Python является объектом определенного типа или класса. (Этот пункт будет повторяться много раз в течение всех уроков.)

Рассмотрим код:

>>> print(300)
300

Когда ему предоставляется выражение print(300), интерпретатор делает следующее:

  • Создает целочисленный объект.
  • Помещает в него значение 300.
  • Показывает его в консоли.

Вы можете видеть, что создан именно целочисленный объект, с помощью встроенной функции type():

>>> type(300)
<class 'int'>

Переменная Python — это символическое имя, которое является ссылкой или указателем на объект. Как только объект назначен переменной, вы можете ссылаться на объект под этим именем. Но сами данные все еще содержатся в объекте.

Например:

>>> n = 300

This assignment creates an integer object with the value 300 and assigns the variable n to point to that object.

Назначение переменной
Назначение переменной

 

Следующий код проверяет, что n указывает на целочисленный объект:

>>> print(n)
300
>>> type(n)
<class 'int'>

Теперь рассмотрим следующее утверждение:

>>> m = n

Что происходит, когда он выполняется? Python не создает другого объекта. Он просто создает новое символическое имя или ссылку m, которая указывает на тот же объект, на который указывает n.

Множественная ссылка на объект
Множественная ссылка на объект

 

Далее, предположим, что вы делаете это:

>>> m = 400

Теперь Python создает новый целочисленный объект со значением 400, и m становится ссылкой на него.

References to separate objects in Python (diagram)
References to Separate Objects

Lastly, suppose this statement is executed next:

>>> n = "foo"

Теперь Python создает строковый объект со значением "foo" и ссылается на него n.

Сиротинушка
Сиротинушка

Больше нет ссылки на целочисленный объект 300. Он осиротел, и нет возможности получить к нему доступ.

Учебные пособия в этой серии иногда ссылаются на время жизни объекта. Жизнь объекта начинается с момента его создания, когда создается хотя бы одна ссылка на него. В течение срока службы объекта, как вы видели выше, могут создаваться дополнительные ссылки на него, а также ссылки на него. Объект остается как бы живым, пока на него есть хотя бы одна ссылка.

Когда количество ссылок на объект падает до нуля, он больше не доступен. В этот момент его время жизни истекло. В конечном итоге Python заметит, что он недоступен, и освободит выделенную память, чтобы ее можно было использовать для чего-то другого. В компьютерном жаргоне этот процесс называется сборка мусора.

Идентичность объекта

В Python каждому создаваемому объекту присваивается номер, который однозначно его идентифицирует. Гарантируется, что никакие два объекта не будут иметь одинаковый идентификатор в течение любого периода, в течение которого их время жизни перекрывается. Как только счетчик ссылок на объект падает до нуля, и он собирается сборщиком мусора, как это произошло с объектом 300 выше, его идентификационный номер становится доступным и может использоваться снова.

Встроенная функция Python id() возвращает целочисленный идентификатор объекта. Используя функцию id (), вы можете проверить, что две переменные действительно указывают на один и тот же объект:

>>> n = 300
>>> m = n
>>> id(n)
60127840
>>> id(m)
60127840

>>> m = 400
>>> id(m)
60127872

После присваивания m = n, m и n оба указывают на один и тот же объект, что подтверждается тем фактом, что id(m) и id(n) возвращают одинаковые значения. Как только m присваеивается значение 400, m и n станут указывать на различные объекты.

Deep Dive: кэширование небольших целочисленных значений
Из того, что вы теперь знаете о Назначении объектов и Ссылки на объекты в Python, следующее, вероятно, вас не удивит:

>>> m = 300
>>> n = 300
>>> id(m)
60062304
>>> id(n)
60062896

С помощью оператора m = 300 Python создает целочисленный объект со значением 300 и устанавливает m в качестве ссылки на него. Затем n аналогичным образом присваивается целочисленному объекту со значением 300, но не одному и тому же объекту. Таким образом, они имеют разные идентификаторы, что вы можете проверить по значениям, возвращаемым id().

Но учтите это:

>>> m = 30
>>> n = 30
>>> id(m)
1405569120
>>> id(n)
1405569120

Здесь m и n отдельно назначаются целочисленным объектам, имеющим значение 30. Но в этом случае id (m) и id (n) идентичны!

В целях оптимизации интерпретатор создает объекты для целых чисел в диапазоне [- 5, 256] при запуске, а затем повторно использует их во время выполнения программы. Таким образом, когда вы назначаете отдельные переменные целочисленному значению в этом диапазоне, они фактически ссылаются на один и тот же объект.

Имена переменных

В примерах, которые вы видели до сих пор, использовались краткие и краткие изменения, такие как m и n. Но Имена значений может быть более многословной. На самом деле, обычно это выгодно, потому что это делает назначение переменной более очевидным на первый взгляд.

Официально, Имена значений в Python может быть любой длины и может состоять из прописных и строчных букв (AZ, az), цифр (0-9 ) и символ подчеркивания (_). Дополнительным ограничением является то, что, хотя имя переменной может содержать цифры, первый символ имени переменной не может быть цифрой.

Note: One of the additions to Python 3 was full Unicode support, which allows for Unicode characters in a variable name as well. You will learn about Unicode in greater depth in a future tutorial.

Например, все, перечисленные ниже, имена переменных верны:

>>> name = "Bob"
>>> Age = 54
>>> has_W2 = True
>>> print(name, Age, has_W2)
Bob 54 True

Но это не так, потому что имя переменной не может начинаться с цифры:

>>> 1099_filed = False
SyntaxError: invalid token

Обратите внимание, что случай является значительным. Строчные и прописные буквы не совпадают. Использование символа подчеркивания также важно. Каждое из следующих значений определяет разные переменные:

>>> age = 1
>>> Age = 2
>>> aGe = 3
>>> AGE = 4
>>> a_g_e = 5
>>> _age = 6
>>> age_ = 7
>>> _AGE_ = 8

>>> print(age, Age, aGe, AGE, a_g_e, _age, age_, _AGE_)
1 2 3 4 5 6 7 8

There is nothing stopping you from creating two different variables in the same program called age and Age, or for that matter agE. But it is probably ill-advised. It would certainly be likely to confuse anyone trying to read your code, and even you yourself, after you’d been away from it awhile.

Целесообразно давать переменным достаточно информативные имена, для понимания того, для чего они используются. Например, вы подсчитываете количество людей, окончивших колледж. Вы можете выбрать любой из следующих вариантов:

>>> numberofcollegegraduates = 2500
>>> NUMBEROFCOLLEGEGRADUATES = 2500
>>> numberOfCollegeGraduates = 2500
>>> NumberOfCollegeGraduates = 2500
>>> number_of_college_graduates = 2500

>>> print(numberofcollegegraduates, NUMBEROFCOLLEGEGRADUATES,
... numberOfCollegeGraduates, NumberOfCollegeGraduates,
... number_of_college_graduates)
2500 2500 2500 2500 2500

Все они, вероятно, являются лучшим выбором, чем n или ncg, или тому подобное. По крайней мере, вы можете сказать по названию, что значение переменной должно представлять.

С другой стороны, они не обязательно разборчивы. Как и во многих вещах, это вопрос личных предпочтений, но большинству людей было бы сложнее читать первые два примера, где все буквы собраны вместе, особенно один из всех заглавных букв. Наиболее часто используемые методы построения имени переменной из нескольких слов — последние три примера:

  • Camel Case: Второе и последующие слова пишутся с заглавной буквы, чтобы легче было видеть границы слов. (Предположительно, в какой-то момент кого-то поразило, что заглавные буквы, разбросанные по всему имени переменной, смутно напоминают верблюжьих горбов.)
    • Например: numberOfCollegeGraduates
  • Pascal Case: Идентичен Camel Case, за исключением того, что первое слово также пишется с большой буквы.
      NumberOfCollegeGraduates
  • Snake Case: Слова разделяются подчеркиванием.
    • Например: number_of_college_graduates

Программисты горячо спорят с удивительным пылом, какой из них предпочтительнее. Приличные аргументы могут быть сделаны для всех из них. Используйте любой из трех наиболее привлекательных для вас. Выберите один и используйте его последовательно.

Позже вы увидите, что переменные — не единственные вещи, которым можно дать имена. Вы также можете называть функции, классы, модули и так далее. Правила, которые применяются к Имена явления, также применяются к идентификаторам, более общему термину для имен, данных объектам программы.

Style Guide for Python Code, известный так-же как PEP 8, содержит Соглашение об именах предлагает некоторые стандарты для именования объектов различных типов. PEP 8 включает следующие рекомендации:

  • Snake Case должен использоваться для именования функций и переменных.
  • Pascal Case используется для именования классов. (PEP 8 refers to this as the “CapWords” convention.)

Зарезервированные (ключевые) слова

Есть еще одно ограничение на имена идентификаторов. Язык Python резервирует небольшой набор ключевых слов, которые обозначают специальные языковые функции. Ни один объект не может иметь того же имени, что и зарезервированное слово.

В Python 3.6 зарезервировано 33 ключевых слова. Вот они:

False
def
if
raise
None
del
import
return
True
elif
in
try
and
else
is
while
as
except
lambda
with
assert
finally
nonlocal
yield
break
for
not
class
from
or
continue
global
pass

 

Вы можете увидеть этот список в любое время, введя help("keywords") в интерпретатор Python. Зарезервированные слова чувствительны к регистру и должны использоваться точно так, как показано. Все они полностью строчные, за исключением False, None и True.

Попытка создать переменную с тем же именем, что и у любого зарезервированного слова, приводит к ошибке:

>>> for = 3
SyntaxError: invalid syntax

Заключение

В этом уроке рассматривается понятие переменных в Python, в том числе ссылки на объекты и идентификаторы, а также имена идентификаторов Python.

Теперь вы хорошо понимаете некоторые типы данных Python и знаете, как создавать переменные, которые ссылаются на объекты этих типов.

Далее вы увидите, как объединить объекты данных в выражения, используя различные операции.

Использованы материалы: Variables in Python