Получи случайную криптовалюту за регистрацию!

Holy Python

Логотип телеграм канала @holy_python — Holy Python H
Логотип телеграм канала @holy_python — Holy Python
Адрес канала: @holy_python
Категории: Технологии
Язык: Русский
Количество подписчиков: 611
Описание канала:

Станьте "папой" питона!
@econature_official

Рейтинги и Отзывы

2.00

2 отзыва

Оценить канал holy_python и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

0

4 звезд

0

3 звезд

1

2 звезд

0

1 звезд

1


Последние сообщения

2022-10-12 13:07:38 #ошибки

Введение

Сегодня мы поговорим о минусах и вреде такого известного фреймворка как Django.

Немного уточнений:

1. Этот пост был сделан для того чтобы вы ознакомились с минусами данного фреймворка, я не заставляю вас не использовать его.
2. С данным фреймворком ознакомится в любом случае придется, так как бизнес все ещё его использует, однако предлагать ее внедрение в новый проект - плохая идея.
3. Если начать с джанги и не смотреть в сторону других фреймворков, будет складываться ощущение, что в ней все сделано правильно.

Проблемы архитектуры

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

1. Нарушены многие принципы написания кода и архитектуры(SOLID, CCP, принцип ацикличности зависимостей)
2. Используются некоторые антипаттерны(например: глобальные переменные, хранение настроек ввиде большого количества переменных в одном файле, подробнее о таких антипаттернах в: https://t.me/holy_python/46)
3. Сильная связность компонентов, отсутствие гибкости, зависимость от преждевременных решений. При замене какого-либо решения используемого Django, сломаются другие компоненты. Пример: Если мы поменяем Django ORM на SQLAlchemy, то сломается Django Admin.

Проблемы ORM

Django ORM, содержит массу серьёзных проблем, которые усложняют разработку.

Отсутствует:

1. Иерархические и рекурсивные запросы в SQL(CTE)
2. Возможность кастомизации group by
3. Виртуальные ключи
4. Возможность разделения таблиц на группы.
5. Виртуальные таблицы
6. Триггеры(особые разновидности хранимых процедур, которые автоматически выполняются при возникновении события на сервере базы данных)
7. Функции
8. Поддержка only по умолчанию
9. Асинхронность для БД(пытаются добавить ввиде костылей в 4.x)

Другие проблемы:

1. Используется спорный паттерн ActiveRecord. Ведёт к нарушению принципа SRP, сильно связан с бизнес логикой. Если вы захотите использовать другую абстракцию для хранения данных придётся проводить рефакторинг.

2. Отсутствие возможности отключить автокоммит.

Заключение

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

P. S. Если вы знаете другие значительные минусы Django добро пожаловать в комментарии.

Дополнительные материалы

https://www.djangoproject.com
https://habr.com/ru/post/198450
https://t.me/rudepython/168523
494 viewsedited  10:07
Открыть/Комментировать
2022-09-26 19:29:18 Введение

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

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

План исправления ошибки

Представим вы запустили вашу программу и увидели что она работает неверно. Данные которые мы должны получать на выходе и данные которые возврашает программа не совпадают. Что делать в данном случае? Следуйте этому плану поиска и исправления бага:

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

1. Стабилизация ошибки(если возможно) - некоторые ошибки возникают не постоянно, их нужно стабилизировать - сделать так чтобы одна и таже ошибка появлялась всегда.

2. Поиск места возникновения ошибки
    a. Пробуем первые входные данные, анализируем выдвигаем гипотезы. Создаем новые входные данные, передаём в программу, на основе всех результатов ищем корреляции, проводим дебаг и выдвигаем гипотезы.
   b. Выдвигаем гипотезы, на основе предыдущих данных, тестируем, если гипотеза подтвердилась ищем причину возникновения ошибки путем дебага, затем приступаем к пункту 3 иначе повторяем процесс.

3. Исправление ошибки. Мы уже знаем проблемное место - 80% работы выполнено, теперь остаётся исправить эту ошибку.

4. Тестируем новое решение.

5. Проверьте все места в проекте, где использовался исправленный вами код. Хорошо спроектированая система не пострадает, однако в большинстве реальных проектов могут появиться побочные эффекты.

6. Ищем схожие возможные проблемы в программе.

Типичные антипаттерны

1. Виноваты все кроме моей программы. Иногда люди начинают обвинять все подряд кроме своей программы(интерпретатор, библиотеку, IDE). И так бывает очень сложно найти и исправить баг в программе, но если ты считаешь что проблема не ней, то это вообще не реально.

2. Исправление конкретного случая возникновения проблемы. Вместо поиска и исправления всей проблемы, новички часто пишут условие которое обрабатывает конкретный случай и возвращают "правильные данные". Такой подход приводит к страшным последствиям в будущем.

3. Поиск гаданием. Вместо применения научного метода в данном случае человек расставляет принты и "трай эксепты" по всей программе, а потом гадает где же проблема. Крайне не эффективный подход.

P. S. Если вы знаете, как дополнить пост, в частности пункт 1 и часть "типичные антипаттерны", добро пожаловать в комментарии.
794 viewsedited  16:29
Открыть/Комментировать
2022-09-04 20:24:45 Дополнительные материалы

https://github.com/Tishka17/aiogram_dialog
https://bit.ly/3CXGBtO
https://habr.com/ru/post/465835/
1.1K views17:24
Открыть/Комментировать
2022-09-04 20:24:20 В библиотеке aiogram dialog есть диалог менеджер, он реализует базовую функциональность управления диалогами: старт разным способом, доступ к контексту и т.п.
Также есть менеджер - это временный объект. Он создаётся на время обработки конкретного события. Соответственно, его надо постоянно пересоздавать.
В какой-то момент я решил что неплохо дать возможность что-то в менеджере переопределить, соответственно нужно и фабрику менять.
Для решения этой проблемы абстрактная фабрика подошла лучше всего.
1.1K viewsedited  17:24
Открыть/Комментировать
2022-09-04 20:24:20 #паттерны

Введение

Сегодня мы рассмотрим паттерн проектирования "Абстрактная фабрика".

Классификация

Тип: Порождающий

Определение: Абстрактная фабрика - это порождающий паттерн проектирования, который предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов.

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

Из чего состоит и как работает данный паттерн

1. Абстрактного класса/Интерфейса абстрактной фабрики. Содержит абстрактные методы которые возвращают абстрактные продукты, связанные одной конпцецией.

class AbstractFactory(ABC):
@abstractmethod
def create_product_a(self):
...

@abstractmethod
def create_product_b(self):
...

2. Конретные фабрики. Конкретные фабрики реализут операции которые создают конкретные продукты.

class ConcreteFactory1(AbstractFactory):
def create_product_a(self):
return ConcreteProductA1()

def create_product_b(self):
return ConcreteProductB1()

class ConcreteFactory2(AbstractFactory):
def create_product_a(self):
return ConcreteProductA2()

def create_product_b(self):
return ConcreteProductB2()

3. Абстрактные классы продуктов. Реализуют интерфейс для всех конретных продуктов своего семейства.

class AbstractProductA(ABC):
@abstractmethod
def very_important_super_function_a(self) -> str:
...

class AbstractProductB(ABC):
@abstractmethod
def very_important_super_function_b(self) -> str:
...

4. Конкретные продукты. Реализуют абстрактные продукты. Продукты одного семейства не могут взаимодействовать, с продуктами другого семейства.

class ConcreteProductA1(AbstractProductA):
def very_important_super_function_a(self) -> str:
return "Product A1"


class ConcreteProductA2(AbstractProductA):
def very_important_super_function_a(self) -> str:
return "Product A2"

class ConcreteProductB1(AbstractProductB):
def very_important_super_function_b(self) -> str:
return "Product B1"


class ConcreteProductB2(AbstractProductB):
def very_important_super_function_b(self) -> str:
return "Product B2"

5. Клиент. Клиентский код работает исключительно с абстрактной фабрикой и абстрактными продуктами.

def client(factory: AbstractFactory) -> str:
return factory.create_product_a().very_important_super_function_a()

for factory in (ConcreteFactory1, ConcreteFactory2, ...):
client(factory)

Плюсы данного паттерна

1. Реализует принцип открытости/закрытости.

2. Упращает поддержку кода.

3. Выделяет код производства продуктов в одно место, упрощая поддержку кода.

Минусы данного паттерна

1. Снижает читаемость программы из-за введения множества дополнительных классов.

Пример и задача

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

1. Лошадка
2. Зайчик
3. Шарик

Всё было бы просто однако Деду Морозу нужны игрушки в новогоднем стиле, а пасхальному кролику в пасхальном.
Создайте абстрактную фабрику игрушек и спасите эти праздники!

Пример из реального кода

Пример из реального кода предоставил @Tishka17. Он разработал замечательный фреймворк aiogram_dialog для разработки интерактивных диалогов и меню в телеграмм ботах, как обычное приложение с графическим интерфейсом.

Вот здесь он использует данный паттерн: https://github.com/Tishka17/aiogram_dialog/blob/develop/aiogram_dialog/manager/manager_middleware.py#L23

Вот его объяснение, какую задачу он решает в данном случае:
985 views17:24
Открыть/Комментировать
2022-08-18 22:39:49 Введение

Наверняка если вы изучаете питон вы слышали об одной достаточно известной аббревиатуре - GIL,
если вы немного углублялись в тему вы могли узнать что это штука как-то связана с потокам

Сегодня мы попробуем разобраться что это, как это работает и зачем это нужно питону.

GIL

GIL(Глобальная блокировка интерпретатора) - это один из способов синхронизации потоков, который позволяет только одному потоку управлять интерпретатором Python. Задача GIL сделать интерпретатор CPython потокобезопасным.

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

Казалось бы, зачем вводить такие ограничения? Это же может очень сильно навредить производительности многопоточных программ?

Да, это очень резонные вопросы к данному подходу, GIL и вправду очень часто является "бутылочным горлышком" производительности многопоточных программ. Однако данный механизм не был добавлен просто так, дело в том, что GIL решает одну довольно серьёзную проблему о которой мы поговорим ниже.

Проблема гонки

У каждого объекта в питоне есть свой "счётчик ссылок", который отслеживает количество ссылок указывающий на данный объект.

Если счётчик = 0, то память занятая объектом высвобождается.

Пример:


import sys

x = 3
a = x
print(sys.getrefcount(x)) # 38 refs
del a
print(sys.getrefcount(x)) # 37 refs

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

Именно эту проблему в случае питона и решает GIL.

Если нет параллельных потоков, то и никаких проблем с ними не будет)

