Автор конечно старался объяснить пользу квантовых вычислений максимально понятно. Но, как мне кажется, всё равно не избежал ловушки с "троллейбусом из буханки хлеба". Хорошо что квантовые вычисления могут за одно измерение определить тип функции. Только вот зачем это делать? Как это можно использовать? И зачем изучать функцию в "чёрном ящике" вместо того, что бы вычислять известные функции?
Ответов на эти вопросы автор не дал. И потому у меня, человека не обременённого знаниями о квантовых вычислениях, так и не сложилось до конца хотя бы одного фрагмента всей картины. Я только понял, что квантовые вычисления позволяют более эффективно делать "троллейбусы".
Вроде как давно уже поддерживается описание тайп-хинтов в отдельном от кода файле с расширением .pyi. Это даже применимо для кода на Python2.
Правда поддерживать актуальность этих файлов несколько сложнее, т.к. надо не забывать вносить в них правки при изменении кода.
С моей точки зрения, наиболее жизненное использование знаний приведённых в статье — это организация скачивания из веб-приложения нескольких файлов одним запросом. Подобное реализовано в популярных облачных хранилищах файлов.
Причём реализовать это можно в виде «realtime стриминга» без предварительного создания zip-файла на диске сервера. И ещё будет возможность докачки после разрыва связи, т.к. за счёт отсутствия сжатия мы можем быстро рассчитать итоговый размер архива, и определять что должно находится по любому смещению от его начала.
Никто не мешает написать свой «базовый» класс на основе оригинального, что бы спрятать в него бойлерплейт. Я так и сделал — запихал туда обработку ошибок, логирование и хитрую инициализацию энвайромента.
Хотя… вот вам самый важный «минус» — экземпляр класс-таски в Celery создаётся только один, и потому его нельзя использовать для хранения данных выполняемой таски. Для этого можно использовать «словарик» Task.request — он создаётся каждый раз перед запуском таски.
А почему вы не воспользовались тем, что «таска» в Celery — это на самом деле и есть класс, который налету создаётся декоратором app.task() с использованием базового класса celery.Task? С давних пор можно было самому писать «таски» в виде классов унаследованных от celery.Task.
Минус был только в том, что их не так удобно регистрировать в Celery-приложении, как это делается с декоратором. И ещё в минус можно записать невозможность переопределить базовый класс через настройку Celery.
Обсуждая размеры докер-образов все рекомендуют использовать Alpine Linux. А как в этих образах обстоят дела с производительностью? Читал в интернете отзывы, и сам проверял, что, например Python, там работает заметно медленнее, чем в образе на базе Ubuntu. В качестве объяснения этого приводились такие варианты:
— все пакеты в Alpine Linux компилируются с оптимизацией по памяти и размеру, а не по скорости;
— в библиотеке musl методы для работы с памятью (выделение и освобождение) работают медленнее чем в glibc.
Ну и в контексте питона, кроме скорости, есть ещё одна заморочка — нет возможности устанавливать бинарные сборки питонячих пакетов из PyPi, т.к. они все собраны под glibc. Приходится их собирать самому из исходников.
Отличная игра — первая, которую я увидел на спектруме. Я её позднее, когда у меня появился свой аля-спектрум, прошёл полностью и по моему даже без читов.
Это не только проблема Скайпа. Сочетание Alt+{hot-key} — это давно сложившийся стандарт быстрого вызова пунктов меню приложения. Не знаю как в Windows, но в Linux (Ubuntu) у меня сразу начались косяки с этим, как только я попытался повесить переключатель языка на Alt+Shift. При быстром наборе текста на двух языках несложно нажать клавишу с буквой ещё до того как полностью отпустил Alt. В результате вместо буквы срабатывает hot-key, или фокус ввода переходит в меню.
Аналогичные проблемы у меня и с другими сочетаниями для переключения языка, которые вместе с «буквами» вызывают какие-то активные изменения фокуса ввода.
Пробовал использовать CapsLock, но и тут засада — если успеть быстро нажать букву, то вместо переключения языка произойдёт ввод этой буквы в верхнем регистре (или нижнем). На Windows без доп. приложений CapsLock не задействовать — это тоже существенный минус.
Поэтому я так и остался на сочетании Shift+Ctrl. Пришлось правда пожертвовать работоспособностью хоткеев, основанных на этом сочетании — они просто не работают у меня теперь.
Вроде как есть вариант «местная анестезия плюс что-то вроде снотворного». Не помню как правильно называется, но это не полный наркоз, который вырубает тебя так, что ты ничего не чувствуешь. У меня жена так импланты делала, что бы не терпеть часовые ковыряния в зубах. «Уснул, проснулся — гипс».
На самом деле, для простого программиста на Python, GIL не решает ни каких проблем при разработке мультипоточных приложений. В статье не совсем корректно об этом «заявляется» (или косяк перевода).
GIL защищает не вашу программу на питоне, а только состояние самого интерпретатора. Вам же, как и обычно, надо использовать в мультипоточной программе разного вида локи, что бы бы избежать параллельного доступа к общим данным.
В питоне просто нет отдельного типа данных для хранения одного символа (юникодный аналог char из C). Поэтому даже один символ — это строка. И хоть __iter__ и итерируется по «символам», но возвращает он всётаки строки.
Совсем другое дело с типом bytes, для его элементов в питоне есть специальный тип — byte. И для него всё как бы нормально — итератор возвращает «числа», а не bytes с длиной в один байт.
"join" и "транзакции" в монге — это такие маркетинговые словечки, которые лучше вообще не ставить в один ряд с теми, что есть, например, в Postgres-е.
Нечто вроде join-а в монге (lookup) доступно только при выполнении агрегаций. А они не во всех случаях подходят. И точно не получится это использовать, например, для обновления данных.
Транзакции в монге накладывают такие ограничения, что полностью пропадает главный плюс — простое шардирование в монге (транзакции не работают на шардированных коллекциях). И работают они только при использовании репликации. На простой, запущенной в локалхосте, в единственном экземпляре, монге — транзакции не работают. Ну и конечно же их использование существенно замедляет обработку запросов.
В живую Fluent я ещё не использовал, но зато пользовался gettext. И после того, что я узнал про возможности Fluent, использовать gettext у меня больше нет желания.
Я программист и мой первый шок от gettext был когда я понял, что для получения перевода с поддержкой множественных форм, я должен в коде использовать отдельную функцию и передать ей варианты написания куска текста, зависящего от числа. Как так? Зачем я, как программист, должен думать про множественные формы?
Позднее мне стало понятно почему так получилось в gettext — потому что он разрешает использовать текст как id сообщения, а значит должен уже в коде приложения иметь всю информацию необходимую для генерации текста, даже если вообще нет ни каких переводов.
В Fluent с этим всё становится легко и просто. Для получения перевода в коде используется только одна функция, и мне не надо при этом думать про множественные формы даже для одного (например английского) языка. А ещё в Fluent (в отличии от gettext) можно в рамках одного сообщения использовать несколько разных чисел для выбора множественной формы разных частей строки. А можно использовать вообще не числа, а например пол «персонажа» и вообще любой другой «селектор», который может повлиять на перевод.
С Fluent в принципе не нужны инструменты для извлечения «строк» из кода, не надо их мержить с уже имеющимися переводами, т.к. в коде есть только id сообщений. Тут конечно есть минус — можно забыть добавить id в переводы если у вас нет тестов в приложении, которые вероятно выдадут ошибку при запросе не существующего перевода.
По поводу инструментов для работы с переводами — у Mozilla есть в гитхабе опен-сорсное веб-приложение для совместной работы над переводами (они используют его для перевода своих проектов), название не помню, но в одной с предыдущих статей про Fluent на хабре оно упоминалось.
Как я написал в первом ответе, наиболее типичным вариантом «сессией с состоянием» как правило является нечто вроде привязки состояния к TCP-соедниению между клиентом и сервером, а не то что у вас в базе данных на сервере или в клиенте хранится. Храните в базе что хотите и как хотите, передавайте информацию от клиента к серверу тоже как хотите (через куки, через тело запроса, через другие заголовки) — главное передавайте в каждом запросе если они ему нужны.
Если рассуждать как вы описали выше, то вообще любое хранение сервером данных — это нарушение REST, т.к. это хранение «состояния». Но это не так, т.к. это совершенно другое «состояние». Это состояние сервера. У клиента тоже есть какое-то своё состояние и он посредством запросов к серверу пытается «синхронизировать» своё состояние с тем, что есть на сервере. При этом ни сервер, ни клиент не должны полагаться на то, что за сколь угодно малое время между двумя запросами, состояние одного из них не изменилось. Архитектура должна проектироваться так, как будто между двумя запросами и даже одновременно с ними, происходят другие процессы меняющие «локальное» состояние участников процесса. Отсюда и происходит требование, что бы в запросе передавалась вся необходимая для его выполнения информация. И не важно в каком она виде, как называется и каким образом обрабатывается сервером. Главное что серверу будет достаточно этой информации.
Попробую привести «кухонный пример» почему stateless важен для построения масштабируемой архитектуры.
Представьте кинотеатр, кассы, билетёр на входе в зал. Вы купили билет, показали билетёру и вошли в зал. Потом решили вернуться в машину за забытыми очками. Если, когда вы возвращались, билетёр ОБЯЗАН запомнил вас в лицо и пустить без проверки билета — это не масштабируемая система. Такого билетёра нельзя подменить другим, потому что он вас не знает и не сможет впустить. Вам придётся снова проходить процедуру покупки билета, если вы его потеряли, и показывать его билетёру.
Если же билетёр ОБЯЗАН пропускать только по билету — то любой билетёр сможет пропустить вас с билетом, даже если видит вас впервые. Вот это масштабируемая схема — можно поставить хоть 10 билетёров и вы можете к любому подойти со своим билетом, а не только к тому, кто вас первым проверил.
Ну если у разных серверов есть доступ к общей базе с этими «restricted-данными», то скорее всего в этой же (или соседней) базе есть данные необходимые для авторизации клиента. Например есть таблица с мапингом токенов на юзеов. Клиент в этом случае передаёт в каждом запросе свой авторизационный токен, который получил от сервера в процессе аутентификации.
Ну или всё ещё проще — клиент в каждом запросе передаёт свой логин-пароль, как это сделано в HTTP Basic авторизации.
Автор конечно старался объяснить пользу квантовых вычислений максимально понятно. Но, как мне кажется, всё равно не избежал ловушки с "троллейбусом из буханки хлеба". Хорошо что квантовые вычисления могут за одно измерение определить тип функции. Только вот зачем это делать? Как это можно использовать? И зачем изучать функцию в "чёрном ящике" вместо того, что бы вычислять известные функции?
Ответов на эти вопросы автор не дал. И потому у меня, человека не обременённого знаниями о квантовых вычислениях, так и не сложилось до конца хотя бы одного фрагмента всей картины. Я только понял, что квантовые вычисления позволяют более эффективно делать "троллейбусы".
Правда поддерживать актуальность этих файлов несколько сложнее, т.к. надо не забывать вносить в них правки при изменении кода.
Причём реализовать это можно в виде «realtime стриминга» без предварительного создания zip-файла на диске сервера. И ещё будет возможность докачки после разрыва связи, т.к. за счёт отсутствия сжатия мы можем быстро рассчитать итоговый размер архива, и определять что должно находится по любому смещению от его начала.
Хотя… вот вам самый важный «минус» — экземпляр класс-таски в Celery создаётся только один, и потому его нельзя использовать для хранения данных выполняемой таски. Для этого можно использовать «словарик» Task.request — он создаётся каждый раз перед запуском таски.
Минус был только в том, что их не так удобно регистрировать в Celery-приложении, как это делается с декоратором. И ещё в минус можно записать невозможность переопределить базовый класс через настройку Celery.
github.com/gliderlabs/docker-alpine/issues/300
superuser.com/questions/1219609/why-is-the-alpine-docker-image-over-50-slower-than-the-ubuntu-image
— все пакеты в Alpine Linux компилируются с оптимизацией по памяти и размеру, а не по скорости;
— в библиотеке musl методы для работы с памятью (выделение и освобождение) работают медленнее чем в glibc.
Ну и в контексте питона, кроме скорости, есть ещё одна заморочка — нет возможности устанавливать бинарные сборки питонячих пакетов из PyPi, т.к. они все собраны под glibc. Приходится их собирать самому из исходников.
Аналогичные проблемы у меня и с другими сочетаниями для переключения языка, которые вместе с «буквами» вызывают какие-то активные изменения фокуса ввода.
Пробовал использовать CapsLock, но и тут засада — если успеть быстро нажать букву, то вместо переключения языка произойдёт ввод этой буквы в верхнем регистре (или нижнем). На Windows без доп. приложений CapsLock не задействовать — это тоже существенный минус.
Поэтому я так и остался на сочетании Shift+Ctrl. Пришлось правда пожертвовать работоспособностью хоткеев, основанных на этом сочетании — они просто не работают у меня теперь.
GIL защищает не вашу программу на питоне, а только состояние самого интерпретатора. Вам же, как и обычно, надо использовать в мультипоточной программе разного вида локи, что бы бы избежать параллельного доступа к общим данным.
Да, с byte это я нагнал конечно, не проверил.
__iter__
и итерируется по «символам», но возвращает он всётаки строки.Совсем другое дело с типом bytes, для его элементов в питоне есть специальный тип — byte. И для него всё как бы нормально — итератор возвращает «числа», а не bytes с длиной в один байт.
"join" и "транзакции" в монге — это такие маркетинговые словечки, которые лучше вообще не ставить в один ряд с теми, что есть, например, в Postgres-е.
Нечто вроде join-а в монге (lookup) доступно только при выполнении агрегаций. А они не во всех случаях подходят. И точно не получится это использовать, например, для обновления данных.
Транзакции в монге накладывают такие ограничения, что полностью пропадает главный плюс — простое шардирование в монге (транзакции не работают на шардированных коллекциях). И работают они только при использовании репликации. На простой, запущенной в локалхосте, в единственном экземпляре, монге — транзакции не работают. Ну и конечно же их использование существенно замедляет обработку запросов.
Я программист и мой первый шок от gettext был когда я понял, что для получения перевода с поддержкой множественных форм, я должен в коде использовать отдельную функцию и передать ей варианты написания куска текста, зависящего от числа. Как так? Зачем я, как программист, должен думать про множественные формы?
Позднее мне стало понятно почему так получилось в gettext — потому что он разрешает использовать текст как id сообщения, а значит должен уже в коде приложения иметь всю информацию необходимую для генерации текста, даже если вообще нет ни каких переводов.
В Fluent с этим всё становится легко и просто. Для получения перевода в коде используется только одна функция, и мне не надо при этом думать про множественные формы даже для одного (например английского) языка. А ещё в Fluent (в отличии от gettext) можно в рамках одного сообщения использовать несколько разных чисел для выбора множественной формы разных частей строки. А можно использовать вообще не числа, а например пол «персонажа» и вообще любой другой «селектор», который может повлиять на перевод.
С Fluent в принципе не нужны инструменты для извлечения «строк» из кода, не надо их мержить с уже имеющимися переводами, т.к. в коде есть только id сообщений. Тут конечно есть минус — можно забыть добавить id в переводы если у вас нет тестов в приложении, которые вероятно выдадут ошибку при запросе не существующего перевода.
По поводу инструментов для работы с переводами — у Mozilla есть в гитхабе опен-сорсное веб-приложение для совместной работы над переводами (они используют его для перевода своих проектов), название не помню, но в одной с предыдущих статей про Fluent на хабре оно упоминалось.
Как я написал в первом ответе, наиболее типичным вариантом «сессией с состоянием» как правило является нечто вроде привязки состояния к TCP-соедниению между клиентом и сервером, а не то что у вас в базе данных на сервере или в клиенте хранится. Храните в базе что хотите и как хотите, передавайте информацию от клиента к серверу тоже как хотите (через куки, через тело запроса, через другие заголовки) — главное передавайте в каждом запросе если они ему нужны.
Если рассуждать как вы описали выше, то вообще любое хранение сервером данных — это нарушение REST, т.к. это хранение «состояния». Но это не так, т.к. это совершенно другое «состояние». Это состояние сервера. У клиента тоже есть какое-то своё состояние и он посредством запросов к серверу пытается «синхронизировать» своё состояние с тем, что есть на сервере. При этом ни сервер, ни клиент не должны полагаться на то, что за сколь угодно малое время между двумя запросами, состояние одного из них не изменилось. Архитектура должна проектироваться так, как будто между двумя запросами и даже одновременно с ними, происходят другие процессы меняющие «локальное» состояние участников процесса. Отсюда и происходит требование, что бы в запросе передавалась вся необходимая для его выполнения информация. И не важно в каком она виде, как называется и каким образом обрабатывается сервером. Главное что серверу будет достаточно этой информации.
Попробую привести «кухонный пример» почему stateless важен для построения масштабируемой архитектуры.
Представьте кинотеатр, кассы, билетёр на входе в зал. Вы купили билет, показали билетёру и вошли в зал. Потом решили вернуться в машину за забытыми очками. Если, когда вы возвращались, билетёр ОБЯЗАН запомнил вас в лицо и пустить без проверки билета — это не масштабируемая система. Такого билетёра нельзя подменить другим, потому что он вас не знает и не сможет впустить. Вам придётся снова проходить процедуру покупки билета, если вы его потеряли, и показывать его билетёру.
Если же билетёр ОБЯЗАН пропускать только по билету — то любой билетёр сможет пропустить вас с билетом, даже если видит вас впервые. Вот это масштабируемая схема — можно поставить хоть 10 билетёров и вы можете к любому подойти со своим билетом, а не только к тому, кто вас первым проверил.
Ну или всё ещё проще — клиент в каждом запросе передаёт свой логин-пароль, как это сделано в HTTP Basic авторизации.