Елена Булыгина предлагает Вам запомнить сайт «Ленусик»
Вы хотите запомнить сайт «Ленусик»?
Да Нет
×
Прогноз погоды

Основная статья: Linux

Ускоряем PHP-проект с помощью кэширования

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

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

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

Какие бывают подходы?

Существует множество подходов к кэшированию. Список совместимых с PHP инструментов можно посмотреть на странице PHP-cache. Самые распространенные из них:

  • Apcu
  • Array
  • Memcached
  • Redis

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

APCu

Один из самых распространённых и простых в настройке инструментов кэширования, сохраняет нужные нам данные в оперативную память. (Ещё умеет кэшировать промежуточный код, но это уже совсем другая история) Чтобы начать работу с APCu, необходимо убедиться, что он установлен. Для этого в командной строке запустите следующую команду:

php -i | grep 'apc.enabled'
# Ожидаем увидеть:
# apc.enabled => On => On

Другой способ проверки: создайте файл index.php и поместите в него вызов функции phpinfo(). Убедитесь, что у вас настроен веб-сервер для используемой директории и откройте скрипт в браузере через адрес сервера. Нас интересует секция APCu: если внутри неё есть пункт APCu Support: Enabled, значит всё хорошо, мы можем идти дальше.

Если APCu у вас не установлен,  сделать это можно следующим способом:

  1. Запустите окно терминала (Linux/MacOS) или командную строку (Windows. Введите в поиске "cmd").
  2. Выполните команду:
pecl install apcu apcu_bc
  1. Откройте в любом текстовом редакторе файл конфигурации php.ini и убедитесь в наличии следующих строк:

# Windows
extension=php_apcu.dll
extension=php_apcu_bc.dll
 
apc.enabled=1
apc.enable_cli=1
 
#Linux / MacOS
extension="apcu.so"
extension="apc.so"
 
apc.enabled=1
apc.enable_cli=1
  1. Если указанных строк нет, добавьте их и сохраните файл конфигурации.
  2. Повторите проверку наличия установленного APCu.

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

$cacheKey = 'product_1';
$ttl = 600; // 10 минут.
 
// Проверка доступности APCu
$isEnabled = apcu_enabled();
 
// Проверяет, есть ли данные в кэше по ключу
$isExisted = apcu_exists($cacheKey);
 
// Сохраняет данные в кэш. В случае успеха возвращает true
// Аргумент $ttl определяет, как долго будет храниться кэш (секунды)
$isStored = apcu_store($cacheKey, ['name' => 'Demo product'], $ttl);
 
// Получает данные из кэша по ключу. В случае их отсутствия, вернет false
$data = apcu_fetch($cacheKey);
 
// Удаляет данные из кэша по ключу
$isDeleted = apcu_delete($cacheKey);
 
var_dump([
    'is_enabled'   => $isEnabled,
    'is_existed'   => $isExisted,
    'is_stored'    => $isStored,
    'is_deleted'   => $isDeleted,
    'fetched_data' => $data,
]);

Любой кэш работает по принципу key-value хранилища: это значит, что данные сохраняются со специальным ключом, по которому и происходит обращение. В данном случае ключ хранится в переменной $cacheKey.

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

Array-кэш

Более простой, но не всегда применимый метод кэширования. Если APCu сохраняет данные и делает их доступными для последующих выполнений всеми процессами, то Array-кэш хранит их только в рамках обрабатываемого запроса.

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

class CustomArrayCache
{
    /**
     * Массив приватный и статический
     * - приватный — чтобы обращаться к нему можно было только
     * из методов класса.
     * - статический — чтобы свойство было доступно во всех экземплярах
     */
    private static array $memory = [];
 
    // Метод сохранения данных в памяти
    public function store(string $key, $value): bool
    {
        self::$memory[$key] = $value;
 
        return true;
    }
 
    // Метод получения данных из памяти
    public function fetch(string $key)
    {
        return self::$memory[$key] ?? null;
    }
 
    // Метод удаления данных из памяти
    public function delete(string $key): bool
    {
        unset(self::$memory[$key]);
 
        return true;
    }
 
    // Метод проверки наличия данных по ключу
    public function exists(string $key): bool
    {
        return array_key_exists($key, self::$memory);
    }
}

Из-за  своей ограниченности этот подход применяется редко, однако знать о нём полезно.

Memcached и Redis

Наиболее продвинутые подходы кэширования. Подразумевают наличие запущенного отдельно сервера Memcached или Redis. Из PHP мы подключаемся к этому серверу по адресу и порту. Конфигурация этих решений сложнее, чем настройка APCu, но метод хранения данных очень похож: оперативная память. Самыми главными их преимуществами являются

  • изолированность от PHP: за кэш отвечают отдельные сервисы;
  • возможность кластеризации: если нагрузка на ваш проект очень велика, кластеризация сервисов кэширования поможет с ней справиться.

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

Стандарт PSR-16

В PSR есть два стандарта, посвящённых кэшированию: PSR-6 (обычный интерфейс кэширования) и PSR-16 (простой интерфейс кэширования) — мы сосредоточимся на PSR-16.

