Литералы строк
Литералы строк позволяют интерпретатору Python убедиться что перед ним действительно находится строка. Такой подход называется "утиной" типизацией – если что-то плавает как утка, крякает ка утка и откладывает яйца как утка, то скорее всего это действительно утка. То же самое и с литералами строк – если что-то соответствует литералам строк, то это можно считать строкой. Вот и все.
В самом простом случае, строкой является последовательность символов заключенная в апострофы, кавычки, тройные кавычки или тройные апострофы:
>>> 'abc' # апострофы (одинарные кавычки)
'abc'
>>>
>>> "abc" # кавычки
'abc'
>>>
>>> """abc""" # тройные кавычки
'abc'
>>>
>>> '''abc''' # тройные апострофы
'abc'
Если внутри строки должны содержаться апострофы или кавычки, то сама строка должна обрамляться кавычками или апострофами соответственно:
>>> "O'Reilly"
"O'Reilly"
>>>
>>> 'Я "замечательный" программист!'
'Я "замечательный" программист!'
Тем не менее если вам нужно использовать внутри стрроки кавычки или апострофы, то лучше всего экранировать их специальным символом \
:
>>> '\'\'\'\''
"''''"
>>> "\"\"\"\""
'""""'
В этом случае можно точно гарантировать, что интерпретатор поймет все правильно и никаких ошибок не произойдет.
Каквычки и апострофы служат для создания "коротких" строк, а тройные кавычки и тройные апострофы для "длинных". Лучше всего это можно продемонстрировать с помощью функции print()
и специальных непечатаемых символов, например таких как \n
– символ переноса строки, \t
– символ табуляции (отступа).
>>> s1 = 'Привет! Я \"короткая\" строка.\nТут я начинаюсь с новой строки,\n\tа тут с новой строки и отступа'
>>>
>>> s1 # вывод строки как есть
'Привет! Я "короткая" строка.\nТут я начинаюсь с новой строки,\n\tа тут с новой строки и отступа'
>>>
>>> print(s1) # print интерпретирует служебные символы
Привет! Я "короткая" строка.
Тут я начинаюсь с новой строки,
а тут с новой строки и отступа
При создании "длинных" строк нам не нужно вводить все непечатаемые символы вручную, т.е. мы можем печатать текст так, как привыкли это делать, спокойно используя клавиши Tab и Enter:
>>> s2 = '''Привет! А я "длинная строка".
... Меня можно печатать как обычный текст.
...
...
... Сколько угодно используя клавиши "Enter" и
... "Tab"
... "Tab"
... "Tab"'''
Причем, обычный вывод снова печатает строку "как есть", обозначая все символы переноса и табуляции:
>>> s2
'Привет! А я "длинная строка".\nМеня можно печатать как обычный текст.\n\n\nСколько угодно используя клавиши "Enter" и\n"Tab"\n\t"Tab"\n\t\t"Tab"'
А вот функция print()
выполняет интерпретацию всех непечатаемых символов, выполняя все необходимые преобразования, в местах где они встречаются:
>>> print(s2)
Привет! А я "длинная строка".
Меня можно печатать как обычный текст.
Сколько угодно используя клавиши "Enter" и
"Tab"
"Tab"
"Tab"
Ввод длинных строк
Наличие очень длинных строк в коде, очень затрудняет его чтение, поэтому в Python, предусмотренно несколько способов ввода таких строк. Допустим у нас есть какая-то очень длинная строка и по каким-то причинам мы не можем вставлять внутрь символ "\n
", что автоматически означает невозможность использования тройных кавычек или тройных апострофов. Ну не вводить же эту строку вот так:
>>> s = 'Это очень длинная строка, которая не может содержать символы переноса - \"\\n\", и можно подумать, что в програмном коде она должна храниться именно в таком виде. Но на самом деле это не так.'
Следует сразу отметить, что символ переноса строки "\n
" для интерпретатора означает конец инструкции, но только в том случае если этот символ не находится внутри: круглых (()
), квадратных ([]
) и фигурных ({}
) скобок, а так же тройных кавычках или тройных апострофах. Это очень удобно, если нам необходимо вводить очень длинные последовательности, например матрица 6x6
может быть введена вот так:
>>> mat = [[0, 0, 0, 0, 0, 0],
... [1, 1, 1, 1, 1, 1],
... [2, 2, 2, 2, 2, 2],
... [3, 3, 3, 3, 3, 3],
... [4, 4, 4, 4, 4, 4],
... [5, 5, 5, 5, 5, 5]]
И в коде это будет смотреться гораздо лучше чем вот это:
>>> mat = [[0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2], [3, 3, 3, 3, 3, 3], [4, 4, 4, 4, 4, 4], [5, 5, 5, 5, 5, 5]]
Но для одинарных кавычек или апострофов этот способне не подойдет. Здесь на помощь приходит символ "\
" (newline), который интерпретируется как "новая строка", а не перевод на новую строку. Этот символ позволяет писать очень длинные "однострочные" иструкции в нескольких строках кода. И если вернуться к нашей строке:
>>> s = 'Это очень длинная строка, которая не может содержать символы переноса - \"\\n\", и можно подумать, что в програмном коде она должна храниться именно в таком виде. Но на самом деле это не так.'
То с помощью символа "\
" и оператора конкатенации мы можем записать ее вот так:
>>> s = 'Это очень длинная строка, которая не может ' + \
... 'содержать символы переноса - \"\\n\", и можно ' + \
... 'подумать, что в програмном коде она должна ' + \
... 'храниться именно в таком виде. Но ' + \
... 'на самом деле это не так.'
То что в переменной s
, хранится та самая длинная строка, очень легко убедиться.
>>> s
'Это очень длинная строка, которая не может содержать символы переноса - "\\n", и можно подумать, что в програмном коде она должна храниться именно в таком виде. Но на самом деле это не так.'
Второй способ, связан с тем, что если две строки присутствуют рядом в одной строке кода и разделены только пробелами, то они автоматически объединяются:
>>> 'XXX' 'YYY'
'XXXYYY'
Надо сразу отметить, что этот трюк работает только для строковых литералов:
>>> a 'YYY'
File "<stdin>", line 1
a 'YYY'
^
SyntaxError: invalid syntax
А если вспомнить, что символ "\n
" игнорируется внутри круглых скобок, то наша длинная строка может быть записана и так:
>>> s = ('Это очень длинная строка, которая не может '
... 'содержать символы переноса - \"\\n\", и можно '
... 'подумать, что в програмном коде она должна '
... 'храниться именно в таком виде. Но '
... 'на самом деле это не так.')
Именно этот способ рекомендуется использовать в коде. Причем данный способ (в отличие от предыдущего) допускает комментирование:
>>> s = ('|login' # логин
... '|pas' # пароль
... '|hash|' # хеш
... )
>>>
>>> s
'|login|pas|hash|'
>>>
Префиксы
Так же к литералам строк можно отнести префиксы (в скобках указаны альтернативные способы их написания):
u
(U
);r
(R
);b
(B
);f
(F
);fr
(rf
,fR
,rF
,Rf
,Fr
,FR
,RF
);br
(rb
,rB
,bR
,Br
,Rb
,BR
,RB
).
>>> u'world' # строки юникода
'world'
>>>
>>>
>>> r'home\my_doc\data.csv' # неотформатированные "сырые" строки
'home\\my_doc\\data.csv'
>>>
>>>
>>> b'\x77\x6f\x72\x6c\x64' # строки байтов
b'world'
>>>
>>>
>>> alpha = 7**0.5 # значение, подставляемое
>>> alpha # в форматируемую строку
2.6457513110645907
>>>
>>> f'coef = {alpha:0=+15.9}' # форматируемая строка
'coef = +00002.64575131'
>>>
>>>
>>> file_name = 'test_data_021'
>>>
>>> fr'home\my_doc\{file_name}.csv' # форматируемая "сырая" строка
'home\\my_doc\\test_data_021.csv'
>>>
>>>
>>> br'\x77\x6f\x72\x6c\x64' # "сырая" строка байтов
b'\\x77\\x6f\\x72\\x6c\\x64'
Нужно сразу уточнить, что между префиксом и строкой не может быть знаков пробела.
Префикс u
Префикс u
обозначает строки Юникода и существует лишь для совместимости строк в коде Python3 и Python2. Так что если вы имеете дело только с Python3, то можете смело забыть об этом префиксе.
Префикс r
Префикс r
обозначает неформатируемые (или необрабатываемые) строки, в которых подавляется действие символов экранирования. Такие строки очень удобны, для хранения путей к файлам в Windows, например:
>>> r'D:\\мои документы\книги\Лутц.pdf'
'D:\\\\мои документы\\книги\\Лутц.pdf'
Может показаться, что строка хранит неверный с точки зрения синтаксиса операционной системы путь, но если воспользоваться функцией print()
, то мы увидим как интерпретатор ее передает:
>>> print(r'D:\\мои документы\книги\Лутц.pdf')
D:\\мои документы\книги\Лутц.pdf
Такие строки можно спокойно использовать для работы с файловой системой. Единственное, что следует помнить так это то что неформатированная строка не может заканчиваться символом обратного слеша, т.е. строка r'D:\\мои документы\книги\'
является недопустимой (как и любая друга строка, заканчивающаяся нечетным количеством обратных слешей). В этой ситуации можно поступить тремя способами.
Первый способ заключается в том что бы добавить в конец строки два символа \
, а затем удалить последний из них:
>>> s = r'D:\\мои документы\книги\\'[:-1]
>>> print(s)
D:\\мои документы\книги\
Можно добавить экранированный символ обратного слеша \\
вручную:
>>> s = r'D:\\мои документы\книги' + '\\'
>>> print(s)
D:\\мои документы\книги\
Или вообще вместо экранированных строк использовать обычные, в которых каждый символ обратного слеша экранирован:
>>> s = 'D:\\\\мои документы\\книги\\'
>>> print(s)
D:\\мои документы\книги\
Так же неформатированные строки очень часто используются для хранения разметки LATEX, которая может быть использована для создания математических формул. Например, строка \sum_{k=1}^{n}k^{2}={\frac{n(n+1)(2n+1)}{6}}
интерпретируется как формула:
$$\sum_{k=1}^{n}k^{2}={\frac{n(n+1)(2n+1)}{6}}$$
А в Python использовать такую строку можно так:
>>> r'\sum_{k=1}^{n}k^{2}={\frac{n(n+1)(2n+1)}{6}}'
'\\sum_{k=1}^{n}k^{2}={\\frac{n(n+1)(2n+1)}{6}}'
Префикс b
Префикс b
используется для обозначения байтовых строк, которые отражают не структуру текста, а двоичную структуру каких-то данных, например файлов с изображениями или аудиофайлов. Каждому байту в такой строке соответствует определенное числовое значение из диапазона [0; 255]
. Такие строки могут содержать символы ASCII, но символы с кодом в диапазоне[128, 255]
должны быть выражены с помощью экранирования символом \x
:
>>> b'a1b2c3'
b'a1b2c3'
>>> b'a1b2c3'[1]
49
>>>
>>> b'\x61\x31\x62\x32\x63\x33'
b'a1b2c3'
>>> b'\x61\x31\x62\x32\x63\x33'[1]
49
>>>
>>> b'\xff\xfe\xfd'
b'\xff\xfe\xfd'
>>> b'\xff\xfe\xfd'[1]
254
Префикс f
В Python, начиная с версии 3.6, появился новый префикс f
, который позволяет определить строку как форматируемую. Это значит что в строке с этим префиксом можно обозначить место для вставки некоторого значения, а заодно и определить внешний вид подставляемого значения.
Работа данного префикса основана том же микроязыке спецификаторов форматирования, который использует метод format()
(см смотрите str.format() — метод форматирования строк). На первый взгляд, этот язык выглядит довольно замысловатой штукой, но если разобраться, то все станет довольно просто.
Как это работает? Допустим, у нас есть строка 'коэффициент k = '
и после знака =
мы хотим вставить какое-то числовое вещественное значение, допустим 7.143827
. Вот как это будет выглядеть в коде:
>>> f'коэффициент k = {7.143827:0=+10.4}'
'коэффициент k = +00007.144'
С помощью фигурных скобок {}
мы указываем то место в котором должно быть размещено поле для вставки. Внутри скобок, мы сначала указали подставляемое значение 7.143827
, а после двоеточия правила его форматирования. Знак =
означает, что если значение короче чем ширина поля, то оно должно быть дополнено слева до нужной ширины тем символом, который указан слева от =
, в нашем случае этот символ 0
. Далее, после знака =
указан символ +
, который вынуждает обозначать положительные и отрицательные числа соответствующими знаками: +
или -
. Далее, перед .
указано число 10
– это ширина поля. А вот число после точки указывает точность (ширину дробной части вместе с точкой) представления вещественного числа.
Действительно замысловато, но в определенных ситуациях весьма и весьма удобно:
>>> import random
>>>
>>> for i in range(15):
... k = random.uniform(-1000, 1000)
... j = random.random()
... print(f'|{i:^4}| k = {k:0<+8.4} | j = {j:0<+10.6}|')
...
| 0 | k = +856.500 | j = +0.9719550|
| 1 | k = +323.400 | j = +0.6675800|
| 2 | k = -501.100 | j = +0.1701850|
| 3 | k = +54.6500 | j = +0.7971660|
| 4 | k = +538.400 | j = +0.5695270|
| 5 | k = +62.1300 | j = +0.5209530|
| 6 | k = +814.500 | j = +0.9725200|
| 7 | k = +714.100 | j = +0.4484240|
| 8 | k = +652.200 | j = +0.2252530|
| 9 | k = +30.8100 | j = +0.9648810|
| 10 | k = +213.900 | j = +0.1805500|
| 11 | k = -931.600 | j = +0.2378620|
| 12 | k = -697.200 | j = +0.4830500|
| 13 | k = +643.200 | j = +0.6072970|
| 14 | k = +745.400 | j = +0.0572248|
В общем, будем считать, что с префиксом f
мы немного познакомились, но по хорошему, он заслуживает отдельной страницы.
Префикс fr
Префикс fr
определяет литералы форматируемых (!) неформатируемых строк. Иногда, строки с префиксом r
действительно называют неформатируемыми, подразумевая, что экранированные последовательности не обрабатываются, а раз они содержат необработанные экранированные последовательности, то их и называют "сырыми". Таким образом, префикс fr
это форматируемая "сырая строка".
Существуют ситуации, в которых такие строки могут оказаться весьма удобны. Например, формирование путей к файлам:
>>> dir_name = 'my_doc'
>>> file_name = 'data.txt'
>>>
>>> fr'home\{dir_name}\{file_name}'
'home\\my_doc\\data.txt'
Однако, для работы с разметкой LATEX (для математических) формул этот префикс вряд ли подойдет. Тем не менее он все равно используется, даже несмотря на то что вызывает много путаницы. Например у нас есть формула:
$$x^{{k-1}}{\frac{e^{{-x/\theta}}}{\theta^{k}\,\Gamma(k)}}$$
LATEX-разметка этой формулы имеет следующий вид: x^{{k-1}}{\frac{e^{{-x/\theta}}}{\theta^{k}\,\Gamma(k)}}
. И, допустим, мы хотим подставить в нее значения:
>>> x = 7.3
>>> k = 2.92
>>>
>>> s = fr'{x}^{{{k-1}}}{{\frac{{e^{{{{-{x}/\theta }}}}}}{{\theta^{k}\,\Gamma({k})}}}}'
>>>
>>> s
'7.3^{1.92}{\\frac{e^{{-7.3/\\theta }}}{\\theta^2.92\\,\\Gamma(2.92)}}'
>>>
>>> print(s)
7.3^{1.92}{\frac{e^{{-7.3/\theta }}}{\theta^2.92\,\Gamma(2.92)}}
Полученная строка 7.3^{1.92}{\frac{e^{{-7.3/\theta}}}{\theta^2.92\,\Gamma(2.92)}}
выдаст формулу:
$$7.3^{1.92}{\frac{e^{{-7.3/\theta }}}{\theta^2.92\,\Gamma(2.92)}}$$
Для сохранения каждой фигурной скобки, нам пришлось ее дублировать, т.к. если этого не делать то интерпретатор может ее счесть за начало указателя для вставки поля:
>>> f'Строка без фигурных скобок {123}'
'Строка без фигурных скобок 123'
>>>
>>> f'Строка с фигурными скобками {{{123}}}'
'Строка с фигурными скобками {123}'
Конечно мы могли бы воспользоваться оператором конкатенации и собрать необходимую строку по частям, но и этот способ может оказаться вполне жизнеспособным.
Префикс br
Префикс br
определяет "сырые" строки байтов, в которых каждый байт всегда выводится в шестнадцатеричном формате, а символы обратного слеша дополнительно экранируются. Обычные строки байтов могут содержать ASCII символы, но иногда это нежелательно:
>>> b'\x23\x56\x76\xa4\x15'
b'#Vv\xa4\x15'
>>>
>>>
>>> br'\x23\x56\x76\xa4\x15'
b'\\x23\\x56\\x76\\xa4\\x15'
И вот здесь я должен честно признаться, что вообще не понимаю для чего это может пригодиться. Но раз этот префикс добавили, значит, это кому-нибудь нужно.
Но все же, допустим у нас есть строка байтов b'\x61'
:
>>> b'\x61'
b'a'
Добавим префикс br
:
>>> br'\x61'
b'\\x61'
Посмотрим на длины строк:
>>> len(b'\x61')
1
>>> len(br'\x61')
4
В строке br'\x61'
последовательность \x61
интерпретируется не как код символа a
, а как \(4\) отдельных символа. Однако, если обратиться к этим символам, то:
>>> for i in range(4):
... print(br'\x61'[i], '-->', chr(br'\x61'[i]))
...
92 --> \
120 --> x
54 --> 6
49 --> 1
То поймем, что обращение к символам по индексу возвращает числовые коды Юникода. А это лишний раз подтверждает, что я вообще не понимаю для чего нужен этот префикс.