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

Визуализацию можно осуществить разными способами. Несмотря на широкое использование форматов фотографий, таких как JPEG или PNG, создание таких форматов файлов с помощью такого языка, как Python, является сложной задачей. Для создания PNG, например, требуются пиксельно-ориентированные массивы данных RGB, которые сжимаются с использованием библиотеки сжатия, такой как zlib. Построение изображения через пиксели, безусловно, возможно, но сложно, поскольку рисование чего-то вроде круга или линии требует вычисления геометрических уравнений для соответствующих форм.

Гораздо более эффективный формат для визуализации кода - это масштабируемая векторная графика (SVG). Это диалект xml, который описывает формы и элементы изображения с помощью элементов xml, таких как <circle cx="30" cy="30" r="10"/>. SVG-графика имеет широкий спектр типов элементов, таких как прямоугольники, круги, линии, многоугольники, кривые Безье, текст и многое другое. Поскольку на самом деле SVG - это просто XML, его легко сформулировать на любом языке программирования. Это ничем не отличается от создания других типов строковых объектов.

SVG, чистый, лаконичный визуальный формат

Давайте взглянем на базовую графику, чтобы получить представление о том, как будет выглядеть файловый документ SVG.

<svg version="1.1"     xmlns="http://www.w3.org/2000/svg">  
     <path d="M100 0 L 100 20 L 20 10 L 10 0 Z" fill="#aaa"/>     
     <circle cx="10" cy="10" r="2" fill="red"/>  
     <circle cx="30" cy="10" r="2" fill="red"/>  
     <circle cx="50" cy="10" r="2" fill="red"/>
</svg>

Первый и самый высокий охватывающий элемент - это элемент <svg>. Этот элемент хранит версию спецификации SVG, которой придерживается документ, а также схему XML для SVG, которой должно следовать средство визуализации. Эти два поля необходимы только для файлов SVG, которые открываются и отображаются в локально размещенных системах. Когда SVG встроен в html-документ, эти поля не нужны, поскольку версия спецификации SVG определяется браузером. Элемент <svg> также может содержать поля, определяющие ширину и длину всего рисунка. Хотя они необходимы и полезны в ситуациях, когда графика создается таким образом, чтобы она вписывалась в веб-страницу, примеры в этой статье предназначены только для демонстрации приблизительных визуализаций для удобства программистов. Таким образом, можно оставить неограниченную ширину и длину.

Элемент <path/> описывает форму многоугольника, заполненную шестнадцатеричным цветом #aaa. Пути образуют форму с помощью серии команд, читаемых в поле d. В этом конкретном примере M означает «перейти к» с точки зрения перемещения курсора пути к некоторой координате xy. L означает «линия до», а Z означает «закрытый путь», или чтобы соединить форму и заполнить ее. У элемента пути также есть инструкции по кривой, но они выходят за рамки данного руководства.

Третий элемент, который вы можете увидеть в этом примере, - это элемент <circle/>. Определяемый именем, он рисует круг на графике с некоторой центральной точкой в ​​координате xy и некоторым радиусом. Наконец, давайте посмотрим, как выглядит этот пример SVG после рендеринга:

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

Создание SVG с помощью Python

В целом документ SVG - это просто строка XML. Такие строки довольно легко построить с помощью специального класса в Python. Подобно тому, как разные элементы SVG преобразуются в разные формы и несут разные визуальные эффекты, классы в Python представляют типы с разными методами и свойствами. Следовательно, каждый используемый нами тип SVG-элемента может быть инкапсулирован в класс. Вот один для элемента <circle>:

class SVGCircle(object):
def __init__(self, x = 0, y = 0, r = 1, f = 'red'):
  self.x = x
  self.y = y
  self.r = r
  self.f = f
def __repr__(self):
  return '<circle cx="{}" cy="{}" r="{}" fill="{}"/>'.format(
        self.x, self.y, self.r, self.f
   )

Вышеупомянутый класс содержит спецификации SVG для круга и интерполирует их в строковой форме. Затем нам нужен класс, который может инкапсулировать весь документ SVG и, возможно, другие типы элементов SVG:

class SVG(object):
 
 def __init__(self):
  self.objs = []
 def __repr__(self):
  return "\n".join(["<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">",
          "\n  ".join([str(obj) for obj in self.objs]),
          "</svg>"])
 def circle(self, x, y, r, f):
   self.objs.append(SVGCircle(x, y, r, f))

Класс SVG позволяет динамически создавать строку документа SVG с circle элементами. При каждом вызове можно изменять положение или даже размер круга, а также цвет. Для начала давайте в качестве базового примера напишем код, который будет рисовать пунктирную диагональную линию. Нам не нужен элемент line из SVG, мы можем использовать его с уже написанным кодом для circle.

a = SVG()
for i in range(0, 100, 4):
   a.circle(i, i, 2, "blue")
print(a)

Этот код создаст строку SVG, которая выглядит как на следующем рисунке:

Такой файл svg можно создать, запустив сценарий python с указанным выше кодом и сохранив вывод в файле, например, с помощью команды $ python svg.py > foo.svg или написав дополнительный код Python для записи строки в файл. В следующих примерах будут рассмотрены другие применения SVG-визуализации кода.

Визуальный контроль ввода и вывода

Далее мы рассмотрим пример, в котором визуализация данных действительно может дать представление о поведении кода. Для этого будут сравниваться две хэш-функции: встроенная функция Python hash и специально написанная реализация хеш-функции djb2. Хешируются строки всех целых чисел от 0 до 200. В результирующем SVG координата x для каждой точки будет входом, а координата y - выходом. Функция djb2 выглядит так:

def djb2(string):
   digest = 5381
   for i in range(len(string)):
       digest = ((digest << 5) + digest) + int(string[i])
   return digest

Цвет встроенной хеш-функции Python будет синим, а для djb2 - красным. Также важно помнить, что система координат для SVG имеет нисходящую ось Y, а не восходящую. Выполняем следующий код:

a = SVG()
for i in range(200):
   a.circle(i, djb2(str(i)) % 200, 1, "red")
   a.circle(i, abs(hash(str(i))) % 200, 1, "blue")
print(a)

Создает следующий рисунок:

Ось X на этом графике идет слева направо от меньшего входа к большему. Ось Y идет сверху вниз, чем ниже положение Y, тем больше значение на выходе. Встроенная хеш-функция Python, синие точки, генерирует большую энтропию и распределяется между выходными данными. Это означает, что в реальном варианте использования хеш-функция Python создала бы меньше конфликтов, если бы входные данные были очень похожими, например "4" и "5". Хэш-функция djb2, красные точки, как правило, группируются по регионам и не отличаются большой вариативностью. Если входные данные очень похожи, это приведет к большему количеству столкновений.

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