Как работает GIL?

Когда какой-либо поток захватывает GIL, остальные потоки которые хотят захватить его вынуждены ждать. Переключение между потоками может происходить: когда активный поток пытается осуществить ввод-вывод, по исчерпании лимита выполненных инструкций, либо по таймеру

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

Почему в питоне используется именно GIL и его не удалили/заменили?

1. GIL очень прост в реализации
2. GIL повышает производительность однопоточных программ.

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

Плюс, даже если будет найдено адекватное решение проблемы гонки, которое удовлетворяет всем требованиям питона, оно сломает существующие расширения на C, которые зависят от GIL.

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

Дополнительные материалы

https://wiki.python.org/moin/GlobalInterpreterLock
https://towardsdatascience.com/python-gil-e63f18a08c65
1.3K viewsedited  19:39
Открыть/Комментировать
2022-08-04 12:35:13 0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4
0.4 * 2 = 0.8
...

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

После сохранения числа 0.8 в память. Мы получим, вот такой результат:


0 01111110 10011001100110011001101


А при переводе данного числа обратно мы получаем непонятное число с большим количеством нулей и цифр:

0.800000011920928955078125

Теперь вы знаете, как на самом деле хранятся дробные числа и почему 0.1 + 0.2 != 0.3 с точки зрения компьютера.

