From 0d886f21c02e1b34248b9c12804aa0a6c65e3f16 Mon Sep 17 00:00:00 2001 From: Alina Date: Wed, 17 Oct 2018 12:34:44 +0300 Subject: [PATCH 1/8] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=B8=D0=BD=D0=BA=D1=83=20?= =?UTF-8?q?=D1=81=D0=BE=20=D1=81=D1=85=D0=B5=D0=BC=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d2ec80c6..011006db 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,4 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. +![alt text](https://devman.org/assets/images/7_40__data_flow.png) \ No newline at end of file From 7e27897fa1e0be6791304eb15ad0afe99a6f9e82 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:38 +0300 Subject: [PATCH 2/8] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 011006db..752fc499 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,12 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. -![alt text](https://devman.org/assets/images/7_40__data_flow.png) \ No newline at end of file +![alt text](https://devman.org/assets/images/7_40__data_flow.png) +Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит +тестирование и повторное использование. + +Стратегия по отделению операций ввода/вывода от обработки данных встречается +повсеместно, в самых разных программах: от небольших скриптов до серьезных и +крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. + +52a1d2b (Добавил ссылку) From 899fe17a8a8a397cf2d734be32629af57cce288a Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:04 +0300 Subject: [PATCH 3/8] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D1=8F=D1=8E=D1=89=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=82=D0=B5=D0=BA=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 752fc499..c782e0df 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ безгранично гибкий код — это как сферический конь в вакууме. Теоретически возможен, но практической ценности не несет. +Если мы заранее будем знать как изменятся требования к коду через год, два или +десять лет, то уже сейчас можно заложить необходимые механизмы расширения +функциональности. К сожалению, такие сведения сложно получить, а зачастую +просто невозможно. Приходится опираться на свой опыт, опыт товарищей, +прорабатывать разные сценарии развития проекта, подстраховываться. + Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. @@ -110,6 +116,8 @@ days_before_start ? ![alt text](https://devman.org/assets/images/7_40__data_flow.png) Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит +Кроме того, часть кода удалось превратить в чистые функции, что облегчит +e9fb5ff (Добавил поясняющий текст) тестирование и повторное использование. Стратегия по отделению операций ввода/вывода от обработки данных встречается @@ -117,3 +125,4 @@ days_before_start ? крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. 52a1d2b (Добавил ссылку) +e9fb5ff (Добавил поясняющий текст) From f75ac4153335652e52b38821cb65d42afcffbe47 Mon Sep 17 00:00:00 2001 From: ilybueface12 Date: Thu, 29 Jan 2026 09:06:58 +0300 Subject: [PATCH 4/8] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 70 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c782e0df..c87ab0e6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Отделяйте ввод/вывод от обработки +# Отделяйте ввод/вывод от обработки Ключевой критерий качества кода — это стоимость внесения в него изменений. @@ -16,12 +16,13 @@ Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. -Пример. Подбор онлайн-курса +## Пример. Подбор онлайн-курса По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: +```python def get_courses_list(courses_url): html = fetch_html(courses_url) if html: @@ -30,34 +31,41 @@ def get_courses_list(courses_url): else: print("can't load list of courses") exit() +``` Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: -В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем +1. В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем подождать еще 30 секунд и так далее. -В случае если адрес недоступен - постучаться по другому url в зеркало сайта. -В случае ошибки сделать запись в лог и взять данные из ранее подготовленного +2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. +3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. -Как все это сделать когда def get_courses_list сама завершает программу ?! От -вызова exit() надо отказаться. Можно выбросить исключение и таким образом +
+Как все это сделать когда `def get_courses_list` сама завершает программу ?! От +вызова `exit()` надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. -Вызов print тоже стоит вынести из тела функции наружу. В рассмотренных + +Вызов `print` тоже стоит вынести из тела функции наружу. В рассмотренных сценариях вывод в консоль зависит от общей логики загрузки данных и -многократных вызовов def get_courses_list. +многократных вызовов `def get_courses_list.` Что еще может потребоваться в скором будущем? -Отладить и покрыть тестами парсер HTML страницы. -Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком +1. Отладить и покрыть тестами парсер HTML страницы. +2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. -Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж -хорошая идея. Жить будет легче если передать в def get_courses_list строку с -HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их +
+Ага, значит вызывать `fetch_html()` внутри `def get_courses_list` не такая уж +хорошая идея. Жить будет легче если передать в `def get_courses_list` строку с +HTML разметкой вместо `courses_url.` Вуаля, мы решили проблемы еще до их появления на горизонте! + + Пойдем дальше. Код другой функции: +```python def get_course_info(html): # ... parsing logic @@ -71,18 +79,20 @@ def get_course_info(html): # .... parsing logic return course_data +``` Что может произойти с кодом дальше? -Если рейтинга нет — надо искать его на другом сайте. -В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. -Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы +1. Если рейтинга нет — надо искать его на другом сайте. +2. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. +3. Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы удобнее было руками проверять. Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". -В Python для этих целей предусмотрено значение rating = None. А строку "No +В Python для этих целей предусмотрено значение `rating = None.` А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. - +
Та же функция, часть вторая, последняя: +```python def get_course_info(html): # ... more parsing logic is here @@ -94,31 +104,36 @@ def get_course_info(html): '4_weeks': duration, "5_rating": rating } +``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с -другим порядком столбцов, как это сделать? Как заменить столбец 2_date на -days_before_start ? +другим порядком столбцов, как это сделать? Как заменить столбец `2_date` на +`days_before_start` ? Кроме того, наперед известно, что пользовательский интерфейс — будь то вывод в консоль или запись в файл — меняется очень часто. Было бы удобно собрать все, что относится к форматированию вывода в одном месте. Например, всю логику -выгрузки в xlsx поместить в def fill_xlsx(workbook, courses):, а вывод в -консоль собрать внутри if __name__=='__main__':. Удастся избежать вычитывания +выгрузки в xlsx поместить в `def fill_xlsx(workbook, courses):`, а вывод в +консоль собрать внутри `if __name__=='__main__':`. Удастся избежать вычитывания и повторной отладки всей программы от начала до конца, ведь изменения локальны и изолированы. -Вместо заключения - +## Вместо заключения В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: +1. от источника данных; +2. от формата вывода в файл. -1)от источника данных; -2)от формата вывода в файл. +![Схема потока данных](https://dvmn.org/filer/canonical/1594117412/678/) ![alt text](https://devman.org/assets/images/7_40__data_flow.png) Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит Кроме того, часть кода удалось превратить в чистые функции, что облегчит e9fb5ff (Добавил поясняющий текст) тестирование и повторное использование. +Кроме того, часть кода удалось превратить в [чистые функции](https://dvmn.org/encyclopedia/decomposition/decomposition_pure_functions/), +что облегчает тестирование и повторное использование. + +df63f18 (добавление заголовков) Стратегия по отделению операций ввода/вывода от обработки данных встречается повсеместно, в самых разных программах: от небольших скриптов до серьезных и @@ -126,3 +141,4 @@ e9fb5ff (Добавил поясняющий текст) 52a1d2b (Добавил ссылку) e9fb5ff (Добавил поясняющий текст) +df63f18 (добавление заголовков) From 530581323efe29a912a19835a3db588c9cf8d0c9 Mon Sep 17 00:00:00 2001 From: ilybueface12 Date: Thu, 29 Jan 2026 09:13:15 +0300 Subject: [PATCH 5/8] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index c87ab0e6..0feb3cb6 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ def get_courses_list(courses_url): 2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. 3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. -
Как все это сделать когда `def get_courses_list` сама завершает программу ?! От вызова `exit()` надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. @@ -55,7 +54,6 @@ def get_courses_list(courses_url): 1. Отладить и покрыть тестами парсер HTML страницы. 2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. -
Ага, значит вызывать `fetch_html()` внутри `def get_courses_list` не такая уж хорошая идея. Жить будет легче если передать в `def get_courses_list` строку с HTML разметкой вместо `courses_url.` Вуаля, мы решили проблемы еще до их @@ -89,7 +87,6 @@ def get_course_info(html): Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". В Python для этих целей предусмотрено значение `rating = None.` А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. -
Та же функция, часть вторая, последняя: ```python From 8e4fed8d0047d2e93c02ca37d04fa9aa599234c0 Mon Sep 17 00:00:00 2001 From: ilybueface12 Date: Thu, 29 Jan 2026 10:31:38 +0300 Subject: [PATCH 6/8] Update README.md --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 0feb3cb6..e1cf9446 100644 --- a/README.md +++ b/README.md @@ -125,17 +125,10 @@ def get_course_info(html): ![alt text](https://devman.org/assets/images/7_40__data_flow.png) Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит Кроме того, часть кода удалось превратить в чистые функции, что облегчит -e9fb5ff (Добавил поясняющий текст) тестирование и повторное использование. Кроме того, часть кода удалось превратить в [чистые функции](https://dvmn.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчает тестирование и повторное использование. -df63f18 (добавление заголовков) - Стратегия по отделению операций ввода/вывода от обработки данных встречается повсеместно, в самых разных программах: от небольших скриптов до серьезных и крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. - -52a1d2b (Добавил ссылку) -e9fb5ff (Добавил поясняющий текст) -df63f18 (добавление заголовков) From 0c5db289272b3da8c30f2912a77d1c3b59d9440e Mon Sep 17 00:00:00 2001 From: ilybueface12 Date: Thu, 29 Jan 2026 10:40:55 +0300 Subject: [PATCH 7/8] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index e1cf9446..1b97c277 100644 --- a/README.md +++ b/README.md @@ -122,10 +122,6 @@ def get_course_info(html): ![Схема потока данных](https://dvmn.org/filer/canonical/1594117412/678/) -![alt text](https://devman.org/assets/images/7_40__data_flow.png) -Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит -Кроме того, часть кода удалось превратить в чистые функции, что облегчит -тестирование и повторное использование. Кроме того, часть кода удалось превратить в [чистые функции](https://dvmn.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчает тестирование и повторное использование. From b1a02a74327b9512d81a060426f8203aa9fd6ffe Mon Sep 17 00:00:00 2001 From: ilybueface12 Date: Thu, 29 Jan 2026 13:32:14 +0300 Subject: [PATCH 8/8] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 1b97c277..e5d6a455 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,9 @@ def get_courses_list(courses_url): 2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. 3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. + +
+ Как все это сделать когда `def get_courses_list` сама завершает программу ?! От вызова `exit()` надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. @@ -54,6 +57,9 @@ def get_courses_list(courses_url): 1. Отладить и покрыть тестами парсер HTML страницы. 2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. + +
+ Ага, значит вызывать `fetch_html()` внутри `def get_courses_list` не такая уж хорошая идея. Жить будет легче если передать в `def get_courses_list` строку с HTML разметкой вместо `courses_url.` Вуаля, мы решили проблемы еще до их @@ -84,6 +90,9 @@ def get_course_info(html): 2. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. 3. Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы удобнее было руками проверять. + +
+ Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". В Python для этих целей предусмотрено значение `rating = None.` А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx.