Пишем на Python квайн — программу, которая выводит свой код
easy

Пишем на Python квайн — программу, которая выводит свой код

Красиво, остроумно, полезно

Сегодня мы напишем на Python программу, которая выводит свой код. Такие программы называются квайнами (в честь логика и философа Уилларда Ван Ормана Квайна). Создание квайнов — хорошая тренировка для программиста, которая помогает лучше понять возможности языка и его конструкций. Ещё такой проект можно положить в портфолио и показать на собеседовании.

В чём сложность

Чтобы программа на Python что-то вывела на экран, используют оператор print, в который передают то, что нужно: переменные, текст или готовые конструкции языка.

Такой код

print('Привет, это журнал Код!')

выведет слова: 

Привет, это журнал Код!

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

Логика проекта

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

  • переменные;
  • строки;
  • срезы;
  • конкатенация.

Переменная — это контейнер, в котором хранятся какие-то данные. Это может быть число, текст или более сложные конструкции, например список или словарь.

Строка — последовательность символов, с помощью которой можно работать со всем, что может быть представлено в текстовом виде. А квайн как раз должен выводить текст. В Python строка обозначается кавычками. Мы можем занести строку в переменную, если напишем:

s = 'Привет!'

Теперь у нас есть переменная s, в которой хранится текст.

Срез — это способ получения части какого-то итерируемого объекта, то есть такого, который можно перебирать по одному элементу за раз. Это может быть строка, список или кортеж. Чтобы программа показала фрагмент строки от первого символа до третьего, нужно написать:

print(s[0:2])

Нумерация объектов начинается с 0, а не 1.

Конкатенация — это процесс соединения двух и более строк в одну. В коде она обозначается знаком +.

Такой код:

print('Пи' + 'шем квайн на Py' + 'thon')

Выведет:

Пишем квайн на Python

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

И последнее: в нашем квайне всё будет в одну строку. Для этого мы будем разделять объявления переменных и вывод на экран точкой с запятой.

Теперь мы знаем всё, что нужно, можно начинать писать.

Разбираемся с кавычками

В нашем коде будет строка с текстом. Каким — мы пока не знаем, но для обозначения строки точно понадобятся кавычки. 

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

print('Если использовать 'одинарные кавычки' внутри одинарных кавычек, мы получим ошибку')

Если в языке программирования допустимы разные кавычки — одинарные и двойные, то их можно использовать вместе. Можно написать так:

print('Внутри одинарных кавычек "можно использовать двойные", и наоборот')

Мы используем эту возможность и объявим переменную-строку, внутри которой между двойными кавычками будет одинарная:

q="'"

Теперь мы можем вывести на экран одну одинарную кавычку так:

print(q)

Как вывести результат

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

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

s=''

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

Вот, что у нас есть сейчас:

q="'";s='';print()

Откуда взялась точка с запятой

Смотрите, какая штука: в Python необязательно ставить точку с запятой в конце команд, но мы поставили, чтобы не разделять код на несколько строк. Если написать эти три команды на разных строках без точки с запятой, нам нужно предусмотреть это в квайне, а это усложняет задачу. Поэтому мы идём на хитрость: пишем всё в одну строку и разделяем команды этим символом.

Пишем и собираем квайн

Сначала нужно составить строку — это переменная s. Наш код начинается с переменной q, добавим её:

s='q=""'

Этот код немного отличается от настоящего. Мы не указали одинарную кавычку, которую объявляем внутри двух двойных. Если мы так сделаем, Python решит, что строка закончилась. Для решения нам пригодится переменная q — внутри неё как раз лежит одинарная кавычка, которую можно вставить в print и не сломать вывод.

Чтобы программа выдавала реальный код, возьмём срез строки s до первой двойной кавычки, приклеим к этому срезу кавычку q и возьмём следующий срез, начиная со второй двойной кавычки и до конца строки s.

Вот как это будет выглядеть:

print(s[:3]+q+s[3:])

Добавим в строку этот код. Помним, что переменные и оператор вывода разделяются точкой с запятой:

s='q="";s=;print(s[:3]+q+s[3:])'

Посмотрим, что получается. Сейчас у нас есть такой код:

q="'";s='q="";s=;print(s[:3]+q+s[3:])';print(s[:3]+q+s[3:])

И этот код выводит такой результат:

q="'";s=;print(s[:3]+q+s[3:])

Сейчас место строки на экране выглядит как s=;. Между знаком равенства и точкой с запятой нужно как-то поместить весь код строки s, включая кавычки.

А теперь смотрите: то, что выделено жирным (q="";s=;print(s[:3]+q+s[3:])'), должно идти на вывод:

q="'";s='q="";s=;print(s[:3]+q+s[3:])';print(s[:3]+q+s[3:])

Как добавить кавычки, мы уже знаем — через переменную q. А чтобы вывести строку без кавычек, её тоже можно подставить просто как переменную. Получается, что такой код выведет на экран строку вместе с кавычками:

print(q+s+q)

Добавляем эту часть в print и поправим второй срез, чтобы выводить нужную часть строки. Сейчас срез (s[3:]) в конце кода выводит ";s=;print(s[:3]+q+s[3:])', а нам нужно, чтобы он выводил ";s=.

Исправляем:

print(s[:3]+q+s[3:7]+q+s+q)

Теперь продублируем этот код в строке:

s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q)'

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

q="'";s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q)';print(s[:3]+q+s[3:7]+q+s+q)

А на экран выводится вот это:

q="'";s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q)'

Нам не хватает точки с запятой и ещё одной части с print в конце. Чтобы добавить их, понадобится срез от 7-го элемента строки и до её конца. Добавим его в print и строку s:

q="'";s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q+s[7:])';print(s[:3]+q+s[3:7]+q+s+q+s[7:])

Собственно, это и есть тот самый код, который нам нужен. Если его запустить, он выведет сам себя. Чтобы детально увидеть, как это получается, сделаем вывод квайна по частям. Это уже не то, что нам нужно, но поможет понять, как именно код по частям собирает нужный вывод на экран:

print('1-я часть, s[:3]: ', s[:3])
print('2-я часть, q: ', q)
print('3-я часть, s[3:7]: ', s[3:7])
print('4-я часть, q: ', q)
print('5-я часть, s: ', s)
print('6-я часть, s: ', q)
print('7-я часть, s[7:]: ', s[7:])

Вот что мы увидим:

1-я часть, s[:3]: q="
2-я часть, q: '
3-я часть, s[3:7]: ";s=
4-я часть, q: '
5-я часть, s: q="";s=;print(s[:3]+q+s[3:7]+q+s+q+s[7:])
6-я часть, s: '
7-я часть, s[7:]: ;print(s[:3]+q+s[3:7]+q+s+q+s[7:])

Как ещё написать квайн

Самый простой способ написать квайн — использовать чтение собственного файла скрипта. Но многие программисты считают, что это жульничество.

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

  • Экранирование: операции со строкой, которые обозначаются обратным слешем. Чтобы поставить в текст кавычку в качестве обычного символа, нужно написать \'.
  • Функция repr() выводит формальное обозначение объекта. Поэтому код repr('Уиллард Квайн') выведет на экран 'Уиллард Квайн', вместе с кавычками.
  • Метод работы со строками .format. С его помощью можно вставлять переменные внутри текста в кавычках.

Есть ли на других языках

Квайны есть на всех языках, которые могут выводить настраиваемую строку. А ещё есть цепные квайны, биквайны и мультиквайны, но о них в другой раз.

Редактор:

Инна Долога

Обложка:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Маша Климентьева

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Вам может быть интересно
easy