Дополнительные материалы

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

Пример:


print(0.8) -> 0.8
print(f"{0.8:.20f}") -> 0.80000000000000004441


Когда нужны точные вычисления(например в области финансов), рекомендую использовать стандартную библиотеку decimal.

При каких-либо вычеслениях я настоятельно не рекомендую обрезать число, так как это приводит к ошибкам и потере точности.

https://docs.python.org/3/library/decimal.html
https://0.30000000000000004.com/
1.3K views09:35
Открыть/Комментировать
2022-08-04 12:35:13 Введение

Давайте напишем простейшую программу на питоне:

print(0.1 + 0.2)

Неопытные программисты с уверенностью ответят что выводом будет 0.3. Однако как вам известно это не так, выводом данной программы будет непонятное число с большим количеством нулей и четвёркой в конце:

0.30000000000000004

И это проблема не питона, вы можете запустить такую же программу на js или c++. Вывод будет таким же.

Что же происходит на самом деле?

Давайте разбираться.

IEEE

Чем отличается целое число от дробного с точки зрения хранения?

Правильно в дробном числе есть точка. Точку не возможно представить с помощью нулей и единиц, поэтому нужна другая, специальная форма хранения такого числа. Данную задачу решил "Институт инженеров электроники и электротехники"(IEEE), он создал стандарт хранения дробных чисел - IEEE 754.