Этот стандарт предлагает специальный интерфейс (CacheInterface), которому могут удовлетворять классы, выполняющие функцию кэширования. Согласно ему, такие классы должны реализовывать следующие методы:

  • get($key, $default) — получение данных из кэша: вторым аргументом передаётся значение, которое будет возвращено в случае отсутствия этих данных;
  • set($key, $value, $ttl = null) — сохранение данных в кэш: как мы уже видели ранее, третьим параметром передаётся время хранения в секундах. Если оставить его пустым (null), значение будет подставлено по умолчанию из конфигурации кэша;
  • delete($key) — удаляет данные по ключу;
  • clear() — очищает все хранилище;
  • getMultiple($keys, $default) — позволяет получить данные сразу по нескольким ключам;
  • setMultiple($values, $ttl = null) — позволяет записать сразу несколько значений. В качестве $value мы передаем ассоциативный массив, где ключ — $key для кэша, а значение — данные для сохранения;
  • deleteMultiple($keys) — удаляет данные по нескольким ключам;
  • has($key) — проверяет наличие данных по ключу.

Как вы можете заметить, интерфейс очень прост, и даже тех функций, что мы рассмотрели в примере с APCu, достаточно для того, чтобы написать свой сервис кэша в соответствии с PSR-16. Но зачем это нужно?

Главные преимущества соблюдения стандартов PSR заключаются в том, что

  • они поддерживаются большинством популярных библиотек;
  • многие PHP-программисты придерживаются PSR и с легкостью освоятся в вашем коде;
  • благодаря интерфейсу, мы можем легко подменять используемый сервис на любой другой, поддерживающий PSR-16.

Давайте подробнее рассмотрим последний пункт и его преимущества.

Подключение PSR-16 библиотек

Библиотеки, создающие «обертку» над существующими инструментами кэширования для соответствия интерфейсу называются адаптерами. Для примера, рассмотрим адаптеры тех методов, что мы уже обсудили:

Все они удовлетворяют PSR-16 и поэтому применяются одинаково, однако логика «под капотом» у каждого своя.

Для примера давайте загрузим APCu- и Array-адаптеры в наш проект с помощью Composer.

composer require cache/array-adapter
composer require cache/apcu-adapter
# Или
composer req cache/apcu-adapter cache/array-adapter

Давайте представим, что у нас есть специальный класс для получения продуктов из базы данных. Назовем его ProductRepository, у него есть метод find($id), который возвращает продукт по его идентификатору, а если такого продукта нет — null.

class ProductRepository
{
    /**
     * Чтобы не усложнять пример, обусловимся, что в качестве продукта
     * возвращается массив, а если его нет — null
     */
    public function find(int $id): ?array
    {
        // ...
        // Получаем данные из БД
        return $someProduct;
    }
}

Если мы хотим подключить кэширование, мы не должны делать это внутри репозитория, потому что его ответственность — возвращать данные из базы данных. Куда же мы тогда добавим кэш? Есть несколько популярных решений, самое простое — дополнительный класс-провайдер. Всё, что он будет делать — пробовать получить данные из кэша, а если это не получится — обратится в репозиторий. Для этого в конструкторе такого класса определим две зависимости — наш репозиторий и CacheInterface. Почему именно интерфейс? Потому что так мы сможем использовать абсолютно любой из упомянутых адаптеров или других классов, удовлетворяющих PSR-16.

class ProductDataProvider
{
   private ProductRepository $productRepository;
   private CacheInterface $cache;
 
   public function __construct(ProductRepository $productRepository, CacheInterface $cache)
   {
       $this->productRepository = $productRepository;
       $this->cache             = $cache;
   }
 
   public function get(int $productId): ?array
   {
       $cacheKey = sprintf('product_%d', $productId);
 
       // Пробуем получить продукт из кэша
       $product = $this->cache->get($cacheKey);
       if ($product !== null) {
           // Если продукт есть, возвращаем
           // Временно выведем echo, чтобы понять, что данные из кэша
           echo 'Данные из кэша' . PHP_EOL; // PHP_EOL - перенос строки
           return $product;
       }
       // Если продукта нет, получаем его из репозитория
       $product = $this->productRepository->find($productId);
 
       if ($product !== null) {
           // Теперь сохраним полученный продукт в кэш для будущих запросов
           // Также временно выведем echo
           echo 'Данные из БД' . PHP_EOL;
           $this->cache->set($cacheKey, $product);
       }
 
       return $product;
   }
}

Наш класс готов. Теперь давайте рассмотрим его применение в сочетании с APCu-адаптером.

use Cache\\Adapter\\Apcu\\ApcuCachePool;
 
// Подключаем автозагрузчик Composer
require_once 'vendor/autoload.php';
 
// Наш репозиторий
$productRepository = new ProductRepository();
// APCu-кэш адаптер. Не требует никаких дополнительных настроек
$cache = new ApcuCachePool();
 
// Создаем провайдер, передаем зависимости
$productDataProvider = new ProductDataProvider(
    $productRepository,
    $cache
);
 
// Если в БД есть такой продукт, он к нам вернется
$product = $productDataProvider->get(1);
var_dump($product);

Если же мы захотим, заменить APCu-кэширование на Array-адаптер или любой другой, мы просто передадим новый подход в провайдер вместо старого, потому что все они реализуют CacheInterface.

use Cache\\Adapter\\PHPArray\\ArrayCachePool;
// ...
$productRepository = new ProductRepository();
//$cache = new ApcuCachePool();
$cache = new ArrayCachePool();
$productDataProvider = new ProductDataProvider(
    $productRepository,
    $cache
);
// ...

Состояние гонки и обновление данных

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

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

  • пользователь 1 получил сущность из кэша;
  • пользователь 1 обновил сущность в БД;
  • пользователь 2 получил сущность из кэша;
  • пользователь 1 обновил данные в кэше;
  • пользователь 2 обновил сущность в БД, но перезаписал её старыми данными, потому что сущность была неактуальна на момент получения и т. д.

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

