Skip to content

Commit 515d46e

Browse files
committed
Добавили основной текст статьи
0 parents  commit 515d46e

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

README.md

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
Ввод/вывод vs Обработка данных
2+
3+
4+
Ключевой критерий качества кода — это стоимость внесения в него изменений.
5+
Если изменять программу сложно, то проект медленно развивается, несет убытки
6+
и, в конечном счете погибает. С другой стороны, бесконечно расширяемый и
7+
безгранично гибкий код — это как сферический конь в вакууме. Теоретически
8+
возможен, но практической ценности не несет.
9+
10+
Один из часто встречающихся и оправданных приемов — это отделение обработки
11+
данных от процесса ввода/вывода. Рассмотрим несколько примеров.
12+
13+
Пример. Подбор онлайн-курса
14+
15+
16+
По условию задачи нужно скачать из сети данных об онлайн-курсах, выбрать из
17+
них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода:
18+
19+
def get_courses_list(courses_url):
20+
html = fetch_html(courses_url)
21+
if html:
22+
# .... parsing logic
23+
return courses_list
24+
else:
25+
print("can't load list of courses")
26+
exit()
27+
Теперь примерим на себя роль провидца и подумаем какой функционал потребуется
28+
через месяц:
29+
30+
В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем
31+
подождать еще 30 секунд и так далее.
32+
В случае если адрес недоступен - постучаться по другому url в зеркало сайта.
33+
В случае ошибки сделать запись в лог и взять данные из ранее подготовленного
34+
кеша.
35+
Как все это сделать когда def get_courses_list сама завершает программу ?! От
36+
вызова exit() надо отказаться. Можно выбросить исключение и таким образом
37+
сообщить о проблеме внешнему коду, пускай там разбираются.
38+
39+
Вызов print тоже стоит вынести из тела функции наружу. В рассмотренных
40+
сценариях вывод в консоль зависит от общей логики загрузки данных и
41+
многократных вызовов def get_courses_list.
42+
43+
Что еще может потребоваться в скором будущем?
44+
45+
Отладить и покрыть тестами парсер HTML страницы.
46+
Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком
47+
диске.
48+
Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж
49+
хорошая идея. Жить будет легче если передать в def get_courses_list строку с
50+
HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их
51+
появления на горизонте!
52+
53+
Пойдем дальше. Код другой функции:
54+
55+
def get_course_info(html):
56+
# ... parsing logic
57+
58+
rating = soup.find_all('div', attrs={'class': 'ratings-text'})
59+
if rating: # check if rating is not empty list
60+
rating = rating[0].contents[0].text
61+
else:
62+
# we wanna be user-friendly, with nice output to xlsx
63+
rating = "No rating yet"
64+
65+
# .... parsing logic
66+
67+
return course_data
68+
Что может произойти с кодом дальше?
69+
70+
Если рейтинга нет — надо искать его на другом сайте.
71+
В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал.
72+
Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы
73+
удобнее было руками проверять.
74+
Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен".
75+
В Python для этих целей предусмотрено значение rating = None. А строку "No
76+
rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx.
77+
78+
Та же функция, часть вторая, последняя:
79+
80+
def get_course_info(html):
81+
# ... more parsing logic is here
82+
83+
# number prefix is usefull for simple sorting data before output to xlsx
84+
return {
85+
'1_title': title,
86+
'2_date': start_date,
87+
'3_language': language,
88+
'4_weeks': duration,
89+
"5_rating": rating
90+
}
91+
Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с
92+
другим порядком столбцов, как это сделать? Как заменить столбец 2_date на
93+
days_before_start ?
94+
95+
Кроме того, наперед известно, что пользовательский интерфейс — будь то вывод в
96+
консоль или запись в файл — меняется очень часто. Было бы удобно собрать все,
97+
что относится к форматированию вывода в одном месте. Например, всю логику
98+
выгрузки в xlsx поместить в def fill_xlsx(workbook, courses):, а вывод в
99+
консоль собрать внутри if __name__=='__main__':. Удастся избежать вычитывания
100+
и повторной отладки всей программы от начала до конца, ведь изменения локальны
101+
и изолированы.
102+
103+
Вместо заключения
104+
105+
106+
В результате мы пришли к ситуации, когда логика обработки данных слабо зависит:
107+
108+
1)от источника данных;
109+
2)от формата вывода в файл.
110+

0 commit comments

Comments
 (0)