Как перевести дробное число из десятичной системы счисления в двоичную?

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

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

Начнём с целой части. Здесь все просто, мы делим число на 2 пока последнее частное не станет меньше двух. Последнее частное и все остатки записываем в обратном порядке.

Пример:

3 ÷ 2 = 1 (1)

3 в двоичной системе счисления - это 11

Перейдём к дробной части. Здесь вместо деления нам нужно умножение. Мы умножаем дробную часть на 2 пока не получим в ответе единицу. А затем просто склеиваем целые части ответов.

Пример:

0.25 * 2 = 0.5
0.5 * 2 = 1.0

0.25 в двоичной системе - это 01

IEEE 754

Итак, мы успешно перевели дробное число в двоичный вид: 11.01

Теперь нам нужно как-то хранить наше число. Для этого стандарт предлагает использовать экспоненцальную запись, которая соответствует, вот этой формуле:


(-1)^s*1.M*10^E

s - знак числа
m - мантиса(дробная часть числа)
e - это количество знаков на которое нужно сдвинуть точку, чтобы перед ней осталась только одна единица.

Чтобы сохранить число в такой форме стандарт предлагает 3 основных формата хранения:

1. Одинарный - 32 бит
2. Двойной - 64 бит
3. Четырехкратный - 128 бит

Чем больше размер тем выше точность хранимого числа.

В качестве простого примера рассмотрим 32 битный формат.

Первый бит - бит знака. Если он равен нулю то число положительное, если он равен единице то число отрицательное.

Следующие 8 бит - выделены для хранения степени. Чтобы определить знак степени, нам нужно прибавить 127 к значению степени. Если число будет больше 127, то степень положительная, иначе - отрицательная.

Оставшиеся 23 бита - выделены для хранения мантиссы. Если мантисса меньше 23 бит, оставшиеся пространство заполняют нулями.

Вы можете спросить, а где хранится целая часть и основание степени?

Ответ прост - целая часть всегда равна единице, а основание степени всегда равно десяти => для экономии памяти их можно не хранить!

Давайте сохраним наше число в памяти.

1. Сдвинем точку на 1 знак влево 1.101. e = 1
2. Первый бит знака равен нулю, так как число положительное
3. e + 127 = 128 => число положительное.
4. Переведём 128 в двоичную систему счисления.

128 ÷ 2 = 64 (0)
64 ÷ 2 = 32 (0)
32 ÷ 2 = 16 (0)
16 ÷ 2 = 8 (0)
8 ÷ 2 = 4 (0)
4 ÷ 2 = 2 (0)
2 ÷ 2 = 1 (0)

128 в двоичной системе счисления -
10000000
5. Запишем 10000000 в 8 бит предназначеные для степени.
6. Запишем 101 в 23 бита предназначеные для хранения дробной части. Оставшиеся биты заполним нулями.
7. Мы сохранили наше число в памяти!

Вот результат выполнения всех шагов:


0 10000000 10100000000000000000000


Отлично, теперь попробуем перевести сохранённое число обратно:

s = 0 (знак положительный)
m = 101
e = 1

-1^s * 1.m * 10^e = 11.01

Осталось перевести 11.01 в десятичную систему счисления и мы получим наше число - 3.25.

Проблема

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

Пример:
1.0K views09:35
Открыть/Комментировать
2022-07-26 21:27:22 #паттерны

Введение

Сегодня мы рассмотрим паттерн проектирования "Адаптер".