Когда вы получаете любую сущность в коде с целью её обновления, всегда используйте данные из БД.

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

Вы можете либо обращаться в нужных местах к ProductRepository вместо ProductDataProvider, либо добавить аргумент к методу DataProvider. Например, такой ($fromCache):

class ProductDataProvider
{
    // ...
    public function get(int $productId, bool $fromCache = true): ?array
    {
        $cacheKey = sprintf('product_%d', $productId);
 
        $product = $fromCache ? $this->cache->get($cacheKey) : null;
        if ($product !== null) {
            return $product;
        }
        $product = $this->productRepository->find($productId);
 
        if ($product !== null) {
            $this->cache->set($cacheKey, $product);
        }
 
        return $product;
    }
}

Заключение

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

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

Подводя итог, повторим ключевые идеи статьи:

  • Соблюдение PSR-16 (или PSR-6) позволит вам с легкостью подключить для кэширования стороннюю библиотеку и сделает ваш код понятным другим разработчикам.
  • Для небольших проектов хорошим решением для кэширования станет APCu, т. к. он прост в настройке и использует оперативную память, доступ к которой очень высокий.
  • Для всех совместимых с PHP-инструментов кэширования есть адаптеры, которые можно посмотреть на сайте php-cache.com.
  • Кэширование — отдельная ответственность. Старайтесь реализовывать работу с кэшем в отдельных классах.
  • Если мы собираемся обновить сущность, её следует получать из БД. Если сущность нужна нам только для просмотра — мы можем запросить её из кэша.
  • В крупных проектах для получения возможности масштабирования применяются Memcached или Redis.

 


29 сен 21, 10:47
0 0
Статистика 1
Показы: 1 Охват: 0 Прочтений: 0

Как написать говорящую программу на Python

Озвучиваем системное время и любой текст в Windows и Linux. Используем pytts3, espeak, RHVoice, gTTS, Speech dispatcher. 

Синтез речи может пригодиться вам в работе над мобильным помощником, умным домом на Raspberry Pi, искусственным интеллектом, игрой, системой уведомлений и звуковым интерфейсом. Голосовые сообщения донесут информацию до пользователя, которому некогда читать текст. Кроме того, если программа умеет озвучивать свой интерфейс, она доступна незрячим и слабовидящим.  Есть системы управления компьютером без опоры на зрение. Одна из самых популярных — NVDA (NonVisual Desktop Access) — написана на Python с добавлением C++.

Давайте посмотрим, как использовать text-to-speech (TTS) в Python и подключать синтезаторы голоса к вашей программе. Эту статью я хотела назвать «Говорящая консоль», потому что мы будем писать консольное приложение для Windows, Linux, а потенциально — и MacOS. Потом решила выбрать более общее название, ведь от наличия GUI суть не меняется. На всякий случай поясню: консоль в данном случае — терминал Linux или знакомая пользователям Windows командная строка.

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

Готовим поляну

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

Чтобы компьютер заговорил, нужны: 

  • голосовой движок (синтезатор речи) с поддержкой нужных нам языков,
  • голоса дикторов для этого движка.

В Windows есть штатный речевой интерфейс Microsoft Speech API (SAPI). Голоса к нему выпускают, помимо Microsoft, сторонние производители: Nuance Communications, Loquendo, Acapela Group, IVONA Software. 

Есть и свободные кроссплатформенные голосовые движки: 

  • RHVoice от Ольги Яковлевой  — имеет четыре голоса для русского языка (один  мужской и три женских), а также поддерживает татарский, украинский, грузинский, киргизский, эсперанто и английский. Работает в Windows, GNU/Linux и Android.
  • eSpeak (не обновляется с 2014 года) и его ответвление — eSpeak NG — c поддержкой более 100 языков и диалектов, включая даже латынь. NG означает New Generation  — «новое поколение». Эта версия разрабатывается сообществом с тех пор, как автор оригинальной eSpeak перестал выходить на связь. Система озвучит ваш текст в Windows, Android, Linux, Mac, BSD. При этом старый eSpeak стабилен в ОС Windows 7 и XP, а eSpeak NG совместим с Windows 8 и 10.

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

По качеству голоса RHVoice неплох и к нему быстро привыкаешь, а вот eSpeak очень специфичен и с акцентом. Зато eSpeak запускается на любом утюге и подходит как вариант на крайний случай, когда ничто другое не работает или не установлено у пользователя.

Установка речевых движков, голосов и модулей в Windows 

С установкой синтезаторов в Windows проблем возникнуть не должно. Единственный нюанс — для русского голоса eSpeak и eSpeak NG нужно скачать расширенный словарь произношения. Распакуйте архив в подкаталог espeak-data или espeak-ng-data в директории программы. Теперь замените старый словарь новым: переименуйте ru_dict-48 в ru_dict, предварительно удалив имеющийся файл с тем же именем (ru_dict).

Теперь установите модули pywin32, python-espeak и py-espeak-ng, которые потребуются нам для доступа к возможностям TTS:

pip install pywin32 python-espeak pyttsx3 py-espeak-ng

Если у вас на компьютере соседствуют Python 2 и 3, здесь и далее пишите «pip3», а при запуске скриптов  — «python3».

Установка eSpeak(NG) в Linux

Подружить «пингвина» с eSpeak, в том числе NG, можно за минуту:

sudo apt-get install espeak-ng python-espeak

pip3 install py-espeak-ng pyttsx3

Дальше загружаем и распаковываем словарь ru_dict с официального сайта:

wget http://espeak.sourceforge.net/data/ru_dict-48.zip

