Языки программирования

В этом разделе мы рассмотрим какими они бывают. И снова давайте посмотрим на определение из Википедии:

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

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

ПРЕДУПРЕЖДАЮ! Объяснять я буду своим языком, но и значительно проще чем обычно.

Высокоуровневые/низкоуровневые языки программирования

Высокоуровневые

К высокоуровневым языкам мы можем отнести те, что предоставляют нам высокие абстрации такие как:

  • Структуры данных
  • Типы
  • Классы
  • Методы
  • Удобное управление памятью
  • Методы обработки ошибок

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

Каких- то жестких границ между Высокоуровневыми и низкоуровневыми языками нет, вполне часто язык C относят к низкоуровневым, так же и Go python разработчик может отнести к низкоуровневым, вот только с очень большой натяжкой.

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

Вводят еще один класс языков - Сверхвысокоуровневые, в этот класс относят и Python.

Низкоуровневые языки программирования

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

> положи в регистр A значение 10(естественно в бинарном представлении)
> положи в регистр B значение 2
> выполни операцию возведения в степень
> возьми полученное значение из регистра A и положи по такому-то адресу в оперативную памятью
...

Пример низкоуровневых языков программирования:

  • Ассемблер
  • Машинный код

Ассемблерный код - это доступное для понимания человеком представление машинного кода.

Компилируемые и интерпретируемые языки программирования

В языке программирования мы пишем инструкции, которые в дальнейшем будут исполняться нашей машиной(на CPU). Есть проблема - CPU не может понять что мы написали на Python, для того чтобы привести наши инструкции в машинный либо байт-код используется промежуточный инструмент называемый компилятором.

Компилируемые языки

В абзаце выше вы могли видеть термины машинный код и байт-код

  • машинный код - представляет из себя список команд исполняемый непосредственно процессором
  • байт-код - близкий к машинному код, но он будет интерпретироваться интрерпретатором или виртуальной машиной

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

  1. Лексический анализ - На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
  2. Синтаксический (грамматический) анализ - Последовательность лексем преобразуется в дерево разбора.
  3. Семантический анализ - Дерево разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
  4. Оптимизация Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
  5. Генерация кода Из промежуточного представления порождается код на целевом машинно-ориентированном языке.

Интересной технологией является JIT-компиляция. В этом случае byte-code превращается в машинный при надобности, машинный код кешируется и следующее обращение к нему он уже скомпилирован. Хорошей стороной подобного исполнения программы является то, что мы можем на ходу оптимизировать ее + выгружать ненужный код из оперативной памяти.

Линки

Интерпретируемые языки

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

Но как вы знаете, вы не компилируете код на Python, а сразу же его исполняете. Исполнением вашего кода занимается интерпретатор(скорее всего CPython). Давайте поверхносто посмотрим на то, как исполняется ваш код на языке Python интерпретатором CPython

Интерпретатор - Программа исполняющая другие программы. Если говорить упрощенно, то его работа заключается в том, чтобы:

  1. Загрузить исполняемый файл
  2. Перевести ваши инструкции в промежуточное представление(byte-code)
  3. PVM(Python Virtual Machine) занимается тем, что выполняет комманды из byte-cod'а

А вот определение из "Лутца":

Интерпретатор – это такой модуль, который исполняет другие программы. Когда вы пишете код на языке Python, интерпретатор Python читает вашу программу и выполняет составляющие ее инструкции. По сути дела интерпретатор – это слой программной логики между вашим программ- ным кодом и аппаратурой вашего компьютера.

Разделение языков по парадигмам программирования

Парадигма программирования — это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером

И снова скучное определение. Если по простому, то парадигма определяет:

  • Как мы пишем код
  • Как мы разделяем логику
  • Каким образом мы будем манипулировать данными
  • В каком виде будем эти данные хранить

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

Давайте приведу пример того как мы можем 2 способами отфильтровать список каких-то объектов(в нашем случае котов).

Пример в функциональном стиле

# создадим список котиков
cats = [
{"name": "Barsik", "age": 3},
{"name": "Luparik", "age": 6},
{"name": "Minsk", "age": 2}
]
def filtrator(cat):
return cat["age"] > 3
# и получим котов старше 3 лет в функциональном стиле
old_cats_fn = list(filter(filtrator, cats))
print(old_cats_fn)

Что мы сделали выше? Отфильтровали список используя только функции, передовая функцию filtrator как объект в функцию filter. Подобный подход называется функциональным программированием. Сущеструют исключительно функциональные языки такие как:

  • Lisp
  • Erlang
  • Elm

Но и, как мы видим, Python тоже позволяет нам писать в функциональном стиле.

Пример сортировки в императивном стиле

Более привычный стиль написания программы для новичков является процедурный/императивный стиль, в котором мы пишем список инструкций которые будут выполняться последовательно.

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

old_cats_proc = []
for cat in cats:
if (cat["age"] > 3):
old_cats_proc.append(cat)

В примере мы снова отфильтровали котов, но в другом стиле.

Интерактивный пример

Разделение языков по типизации

Фух! Предыдущие абзацы были попроще, но сейчас будет более сложная в понимании теория. Мы поговорим с вами о Системе типов

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

В основе системы типов любого языка программирования всегда лежит базисная система типов, встроенных в язык. К базисным, или встроенным, типам данных относятся такие типы, как byte, int, string, boolean, object и им подобные. На их основе среда выполнения или разработчик могут определять типы данных более высокого уровня, например Date или Array.

Типизорованные/нетипизированные языки

Давайте так:

  • В типизированных языках есть такая абстрация как тип, и с вероятность 99% вы будете работать именно на таких языках.
  • Представителями нетипизированных языков является Язык ассемблера в котором вы манипулируете только бинарными данными.

Динамическая/статическая типизация

TODO: Продолжить статью