19 заметок с тегом

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

Трансляторы кода

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

Чтобы процессор видел код на пайтоне в привычных ’0101’, существуют трансляторы языков программирования.
Трансляторы — программы-переводчики из одного ЯП в машинный или в любой другой код. Их разделяют на два вида: интерпретатор и компилятор. На вход оба получают код и данные, но в работе они отличаются.

Компилятор

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

  1. Синтаксический анализ. Он пробегается по коду и проверяет правильность написания команд, потом создаёт таблицу для ссылок на функции и переменные, чтобы не было ошибок имён и вызовов.
  2. Семантический анализ. Раскладывает код на составляющие и находит начальную точку.
  3. Распределение памяти. Компилятор выделяет память под переменные, функции и другие данные программы.
  4. Компиляция. Переводит исходный код в машинный и отдаёт EXE-файл для винды и APP-файл для мака.

Синтаксический анализ

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

При синтаксическом анализе может возникнуть проблема, когда две переменные в разных местах имеют одно и тоже название и тип.
Если они существуют в своих областях видимости, то всё нормально — в памяти они не пересекаются и не мешают друг другу. Но если в одной области видимости сделать две одинаковых переменных, компилятор будет ругаться и не запустит программу.
Например

------
def print_emoji():
    emoji = '¯\\_(ツ)_/¯'
    print(emoji)
------
emoji = 't(-_-)t'
print(print_emoji(), emoji)

Семантический анализ

На основе токенов из синтаксического анализа строится семантическое дерево, для того чтобы компилятор позже смог сгенерировать код.

Вычисления начинаются на этапе компиляции, тут просто подготовка

Распределение памяти

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

Интерпретатор

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



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

 47   1 мес   языки программирования

Парадигмы программирования

Когда мы проходили пайтон, у нас была статья про два стиля описания кода: декларативный и императивный.
Вообще стилей много, но условно их разделяют на шесть стилей: функциональный, структурный, модульный, ООП, декларативный и императивный.

Это не значит, что в программе нужно использовать только один, но для каких-то задач подходит только функциональный или только декларативный, как в SQLe.

Функциональное программирование

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

from math import exp, sin, sqrt, fabs


def get_exp(x):
    return exp(sin(sqrt(fabs(x))) + 1)


print(get_exp(6))

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

Для реального примера можно представить банк: чтобы получить справку 414, нужно получить справку 519, для неё нужна 139, а для неё паспорт и так далее. Вызов выглядит вот так

справка_414(справка_519(справка_139(паспорт)))

Структурное программирование

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

a = input('Первое число: ')
b = input('Второе число: ')
c = input('Третье число: ')

if a > b and a > c:
    print('Самое больше число {}'.format(a))
elif b > c and b > a:
    print('Самое большое число {}'.format(b))
else:
    print('Самое большое число {}'.format(c))

Модульное программирование

Программу делят на отдельные файлы и вызывают из главного когда нужно.
В модульном программировании данные распределяются по модулям и используются в одном главном файле.
Когда мы писали бота, мы его дробили на файлики для прокси, для ответа на сообщения и другие. Эти файлы — модули, которые вызывались в главный файл. Прокси ченджер отвечал за поиск и хранение работающих прокси, лист контрол за управление списком покупок и напоминаниями, а логер — за ведение отчётов.

import proxy_changer
import list_control
import loger
'Дальше по коду идут вызовы через точку loger.функция, list_control.функция ,proxy_changer.функция'

Объектно-ориентированное программирование

Код состоит из объектов — классов. Классы работают как рецепты для еды. У них есть название, есть ингредиенты и способы готовки. Но это рецепт, чтобы его приготовить, нужно вызвать рецепт из main-блока, то есть — создать объект этого рецепта.
Для примера сделаем крабсбургер

class Крабсбургер
  {
  булочки = 2
  сыр = 2
  котлетки = 1
  томаты = 2
  салат = 1
  кетчуп = 1,25
   
  def готовка():
    'положить одно на другое и прогреть'
}

мой_перекус = Крабсбургер()

С ООП мы сталкивались когда писали бота.

# Создаём объект бота через класс TeleBot.
bot = tb.TeleBot('Токен бота', threaded=False)
# Используем метод send_message, который теперь доступен у объекта бота
bot.send_message(message.chat.id, about_me)

Декларативный

Описывает сам объект, что он из себя представляет и его характеристики.
Например, мы хотим бутерброд. «ЗАХААААР, сделай мне бутерброд». Он идёт на кухню и делает бутерброд как умеет.
Декларативный факториал