unzip ru_dict-48.zip

Теперь ищем адрес каталога espeak-data (или espeak-ng-data) где-то в /usr/lib/ и перемещаем словарь туда. В моем случае команда на перемещение выглядела так:

sudo mv ru_dict-48 /usr/lib/i386-linux-gnu/espeak-data/ru_dict

Обратите внимание: вместо «i386» у вас в системе может быть «x86_64...» или еще что-то. Если не уверены, воспользуйтесь поиском:

find /usr/lib/ -name "espeak-data"

Готово! 

RHVoice в Linux

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

Смысл в том, что мы клонируем git-репозиторий и собираем необходимые компоненты через scons.

Для экспериментов в Windows и Linux я использую одни и те же русские голоса: стандартный ‘ru’ в eSpeak и Aleksandr в RHVoice.

Как проверить работоспособность синтезатора

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

Проверить работу eSpeak в Windows проще всего через GUI  — достаточно запустить TTSApp.exe в папке с программой. Дальше открываем список голосов, выбираем eSpeak-RU, вводим текст в поле редактирования и жмем на кнопку Speak.

Обратиться к espeak можно и из терминала. Базовые консольные команды для eSpeak и NG совпадают — надо только добавлять или убирать «-ng» после «espeak»:

espeak -v ru -f D:\my.txt

espeak-ng -v en "The Cranes are Flying"

echo "Да, это от души. Замечательно. Достойно восхищения" |RHVoice-test -p Aleksandr

Как нетрудно догадаться, первая команда с ключом -f  читает русский текст из файла. Чтобы в Windows команда espeak подхватывалась вне зависимости от того, в какой вы директории, добавьте путь к консольной версии eSpeak (по умолчанию — C:\Program Files\eSpeak\command_line) в переменную окружения Path. Вот как это сделать.

Библиотека pyttsx3

PyTTSx3 — удобная кроссплатформенная библиотека для реализации TTS в приложениях на Python 3. Использует разные системы синтеза речи в зависимости от текущей ОС:

  • в Windows — SAPI5,
  • в Mac OS X — nsss,
  • в Linux и на других платформах — eSpeak.

Это очень удобно: пишете код один раз и он работает везде. Кстати, eSpeak NG поддерживается наравне с исходной версией.

А теперь примеры!

Просмотр голосов

У каждого голоса есть несколько параметров, с которыми можно работать:

  • id (идентификатор в операционной системе), 
  • name (имя),
  • languages (поддерживаемые языки),
  • gender (пол),
  • age (возраст).

Первый вопрос всегда в том, какие голоса установлены на стороне пользователя. Поэтому создадим скрипт, который покажет все доступные голоса, их имена и ID. Назовем файл, например, list_voices.py:

import pyttsx3

tts = pyttsx3.init() # Инициализировать голосовой движок.

У активного движка есть стандартный параметр ‘voices’, где содержится список всех доступных этому движку голосов. Это нам и нужно

voices = tts.getProperty('voices')

 # Перебрать голоса и вывести параметры каждого

for voice in voices:

    print('=======')

    print('Имя: %s' % voice.name)

    print('ID: %s' % voice.id)

    print('Язык(и): %s' % voice.languages)

    print('Пол: %s' % voice.gender)

    print('Возраст: %s' % voice.age)

Теперь открываем терминал или командную строку, переходим в директорию, куда сохранили скрипт, и запускаем list_voices.py.

 Результат будет примерно таким:

В Linux картина будет похожей, но с другими идентификаторами. 

Как видите, в Windows для большинства установленных голосов MS SAPI заполнены только «Имя» и ID. Однако этого хватит, чтобы решить следующую нашу задачу: написать код, который выберет русский голос и что-то им произнесет.

Например, у голоса RHVoice Aleksandr есть преимущество — его имя уникально, потому что записано транслитом и в таком виде не встречается у других известных производителей голосов. Но через pyttsx3 этот голос будет работать только в Windows. Для воспроизведения в Linux ему нужен речевой Speech Dispatcher, с которым библиотека взаимодействовать не умеет. Как общаться с «диспетчером» еще обсудим, а пока разберемся с доступными голосами.

Как выбрать голос по имени

В Windows голос удобно выбирать как по ID, так и по имени. В Linux проще работать с именем или языком голоса. Создадим новый файл set_voice_and_say.py:

import pyttsx3

tts = pyttsx3.init()

voices = tts.getProperty('voices')

# Задать голос по умолчанию

tts.setProperty('voice', 'ru') 

# Попробовать установить предпочтительный голос

for voice in voices:

    if voice.name == 'Aleksandr':

        tts.setProperty('voice', voice.id)

tts.say('Командный голос вырабатываю, товарищ генерал-полковник!')

tts.runAndWait()

В Windows вы услышите голос Aleksandr, а в Linux — стандартный русский eSpeak. Если бы мы вовсе не указали голос, после запуска нас ждала бы тишина, так как по умолчанию синтезатор говорит по-английски.

Обратите внимание: tts.say() не выводит реплики мгновенно, а собирает их в очередь, которую затем нужно запустить на воспроизведение командой tts.runAndWait().

Выбор голоса по ID

Часто бывает, что в системе установлены голоса с одинаковыми именами, поэтому надежнее искать необходимый голос по ID.

Заменим часть написанного выше кода:

for voice in voices:

    ru = voice.id.find('RHVoice\Anna')  # Найти Анну от RHVoice

    if ru > -1: # Eсли нашли, выбираем этот голос

        tts.setProperty('voice', voice.id)

