Литералы строк

Литералы строк позволяют интерпретатору 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

То поймем, что обращение к символам по индексу возвращает числовые коды Юникода. А это лишний раз подтверждает, что я вообще не понимаю для чего нужен этот префикс.