Классификация

Тип:
Структурный

Определение: Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.
Адаптер выступает прослойкой между объектами превращая вызовы одного в понятные для другого.

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

Из чего состоит и как работает паттерн адаптер

1. Клиент. Описывает бизнес логику программы. Работает с адаптером и другими объектами.


adapter = Adapter()
result = adapter.do() + "5"


2. Интерфейс с которым может работать клиентский код.


class Test1:
def do(self) -> str:
return "3"


3. Класс, который имеет нужную клиенту логику, однако клиент не может с ним общаться, так как интерфейс данного класса ему не понятен.


class Test2:
def specific_do(self) -> float:
return 3.4


4. Адаптер - класс который помогает клиенту использовать функционал незнакомого ему сервиса в правильном формате. Реализует клиентский интерфейс. Содержит ссылку на незнакомый сервис. Адаптер при получении вызовов от клиента через клиентский интерфейс и переводит их в вызовы незнакомого сервиса в правильном формате.


class Adapter(Test1, Test2):
def do(self) -> str:
return f"Translated: {round(self.specific_do())}"


Уточнение: Приложение должно использовать объект адаптера только через клиентский интерфейс. Это позволит легко создавать и изменять адаптеры в будущем.

Плюсы данного паттерна

1. Скрывает все "низкоуровневые" преобразования интерфейсов от клиента. Реализует принцип абстракция.

Минусы данного паттерна

Таковых я не обнаружил

Пример и задача

В качестве примера возьмём класс с методом do который возвращает небольшой текст. Также есть класс с методом secret_do который возвращает другую строку, зашифрованную шифром Цезаря со сдвигом 5, которая ещё и полностью развёрнута.

В виде клиента выступает функция которая постит текст в ваш блог.(Можете просто создать функцию которая выводит переданный в неё текст). Естественно она должна принимать только расшифрованный текст.

Реализуйте адаптер для второго класса, который спасёт ваш блог от зашифрованных постов.

Пример из реального кода

https://github.com/aiogram/aiogram/blob/b190bbba1915ed3b7f311a780f34723ebd6b5acd/aiogram/contrib/fsm_storage/redis.py#L280

Вот его объяснение какую задачу решает адаптер в данном случае:

Здесь адаптер дает возможность контроллеру хранилища (RedisStorage) работать с первой версией редиса, т.е. адаптирует aioredis-v1, там еще есть адаптер для aioredis-v2, но он в отличие от первой версии адаптирует только создание клиента редиса


Дополнительные материалы

https://refactoring.guru/ru/design-patterns/adapter
https://habr.com/ru/post/85095/
959 viewsedited  18:27
Открыть/Комментировать
2022-07-18 15:21:24 #паттерны

Введение

Сегодня мы рассмотрим паттерн проектирования "Прототип".

Классификация

Тип: Порождающий

Определение: Прототип - это порождающий паттерн проектирования, который позволяет копировать объекты не вдаваясь в подробности их реализации.

Из чего состоит и как работает данный паттерн

1. Абстрактного класса/Интерфейс всех прототипов. Практически всегда содержит только один абстрактный метод - clone.


class BasePrototype(ABC):
@abstractmethod
def clone(self) -> object:
...


2. Конкретный прототип. Реализует операцию клонирования самого себя.


class Test(BasePrototype):
def init(self, test1, test2):
self.test1 = test1
self.test2 = test2

def clone(self):
return Test(self.test1, self.test2)


3. Клиент. Создаёт копии объектов.


test = Test(1, 2)
test2 = test.clone()


4. Опционально - хранилище прототипов. В нём удобно хранить вариации объектов, которые по разному настроены.

В питоне данный паттерн с нуля писать не нужно - есть готовые методы copy.copy() и copy.deepcopy(), рекомендую использовать их.

Плюсы данного паттерна

1. Уменьшает количество кода, ускоряет создание объектов

2. Позволяет клонировать объекты без привязки к конкретному классу.

Минусы данного паттерна

1. Прямое использование в клиенте может ухудшить читаемость кода.

Пример и задача

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

Дополнительные материалы




https://refactoring.guru/ru/design-patterns/prototype/python/example
1.0K viewsedited  12:21
Открыть/Комментировать