def factorial(num):
    if (num == 1):  # Это условие для факториала единицы 1! = 1 
        return 1

    # А это вычисление любого факториала
    num = num * factorial(num - 1)
    return num

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

Описывает как найти объект или вычислить его значение.
Опять таки мы хотим бутерброд. «ЗАХАААР, иди на кухню, возьми и порежь хлеб, намажь на него масло и положи сверху три кусочка колбасы.» Идёт и делает по вашему алгоритму, а не как он умеет.
Императивный факториал

def factorial(num):
    counter = 1  
    result = 1  

    while(counter <= num):  # Выполняется пока счётчик не дойдёт до края, а если край один — сразу выходит из цикла
        result *= counter  # Сокращённое выражение result = result * counter
        counter += 1  # Сокращённое выражение counter = counter + 1

    return result

В следующей статье расскажем про компиляторы и интерпретаторы.

 40   2 мес   языки программирования

Напоминания от бота

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

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

В стандартной библиотеке пайтона есть библиотека для работы с потоками — threading. В ней есть класс Timer, его будем использовать для таймера. Для работы таймера нужно два аргумента: время таймера в секундах и функция для вызова по окончанию таймера.
Импортируем в файл list_control.py библиотеку для работы со временем и потоками:

from datetime import datetime, date, time
from threading import Timer

Функция для вычисления разницы

Назовём функцию get_time_delta. На вход она получает обязательный аргумент время и необязательный аргумент дату. Так получается из-за разных напоминаний, вот пример: во фразе «напомни через две минуты» бот найдёт только время, а во фразе «напомни завтра в два часа дня» дату и время.

От сервера ДФ дата и время приходит в текстовом формате, их нужно перевести в формат даты или времени.
Потом нам нужно собрать дату и время в один формат — datetime и вычислить текущий datetime, чтобы получить время для таймера в секундах.
Проверку даты мы вынесли в отдельную функцию, чтобы не дублировать код.
Получилась вот такая функция

def get_time_delta(time_notify, date_notify=None):
    def calculating_date():
        if date_notify is None:
            return date.today()
        return date.fromisoformat(date_notify)

    # Переводим время в формат времени
    parsed_time_notify = time.fromisoformat(time_notify)
    # Переводим дату в формат даты
    parsed_date_notify = calculating_date()
    # Собираем дату и время в формат даты и времени
    parsed_datetime = datetime.combine(parsed_date_notify, parsed_time_notify)
    datetime_now = datetime.now()
    delta = parsed_datetime - datetime_now
    # Получаем разницу во времени в секундах
    delta_seconds = delta.seconds

    return delta_seconds

Ставим таймер

Назовём функцию set_notify, она будет принимать три аргумента: разницу времени, объект бота и айди чата.
В функции set_notify сделаем ещё одну, она будет отправлять пользователю напоминание о списке и прикреплять его к напоминанию.

def notify():
        shop_list = list_from_file()
        markup = create_buttons(shop_list)
        bot.send_message(chat_id, 'Ты просил напомнить про покупки.\rВот список',
                         reply_markup=markup)
        # Удаляем таймер
        timer.cancel()

Теперь создаём таймер и запускаем его. В итоге файл list_control.py выглядит вот так

Прикручиваем к главному файлу

Интент с напоминанием о списке активирует такой action: create_list.shop_list.notify.
Добавляем в главный файл такие блоки

# Создаем напоминание
    elif action == 'create_list.shop_list.notify':
        # Если бот не смог распарсить время
        if not parameters['time']:
            bot.send_message(chat_id, 'Не могу прочитать время')

        # Если напоминание поставлено без даты
        if not parameters['date']:
            bot.send_message(chat_id, 'Напомню в {}'.format(parameters['time']))
            time_delta = list_control.get_time_delta(parameters['time'])
            list_control.set_notify(time_delta, bot, chat_id)

        # Если напоминание поставлено с датой и временем
        else:
            bot.send_message(chat_id, 'Напомню {} в {}'.format(parameters['date'],
                                                               parameters['time']))
            time_delta = list_control.get_time_delta(parameters['time'], parameters['date'])
            list_control.set_notify(time_delta, bot, chat_id)

Файл bot.py выглядит теперь вот так

Теперь у вас есть бот для помощи с покупками на ИИ. Рабочий код лежит в нашем репозитории на гитлабе, можно скачать, поставить свои токены и пользоваться. Если что-то не работает, пишите @cmp_sci.

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

В следующей статье расскажем про парадигмы языков программирования.

 41   2 мес   python   языки программирования
Ранее Ctrl + ↓