Теперь в Windows мы точно не перепутаем голоса Anna от Microsoft и RHVoice. Благодаря поиску в подстроке нам даже не пришлось вводить полный ID голоса.

Но когда мы пишем под конкретную машину, для экономии ресурсов можно прописать голос константой. Выше мы запускали скрипт list_voices.py — он показал параметры каждого голоса в ОС. Тогда-то вы и могли обратить внимание, что в Windows идентификатором служит адрес записи в системном реестре:

import pyttsx3

tts = pyttsx3.init()

EN_VOICE_ID = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20DSK"

RU_VOICE_ID = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\TokenEnums\RHVoice\Anna"

# Использовать английский голос

tts.setProperty('voice', EN_VOICE_ID)

tts.say("Can you hear me say it's a lovely day?")

# Теперь — русский

tts.setProperty('voice', RU_VOICE_ID)

tts.say("А напоследок я скажу")

tts.runAndWait()

Как озвучить системное время в Windows и Linux

Это крошечное приложение каждую минуту проговаривает текущее время по системным часам. Точнее, оно сообщает время при каждой смене минуты. Например, если вы запустите скрипт в 14:59:59, программа заговорит через секунду. 

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

# «Говорящие часы» — программа озвучивает системное время

from datetime import datetime, date, time

import pyttsx3, time

tts = pyttsx3.init()

tts.setProperty('voice', 'ru')  # Наш голос по умолчанию

tts.setProperty('rate', 150)    # Скорость в % (может быть > 100)

tts.setProperty('volume', 0.8)  # Громкость (значение от 0 до 1)

def set_voice(): # Найти и выбрать нужный голос по имени

    voices = tts.getProperty('voices')

    for voice in voices:

        if voice.name == 'Aleksandr':

           tts.setProperty('voice', voice.id)

        else:

            pass

def say_time(msg): # Функция, которая будет называть время в заданном формате

    set_voice() # Настроить голос 

    tts.say(msg)

    tts.runAndWait() # Воспроизвести очередь реплик и дождаться окончания речи

while True:

    time_checker = datetime.now() # Получаем текущее время с помощью datetime

    if time_checker.second == 0:

        say_time('{h} {m}'.format(h=time_checker.hour, m=time_checker.minute))

        time.sleep(55)   

    else:

        pass

Программа будет отслеживать и называть время, пока вы не остановите ее сочетанием клавиш Ctrl+Break или Ctrl+C (в Windows и Linux соответственно).

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

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

Обертка для eSpeak NG

Модуль называется py-espeak-ng. Это альтернатива pyttsx3 для случаев, когда вам нужен или доступен только один синтезатор — eSpeak NG. Не дай бог, конечно. Впрочем, для быстрых экспериментов с голосом очень даже подходит. Принцип использования покажется вам знакомым:

from espeakng import ESpeakNG

engine = ESpeakNG()

engine.speed = 150 

engine.say("I'd like to be under the sea. In an octopus's garden, in the shade!", sync=True)

engine.speed = 95 

engine.pitch = 32

engine.voice = 'russian' 

engine.say('А теперь Горбатый!', sync=True)

Обратите внимание на параметр синхронизации реплик sync=True. Без него синтезатор начнет читать все фразы одновременно — вперемешку. В отличие от pyttsx3, обертка espeakng не использует команду runAndWait(), и пропуск параметра sync сбивает очередь чтения.

Озвучиваем текст из файла

Не будем довольствоваться текстами в коде программы — пора научиться брать их извне. Тем более, это очень просто. В папке, где хранится только что рассмотренный нами скрипт, создайте файл test.txt с текстом на русском языке и в кодировке UTF-8. Теперь добавьте в конец кода такой блок:

text_file = open("test.txt", "r")

data = text_file.read()

tts.say(data, sync=True)

text_file.close()

Открываем файл на чтение, передаем содержимое в переменную data, затем воспроизводим голосом все, что в ней оказалось, и закрываем файл.

Управляем речью через Speech Dispatcher в Linux

До сих пор по результатам работы нашего кода в Linux выводился один суровый eSpeak. Пришло время позаботиться о друзьях Tux’а и порадовать их сравнительно реалистичными голосами RHVoice. Для этого нам понадобится Speech Dispatcher — аналог MS SAPI. Он позволяет управлять всеми установленными в системе голосовыми движками и вызывать любой из них по необходимости.

Скорее всего Speech Dispatcher есть у вас в системе по умолчанию. Чтобы обращаться к нему из кода Python, надо установить модуль speechd:

sudo apt install python3-speechd

Пробуем выбрать синтезатор RHVoice с помощью «диспетчера» и прочесть текст:

import speechd

tts_d = speechd.SSIPClient('test')

tts_d.set_output_module('rhvoice')

tts_d.set_language('ru')

tts_d.set_rate(50)

tts_d.set_punctuation(speechd.PunctuationMode.SOME)

tts_d.speak('И нежный вкус родимой речи так чисто губы холодит')

tts_d.close()

Ура! Наконец-то наше Linux-приложение говорит голосом, похожим на человеческий. Обратите внимание на метод .set_output_module() — он позволяет выбрать любой установленный движок, будь то espeak, rhvoice или festival. После этого синтезатор прочтет текст голосом, предписанным для данного движка по умолчанию. Если задан только язык — голосом по умолчанию для данного языка.

Получается, чтобы сделать кроссплатформенное приложение с поддержкой синтезатора RHVoice, нужно совместить pyttsx3 и speechd: проверить, в какой системе работает наш код, и выбрать SAPI или Speech Dispatcher. А в любой непонятной ситуации — откатиться на неказистый, но вездеходный eSpeak.

