From e9fb5ff4d4ce4b6aa85ad6d6672615b4cb09e524 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:04 +0300 Subject: [PATCH 01/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D1=8F=D1=8E=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=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 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 7ad73b2c..58e23870 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ безгранично гибкий код — это как сферический конь в вакууме. Теоретически возможен, но практической ценности не несет. +Если мы заранее будем знать как изменятся требования к коду через год, два или +десять лет, то уже сейчас можно заложить необходимые механизмы расширения +функциональности. К сожалению, такие сведения сложно получить, а зачастую +просто невозможно. Приходится опираться на свой опыт, опыт товарищей, +прорабатывать разные сценарии развития проекта, подстраховываться. + Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. @@ -108,3 +114,10 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. +Кроме того, часть кода удалось превратить в чистые функции, что облегчит +тестирование и повторное использование. + +Стратегия по отделению операций ввода/вывода от обработки данных встречается +повсеместно, в самых разных программах: от небольших скриптов до серьезных и +крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. + From 52a1d2b42b14cd16e1b6a5d2b36b0cf7c751f898 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:38 +0300 Subject: [PATCH 02/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58e23870..5a2b6610 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. -Кроме того, часть кода удалось превратить в чистые функции, что облегчит +Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит тестирование и повторное использование. Стратегия по отделению операций ввода/вывода от обработки данных встречается From 4846789cce2932f1eae848269be88efd106e3eae Mon Sep 17 00:00:00 2001 From: Alina Date: Wed, 17 Oct 2018 12:34:44 +0300 Subject: [PATCH 03/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=B8=D0=BD=D0=BA=D1=83?= =?UTF-8?q?=20=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 7ad73b2c..c26851c5 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 4fb288681d3dac182c839de6afd30812775372d3 Mon Sep 17 00:00:00 2001 From: dvmn-tasks <44063469+dvmn-tasks@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:10:43 +0300 Subject: [PATCH 04/10] Update image link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c26851c5..d3c84658 100644 --- a/README.md +++ b/README.md @@ -108,4 +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 +![image](https://dvmn.org/filer/canonical/1594117412/678/) From bb069e9178289a163d4d12fb5dd6ed6e254b7e59 Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Wed, 4 Mar 2026 20:24:37 +0300 Subject: [PATCH 05/10] =?UTF-8?q?=D0=9E=D1=84=D0=BE=D1=80=D0=BC=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b6c07890..c06e7d5f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Отделяйте ввод/вывод от обработки +# Отделяйте ввод/вывод от обработки # Ключевой критерий качества кода — это стоимость внесения в него изменений. @@ -16,7 +16,7 @@ Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. -Пример. Подбор онлайн-курса +## Пример. Подбор онлайн-курса По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из @@ -46,7 +46,7 @@ def get_courses_list(courses_url): сценариях вывод в консоль зависит от общей логики загрузки данных и многократных вызовов def get_courses_list. -Что еще может потребоваться в скором будущем? +## Что еще может потребоваться в скором будущем? Отладить и покрыть тестами парсер HTML страницы. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком @@ -56,7 +56,7 @@ def get_courses_list(courses_url): HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их появления на горизонте! -Пойдем дальше. Код другой функции: +## Пойдем дальше. Код другой функции: def get_course_info(html): # ... parsing logic @@ -71,7 +71,7 @@ def get_course_info(html): # .... parsing logic return course_data -Что может произойти с кодом дальше? +## Что может произойти с кодом дальше? Если рейтинга нет — надо искать его на другом сайте. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. @@ -81,7 +81,7 @@ def get_course_info(html): В Python для этих целей предусмотрено значение rating = None. А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. -Та же функция, часть вторая, последняя: +## Та же функция, часть вторая, последняя: def get_course_info(html): # ... more parsing logic is here @@ -106,7 +106,7 @@ days_before_start ? и повторной отладки всей программы от начала до конца, ведь изменения локальны и изолированы. -Вместо заключения +## Вместо заключения В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: From 56e27ff6ae6d25eadc4833b98206b3b0f552be9a Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Thu, 5 Mar 2026 14:57:57 +0300 Subject: [PATCH 06/10] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=D0=B0=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c06e7d5f..14644b01 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Отделяйте ввод/вывод от обработки # +# Отделяйте ввод/вывод от обработки Ключевой критерий качества кода — это стоимость внесения в него изменений. @@ -46,7 +46,7 @@ def get_courses_list(courses_url): сценариях вывод в консоль зависит от общей логики загрузки данных и многократных вызовов def get_courses_list. -## Что еще может потребоваться в скором будущем? +Что еще может потребоваться в скором будущем? Отладить и покрыть тестами парсер HTML страницы. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком @@ -56,7 +56,7 @@ def get_courses_list(courses_url): HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их появления на горизонте! -## Пойдем дальше. Код другой функции: +Пойдем дальше. Код другой функции: def get_course_info(html): # ... parsing logic @@ -71,7 +71,7 @@ def get_course_info(html): # .... parsing logic return course_data -## Что может произойти с кодом дальше? +Что может произойти с кодом дальше? Если рейтинга нет — надо искать его на другом сайте. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. @@ -81,7 +81,7 @@ def get_course_info(html): В Python для этих целей предусмотрено значение rating = None. А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. -## Та же функция, часть вторая, последняя: +Та же функция, часть вторая, последняя: def get_course_info(html): # ... more parsing logic is here From 9fa990f899217df7fd5fae309ae79fca350d0aa7 Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Thu, 5 Mar 2026 15:37:38 +0300 Subject: [PATCH 07/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 14644b01..04b3ff0d 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,12 @@ def get_courses_list(courses_url): Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: -В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем +1. В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем подождать еще 30 секунд и так далее. -В случае если адрес недоступен - постучаться по другому url в зеркало сайта. -В случае ошибки сделать запись в лог и взять данные из ранее подготовленного +2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. +3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. + Как все это сделать когда def get_courses_list сама завершает программу ?! От вызова exit() надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. @@ -48,9 +49,10 @@ def get_courses_list(courses_url): Что еще может потребоваться в скором будущем? -Отладить и покрыть тестами парсер HTML страницы. -Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком +1. Отладить и покрыть тестами парсер HTML страницы. +2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. + Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж хорошая идея. Жить будет легче если передать в def get_courses_list строку с HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их @@ -73,10 +75,11 @@ def get_course_info(html): return course_data Что может произойти с кодом дальше? -Если рейтинга нет — надо искать его на другом сайте. -В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. -Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы +1. Если рейтинга нет — надо искать его на другом сайте. +2. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. +3. Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы удобнее было руками проверять. + Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". В Python для этих целей предусмотрено значение rating = None. А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. @@ -111,8 +114,8 @@ days_before_start ? В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: -1)от источника данных; -2)от формата вывода в файл. +1. от источника данных; +2. от формата вывода в файл. ![image](https://dvmn.org/filer/canonical/1594117412/678/) From 886b17a7d19b5a68b29238be3d276b9d8692e0f6 Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Thu, 5 Mar 2026 15:39:56 +0300 Subject: [PATCH 08/10] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=D0=B0=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 04b3ff0d..d4de48eb 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: -def get_courses_list(courses_url): - html = fetch_html(courses_url) - if html: - # .... parsing logic - return courses_list - else: - print("can't load list of courses") - exit() + def get_courses_list(courses_url): + html = fetch_html(courses_url) + if html: + # .... parsing logic + return courses_list + else: + print("can't load list of courses") + exit() Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: @@ -60,19 +60,19 @@ HTML разметкой вместо courses_url. Вуаля, мы решили Пойдем дальше. Код другой функции: -def get_course_info(html): - # ... parsing logic + def get_course_info(html): + # ... parsing logic - rating = soup.find_all('div', attrs={'class': 'ratings-text'}) - if rating: # check if rating is not empty list - rating = rating[0].contents[0].text - else: - # we wanna be user-friendly, with nice output to xlsx - rating = "No rating yet" + rating = soup.find_all('div', attrs={'class': 'ratings-text'}) + if rating: # check if rating is not empty list + rating = rating[0].contents[0].text + else: + # we wanna be user-friendly, with nice output to xlsx + rating = "No rating yet" - # .... parsing logic + # .... parsing logic - return course_data + return course_data Что может произойти с кодом дальше? 1. Если рейтинга нет — надо искать его на другом сайте. @@ -86,17 +86,17 @@ rating yet" можно переместить туда где данные по Та же функция, часть вторая, последняя: -def get_course_info(html): - # ... more parsing logic is here - - # number prefix is usefull for simple sorting data before output to xlsx - return { - '1_title': title, - '2_date': start_date, - '3_language': language, - '4_weeks': duration, - "5_rating": rating - } + def get_course_info(html): + # ... more parsing logic is here + + # number prefix is usefull for simple sorting data before output to xlsx + return { + '1_title': title, + '2_date': start_date, + '3_language': language, + '4_weeks': duration, + "5_rating": rating + } Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с другим порядком столбцов, как это сделать? Как заменить столбец 2_date на days_before_start ? From 98f4b14587113aca1549c872e14c620a95f9b76f Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Thu, 5 Mar 2026 15:56:30 +0300 Subject: [PATCH 09/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B2=D0=B5=D1=82=D0=BA?= =?UTF-8?q?=D1=83=20=D1=81=D0=B8=D0=BD=D1=82=D0=B0=D0=BA=D1=81=D0=B8=D1=81?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d4de48eb..1287b7c6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: +```python def get_courses_list(courses_url): html = fetch_html(courses_url) if html: @@ -30,6 +31,8 @@ else: print("can't load list of courses") exit() +``` + Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: @@ -60,6 +63,7 @@ HTML разметкой вместо courses_url. Вуаля, мы решили Пойдем дальше. Код другой функции: +```python def get_course_info(html): # ... parsing logic @@ -73,6 +77,7 @@ HTML разметкой вместо courses_url. Вуаля, мы решили # .... parsing logic return course_data +``` Что может произойти с кодом дальше? 1. Если рейтинга нет — надо искать его на другом сайте. @@ -86,6 +91,7 @@ rating yet" можно переместить туда где данные по Та же функция, часть вторая, последняя: +```python def get_course_info(html): # ... more parsing logic is here @@ -97,6 +103,7 @@ rating yet" можно переместить туда где данные по '4_weeks': duration, "5_rating": rating } +``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с другим порядком столбцов, как это сделать? Как заменить столбец 2_date на days_before_start ? @@ -125,4 +132,3 @@ days_before_start ? Стратегия по отделению операций ввода/вывода от обработки данных встречается повсеместно, в самых разных программах: от небольших скриптов до серьезных и крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. - From f00bcaadb8480288e11ec5757a629aed180447bb Mon Sep 17 00:00:00 2001 From: Malinkina-Nadejhda Date: Sat, 7 Mar 2026 08:17:21 +0300 Subject: [PATCH 10/10] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=D0=B0=20=D0=BE=D1=82=D1=81=D1=82=D1=83=D0=BF=D1=8B?= =?UTF-8?q?=20=D0=B2=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B0=D1=85=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 1287b7c6..3b89c06f 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: ```python - def get_courses_list(courses_url): - html = fetch_html(courses_url) - if html: - # .... parsing logic - return courses_list - else: - print("can't load list of courses") - exit() +def get_courses_list(courses_url): + html = fetch_html(courses_url) + if html: + # .... parsing logic + return courses_list + else: + print("can't load list of courses") + exit() ``` Теперь примерим на себя роль провидца и подумаем какой функционал потребуется @@ -64,19 +64,19 @@ HTML разметкой вместо courses_url. Вуаля, мы решили Пойдем дальше. Код другой функции: ```python - def get_course_info(html): - # ... parsing logic +def get_course_info(html): + # ... parsing logic - rating = soup.find_all('div', attrs={'class': 'ratings-text'}) - if rating: # check if rating is not empty list - rating = rating[0].contents[0].text - else: - # we wanna be user-friendly, with nice output to xlsx - rating = "No rating yet" + rating = soup.find_all('div', attrs={'class': 'ratings-text'}) + if rating: # check if rating is not empty list + rating = rating[0].contents[0].text + else: + # we wanna be user-friendly, with nice output to xlsx + rating = "No rating yet" - # .... parsing logic + # .... parsing logic - return course_data + return course_data ``` Что может произойти с кодом дальше? @@ -92,17 +92,17 @@ rating yet" можно переместить туда где данные по Та же функция, часть вторая, последняя: ```python - def get_course_info(html): - # ... more parsing logic is here - - # number prefix is usefull for simple sorting data before output to xlsx - return { - '1_title': title, - '2_date': start_date, - '3_language': language, - '4_weeks': duration, - "5_rating": rating - } +def get_course_info(html): + # ... more parsing logic is here + + # number prefix is usefull for simple sorting data before output to xlsx + return { + '1_title': title, + '2_date': start_date, + '3_language': language, + '4_weeks': duration, + "5_rating": rating + } ``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с другим порядком столбцов, как это сделать? Как заменить столбец 2_date на