Однако для этого программа должна знать, где работает. Определить текущую ОС и ее разрядность очень легко! Лично я предпочитаю использовать для этого стандартный модуль platform, который не нужно устанавливать:

import platform

system = platform.system() # Вернет тип системы.

bit = platform.architecture() # Вернет кортеж, где разрядность — нулевой элемент

print(system)

print(bit[0])

Пример результата:

Windows

64bit

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

Модуль Google TTS — голоса из интернета

Google предлагает онлайн-озвучку текста с записью результата в mp3-файл. Это не для каждой задачи:

  • постоянно нужен быстрый интернет;
  • нельзя воспроизвести аудио средствами самого gtts;
  • скорость обработки текста ниже, чем у офлайн-синтезаторов.

Что касается голосов, английский и французский звучат очень реалистично. Русский голос Гугла — девушка, которая немного картавит и вдобавок произносит «ц» как «ч». По этой причине ей лучше не доверять чтение аудиокниг, имен и топонимов.

Еще один нюанс. Когда будете экспериментировать с кодом, не называйте файл «gtts.py» — он не будет работать! Выберите любое другое имя, например use_gtts.py.

Простейший код, который сохраняет текст на русском в аудиофайл:

from gtts import gTTS

tts = gTTS('Иван Федорович Крузенштерн. Человек и пароход!', lang='ru')

tts.save('tts_output.mp3')

После запуска этого кода в директории, где лежит скрипт, появится запись. Чтобы воспроизвести файл «не отходя от кассы», придется использовать еще какой-то модуль или фреймворк. Годится pygame или pyglet. 

Вот листинг приложения, которое построчно читает txt-файлы с помощью связки gtts и PyGame. Я заметила, что для нормальной работы этого скрипта текст из text.txt должен быть в кодировке Windows-1251 (ANSI).

Выводим текст через NVDA

Мы научились озвучивать приложение с помощью установленных в системе синтезаторов. Но что если большинству пользователей эта фишка не нужна, и мы хотим добавить речь исключительно как опцию для слабовидящих? В таком случае не обязательно писать код озвучивания: достаточно передать текст интерфейса другому приложению — экранному диктору.

Одна из самых популярных программ экранного доступа в Windows — бесплатная и открытая NVDA. Для связи с ней к нашему приложению нужно привязать библиотеку nvdaControllerClient (есть варианты для 32- и 64-разрядных систем). Узнавать разрядность системы вы уже умеете.

Еще для работы с экранным диктором нам понадобятся модули ctypes и time. Создадим файл nvda.py, где напишем модуль связи с NVDA:

import time, ctypes, platform

# Загружаем библиотеку клиента NVDA

bit = platform.architecture()

if bit[0] == '32bit':

    clientLib = ctypes.windll.LoadLibrary('nvdaControllerClient32.dll')

elif bit[0] == '64bit':

    clientLib = ctypes.windll.LoadLibrary('nvdaControllerClient32.dll')

else:

    errorMessage=str(ctypes.WinError(res))

    ctypes.windll.user32.MessageBoxW(0,u"Ошибка! Не удалось определить разрядность системы!",0)

# Проверяем, запущен ли NVDA

res = clientLib.nvdaController_testIfRunning()

if res != 0:

errorMessage=str(ctypes.WinError(res))

ctypes.windll.user32.MessageBoxW(0,u"Ошибка: %s"%errorMessage,u"нет доступа к NVDA",0)

def say(msg):

    clientLib.nvdaController_speakText(msg)

    time.sleep(1.0)

def close_speech():

    clientLib.nvdaController_cancelSpeech()

Теперь эту заготовку можно применить в коде основной программы:

import nvda

nvda.say('Начать игру')

# … другие реплики или сон

nvda.close_speech()

Если NVDA неактивна, после запуска кода мы увидим окошко с сообщением об ошибке, а если работает — услышим от нее заданный текст. 

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

Заключение

Ваша программа уже глаголет устами хотя бы одного из установленных синтезаторов? Поздравляю! Как видите, это не слишком сложно и «в выигрыше даже начинающий». Еще больше радуют перспективы использования TTS в ваших проектах. Все, что можно вывести как текст, можно и озвучить.

Представьте утилиту, которая при внезапной проблеме с экраном телефона или монитора сориентирует пользователя по речевым подсказкам, поможет спокойно сохранить данные и штатно завершить работу. Или как насчет прослушивания входящей почты, когда вы не за монитором? Напишите, когда, на ваш взгляд, TTS полезна, а когда только раздражает. Говорящая программа с какими функциями пригодилась бы вам?

Пройти обучение

29 авг 19, 15:15
0 0
Статистика 1
Показы: 1 Охват: 0 Прочтений: 0

11 лучших дистрибутивов Linux для программиста

О том, какая операционная система лучше, можно спорить бесконечно — у противоборствующих лагерей полно разумных аргументов. Сегодня мы поговорим про линуксовые системы — в отличие от конкурентов, они дают больше свободы, позволяют конфигурировать их под свои задачи и возможности машины. Формат open-source — ещё один их плюс. Это именно то, что так любят программисты. Если подумываете отказаться от «родных» Windows или MacOS — ловите подборку лучших дистрибутивов Linux-систем.

Ubuntu

Ubuntu считается лучшей системой для новичков, переход на неё с Windows и MacOS не уничтожит ни единой нервной клетки. Установка программ и пакетов предельно проста: базовое знакомство — и вы не почувствуете неудобств. Для разработчиков и программистов эта линуксовая ОС привлекательна стабильностью и надёжностью.

Ubuntu постоянно растёт и обновляется, в этом она схожа со «старшими» конкурентами. Как у Windows, у неё есть несколько дистрибутивов (Lubuntu, Xubuntu, Kubuntu), которые сохраняют функциональность, требуя меньше ресурсов. Это весомый аргумент для старых машин или задач, где нужна высокая производительность.

Бонус — отличная поддержка ОС. Все недочёты в Ubuntu устраняются молниеносно, техническая поддержка отвечает на вопросы постоянно, перечень дистрибутивов программ огромный и постоянно пополняется.

Pop!_OS

Американский производитель компьютеров и серверов System76 в прошлом году выпустил операционную систему Pop!_OS. Формально это та же Ubuntu, но изменения в графической оболочке GNOME и подключение собственных пакетов юридически сделали её новой системой.

По факту, для российского пользователя Pop!_OS — это Ubuntu с изменённым интерфейсом и улучшенной функциональностью. Если у вас есть претензии к внешнему виду первого фигуранта нашего списка — попробуйте Pop!_OS.

Debian

Debian — базовая ОС как для вышеупомянутых Ubuntu и Pop!_OS, так и менее раскрученных SteamOS, Linux mint, Knoppix. Исключительная стабильность делает Debian надёжным фундаментом для надстроек. Если захотите сделать свою операционную систему с блэкджеком и прочими приложениями — кастомизируйте Debian.

CentOS

Red Hat Enterprise Linux (RHEL) — одна из известнейших корпоративных ОС на базе Linux. CentOS — её ближайшая альтернатива, позволяющая запускать большинство приложений от RHEL и пользоваться менеджером загрузок YUM. Ещё одно преимущество — CentOS имеет доступ к репозиторию Red Hat Software Collections (RHSCL), расширяющему возможности по использованию языков и баз данных.

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

Fedora

Альтернатива RHEL для корпоративного использования. Обновления выходят каждые полгода, все необходимые пакеты включены в базовую сборку, конфигурация автоматическая. Факт (почти реклама): Fedora использует сам Линус Торвальдс, оценивая эту ОС выше Ubuntu и Debian.

Kali Linux

Операционная система, предназначенная для программистов в области кибербезопасности. Если вы видели сериал «Мистер Робот», то наверняка приметили там Kali Linux. Она основана на Debian и содержит множество инструментов для взлома и защиты системы. К примеру, «из коробки» поставляются John the Ripper — утилита для восстановления паролей из хэшей, Aircrack-ng — улавливатель беспроводных сетей и перехватчик трафика, а также сканер OWASP ZAP. Такой набор будет интересен каждому разработчику, и это только малая часть возможностей.

Ещё одна фича — возможность полноценно установить ОС на Raspberry Pi в качестве альтернативы Raspbian.

Arch Linux

Если вам нужна легковесная операционная система с гибкими возможностями настройки (в том числе ядра), обратите внимание на Arch Linux. ОС поставляется с голым «фундаментом» и Pacman — менеджером пакетов. Графический интерфейс идёт как опция, а не необходимая составляющая, что ценят тру-программисты.

Gentoo

У Gentoo те же преимущества и недостатки, что у Arch. К первым можно отнести тотальный контроль над сборкой: буквально каждый элемент будет установлен только с вашего разрешения. Отсюда и недостатки — без опыта в линуксовых системах собрать удобную ОС будет затруднительно.

NuTyX

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

OpenSUSE

Вернёмся к «дружелюбным» системам, на которые стоит обратить внимание Linux-новичку. OpenSUSE — достойная альтернатива Ubuntu. Удобна для разработчиков благодаря множеству доступных сред и приложений, загружаемых с помощью менеджера пакетов YaST. С ним вы забудете о стандартных приложениях Windows и MacOS — здесь тоже всё на высшем уровне.

Elementary OS

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

Если понравилась Elementary OS, обратите внимание на ChaletOS и Manjaro. Это схожие по устройству системы, которые подойдут для повседневного использования: просмотра почты, фотографий, видео. С их помощью можно дать вторую жизнь старому ноутбуку.

Сборок и кастомных систем Linux значительно больше перечисленных одиннадцати. Но именно они на сегодняшний день наиболее популярны и стабильны. Познакомьтесь с ними, «набейте руку», оцените преимущества и недостатки и обязательно расскажите о своем опыте в комментариях!


20 июн 18, 17:23
0 0
Статистика 1
Показы: 1 Охват: 0 Прочтений: 0

Что нужно знать, чтобы стать Java-разработчиком

Александр Чебыкин уже полгода работает Java-разработчиком в компании «Сбербанк-технологии». До этого он писал код на Фортране, работал над диссертацией в Институте прикладной математики имени М. В. Келдыша РАН и успел закончить две четверти в GeekUniversity. Этих знаний хватило, чтобы успешно пройти собеседования в Сбертехе и получить сразу два интересных предложения работы.

— Чем вы сейчас занимаетесь?

— В Сбертехе я работаю Java-разработчиком в отделе эквайринга, в команде клиринга.

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

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

— Какие знания нужны, чтобы получить такой оффер?

— Если вы хотите писать backend корпоративных приложений, нужно хорошо разобраться в следующих темах:

  • Java SE (Collections, многопоточность);
  • Maven;
  • Git;
  • Linux OS;
  • Spring (IoC, DI) — от джуниора обычно ждут общего представления, что это и для каких задач используется. Но знание этого фреймворка будет большим преимуществом при трудоустройстве и в дальнейшей разработке.

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

  • Как устроены ArrayList и LinkedList? В чем разница?
  • Как устроены HashMap/HashSet?
  • Что такое Deadlock (с примерами)? Как его избежать? Как убедиться, что в запущенном приложении он случился?
  • Что вы знаете про жизненный цикл потока? Приведите схему перехода между состояниями;
  • Как бы вы проводили ревью кода другого разработчика?
  • Выполните умножение двух чисел, представленных строкой (пример):
String str =239391391289323784827473442342*239391391289323784827473442342”.

— Как нашли эту работу?

— Она сама меня нашла. После первой четверти в GeekUniversity (факультет Android-разработки), когда достаточно изучил Java, я выложил резюме Java-разработчика на HeadHunter. Честно написал, что работал только с Фортран, что Java освоил самостоятельно и прохожу обучение.

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

После трех–четырех собеседований у меня сложилось понимание, как отвечать. Я стал увереннее, но приостановил поиски. Решил уделять больше внимания учебе, пошел на стажировку по Java-разработке в GeekBrains.

Чуть позже сотрудники кадрового агентства, с которым работает Сбертех, увидели мое резюме на HeadHunter и предложили пройти два собеседования. Первое — с командой в отделе кредитов. Вопросы, которые мне задавали, мы хорошо изучили на курсе «Java 3». Второе собеседование, в отделе эквайринга, проводил руководитель направления. На следующий день рекрутер позвонил и сообщил, что мне готовы сделать оффер. Позже поступило предложение и из первого отдела.

— Были какие-то сложности в первые месяцы работы?

— Команда, в которую я пришел, работала над проектом уже больше года. Было сложно сразу во всем разобраться. Хочу сказать одногруппникам, которые жаловались на количество кода в наших учебных проектах (например, когда мы писали сетевой чат в первой четверти): «Это вовсе не много кода!». Там было от силы 3 модуля по 20 классов в каждом. На момент, когда я подключился к работе своей команды в Сбертехе, в нашем рабочем проекте было около 20 модулей и сотни классов. Поэтому первый месяц на реальном проекте был для меня непростым.

К тому же, у меня не было представления, как устроен бизнес-процесс в компании. Стеснялся спрашивать — не знал, насколько глупыми могут показаться мои вопросы, и помогла только практика. Рабочий процесс складывался из двухнедельных спринтов: мне накидывали задач, и я потихоньку разбирался в проекте. Ещё увереннее почувствовал себя, когда пришел новый разработчик и мы уже вместе разбирались в том, как все устроено.

— Почему вы решили учиться программированию?

— Я закончил МИФИ, факультет теоретической и экспериментальной физики, по специальности «Прикладная математика и информатика». После вуза я полгода работал аналитиком в компании «Филип Моррис». Мне не нравились мои задачи: рутинная работа над статистикой и контроль того, как работают другие. На тот момент у меня не было четкого представления, чем хочу заниматься дальше. Увидел, что Институт прикладной математики проводит конкурс на место научного сотрудника — и пошел туда работать, поступил в аспирантуру.

Там я занимался разработкой, 70% из которой занимала матфизика и только 30% — программирование. За 4 года такой работы я понял, что физики с меня хватит — мне интереснее рассматривать задачи с точки зрения математики и программирования. Поэтому начал искать учебные программы для разработчиков.

Точно не знал, хочу заниматься корпоративной или мобильной разработкой или уйти в геймдев. У меня были базовые знания в Python и Java. Я работал только с простыми задачами, которые можно решить любым процедурным языком. Опыт разработки на Фортране дал мне неплохую базу, но объектно-ориентированное программирование пришлось осваивать почти «с нуля». Но я хотел развиваться и писать полноценные проекты.

— Почему поступили именно в GeekUniversity и выбрали факультет Android-разработки?

— До определенного профессионального уровня я не мог придумывать задачи сам. Хотел, чтобы мне их ставили опытные специалисты.

На тот момент программа GeekUniversity только запускалась. Было открыто три направления: веб-разработка, Android и iOS. Факультет Android-разработки выбрал методом исключения. iOS мне был не очень интересен, так как сам не пользуюсь техникой Apple и не обладаю нужными базовыми знаниями. Веб-разработку не рассматривал, потому что на тот момент не знал ничего о HTML, CSS, JavaScript. А вот моих минимальных знаний по Java для поступления на факультет Android-разработки было достаточно.

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

— Какие впечатления об обучении в GeekUniversity?

— В целом, мне понравилось, как и в каком объеме нам давали материал. Были преподаватели и курсы, которым я до сих пор благодарен, а были темы, которые не особо запомнились и пригодились.

Понравился курс Алексея Степченко «Java 2». Он же показал, как писать игру под Android. На курсе «Java 3» преподаватель Николай Дмитриев подробно объяснил всю теорию, которая требуется на собеседованиях. Хочу отметить, что Николай лучше всех давал обратную связь по домашним заданиям — он обращал внимание не только на очевидные ошибки, но и расписывал замечания по стилю кода.

Из преподавателей по Android запомнился Станислав Хижняк — подробно и понятно объяснял нужные нам инструменты. С преподавателем первого курса по Android Александром Аникиным писали погодное приложение — он разложил «по полочкам» всю работу.

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

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

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

Пройти обучение

13 июн 18, 09:24
0 0
Статистика 1
Показы: 1 Охват: 0 Прочтений: 0
Показаны все темы: 4

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

нет комментариев
Читать

Поиск по блогу

Люди

7 пользователям нравится сайт lena2018.mirtesen.ru