Conversation
| observational_clouds_paths = dict( | ||
| u=os.path.join(base_dir, u_observational_clouds_path), | ||
| v=os.path.join(base_dir, v_observational_clouds_path), | ||
| w=os.path.join(base_dir, w_observational_clouds_path)) |
There was a problem hiding this comment.
но ведь эти вещи можно передавать сверху, что облегчит сигнатуру и уберёт значения по умолчанию, которые не стоит использовать для путей, потому что у всех они разные, а при изменении никто не обратит внимание на функцию на самом дне
There was a problem hiding this comment.
сверху - это откуда именно? из функции draw модуля plots.service в месте вызова?
There was a problem hiding this comment.
ага, в месте вызова, этой функции будет без разницы, откуда именно её вызывают, лишь бы передали словарь с путями
| v_observational_bins_df = pd.read_csv(observational_bins_paths['v'], | ||
| delimiter=' ', | ||
| skipinitialspace=True, | ||
| header=None) |
There was a problem hiding this comment.
DRY, что нам мешает использовать partial?
| w_observational_clouds_df = pd.read_csv(observational_clouds_paths['w'], | ||
| delimiter=' ', | ||
| skipinitialspace=True, | ||
| header=None) |
There was a problem hiding this comment.
6 раз (!)
вместо того, чтобы делать простой Copy-Paste внутри проекта без изменения логики с инициализацией объекта (что уже само говорит о нарушении DRY-принципа) ничто нам не стоит потратить некоторое время на вынос в отдельную функцию (если это всё ещё не функция) или на фиксирование значений у уже имеющейся, если часть не изменяется
There was a problem hiding this comment.
и мы в очередной раз пройдём путь, когда все эти префиксы u, v, w – суть проекции данных, логика которых изменяется только на префикс
в этом прелесть физики, в наличии всевозможных инвариантов, и мы должны использовать эту фичу, а не копипастить
| y_line_obs=v_observational_bins_df[1], | ||
| yerr_obs=v_observational_bins_df[2], | ||
| x_scatter_obs=v_observational_clouds_df[0], | ||
| y_scatter_obs=v_observational_clouds_df[1]) |
There was a problem hiding this comment.
эти вызовы отличаются лишь префиксом u/v/w, если исходную функцию обобщить и все эти *_vs_mag_bins. df, SAMPLE TEXT считывать сверху, а сюда просто их передавать по одному, мы уменьшим объём как минимум втрое
There was a problem hiding this comment.
и это нужно сделать, потому что дальше кода будет ещё больше, нас всего 2ое, не хочется потом тратить пару недель на простой рефакторинг, который легче всего сделать, пока мы (ну я не в счёт) в контексте задачи, которую этот код должен выполнять
There was a problem hiding this comment.
этот подход с написанием общей функции и передачей в неё проекций значительно упростит следующее:
- чтение кода, ведь его объём уменьшится, а всё самое интересное (I/O операции) будет происходить наверху, а не на самом дне, не забываем, что современные программисты несмотря на заблуждения масс бОльшую часть времени
- тестирование, которое было бы неплохо начать писать уже сейчас, ведь это увеличивает количество участников до 3, притом он всегда онлайн, отвечает в считанные секунды, говорит фактами, и чем проще – тем он лучше, потому что мы можем себе позволить пропустить незначительный edge-case'ы, но наши use-case'ы он должен покрывать, ТАКЖЕ написание unit-тестов облегчится за счёт того, что всё I/O в таком подходе уползает наружу, а не происходит в самом низу, что само по себе есть причина многих бед, надо по возможности оперировать с чистыми функциями, может показаться, что я этого начитался/насмотрелся и теперь бездумно это проповедую, но так оно и есть + я немного писал тестов и это становится тем очевиднее, чем больше их напишешь, поэтому я так против мутирования
Starобъектов.
There was a problem hiding this comment.
Для наглядности рассмотрим пример:
есть функция, которая мутирует state одного Star-объекта:
def set_coordinates(star: Star,
coordinates: Tuple[int, int, int]) -> None:
# `if` здесь чтобы добавить логики
if not star.coordinates:
star.coordinates = coordinatesкод максимально краток, при этом аналогичный тест
...generating star object and new coordinates...
old_coordinates = star.coordinates
set_coordinates(star, new_coordinates)
assert (not old_coordinates and star.coordinates == new_coordinates or
star.coordinates == old_coordinates)не отличается ясностью. Мы оперируем не в терминах звёзд, а в терминах их внутренностей, и из этого теста можно подумать, что Star – абстракция, которая «заточила» в себе бедные объекты и нам приходится их освобождать (создавать old_coordinates), чтобы работать наконец с самими объектами, этот подход противоречит самой сути абстракции и ООП, когда ты должен скрывать реализацию и просто обмениваться сообщениями а-ля
...generating star object and new coordinates...
new_star = update_coordinates(star, new_coordinates)
assert (not star.coordinates and new_star.coordinates == new_coordinates or
new_star.coordinates == star.coordinates)т. е. в данном тесте coordinates уже спокойно может быть как полем, так и property (напомню, что основная суть property в том, что мы предоставляем динамический getter (и опционально setter/deleter) для объектов, и в отсутствие setter/deleter, пользователь не может напрямую изменить поле объекта, т. е. оно становится read-only, а если ко всем полям относиться read-only, то возникает очевидная возможность мемоизации (с которой нужно быть осторожным, но «we are adults here», советую это выступление посмотреть с самого начала), мы абстрагируемся от реализации Star.coordinates и работаем с ним путём общения с объектом вроде
"эй, star, что ты мне скажешь насчёт coordinates?"
а не как с продуктом в корзине, который нужно достать и посмотреть самому и так каждый раз... СНОВА И СНОВА
There was a problem hiding this comment.
в рассмотренном примере первый тест будет некорректным, если например
coordinatesпринадлежит мутируемому типу (напримерlistилиnumpy.array)- внутри
set_coordinatesпроизойдёт мутацияcoordinatesв духе C-style:
for coordinate in new_coordinates:
star.coordinates.append(coordinate)тогда old_coordinates уже будет удовлетворять не первому условию assert'a (coordinates были пусты и перестали быть пустыми, наполнившись новыми координатами)
not old_coordinates and star.coordinates == new_coordinatesа второму (координаты были не пусты и не наполнились)
star.coordinates == old_coordinatesи самое ужасное здесь, что код содержит логическую ошибку, а тесты проходят!
| w=[star.w_velocity | ||
| for star in velocity_vs_magnitude_clouds['w']]) | ||
|
|
||
| for key in ('u', 'v', 'w'): |
There was a problem hiding this comment.
разве я не писал про ту утилиту для прохождения по одинаковым ключам словарей?
There was a problem hiding this comment.
тем более что опять нужно всё это превратить в функцию для одной проекции и работать с ней, а сверху просто передавать их одну за другой, так будет «чище»
There was a problem hiding this comment.
хотел было глянуть, но подумал, что раз я решил избавиться от пост-процессинга, то сначала и избавлюсь от него. иначе, возможно, сейчас бы зря переписывал этот модуль.
| avg_velocities[key], | ||
| velocities_std[key]) = zip(*sorted(zip(bins_avg_magnitudes[key], | ||
| avg_velocities[key], | ||
| velocities_std[key]))) |
There was a problem hiding this comment.
если нужна сортировка, её стоит делать отдельно от draw_subplot
There was a problem hiding this comment.
остаток от кассандры. она перемешивала всё. графики непоследовательно строились. надо будет ветку с фиксом сделать, где удалю эти сортировки
c8ac9e6 to
88d3b63
Compare
77a7649 to
38620fc
Compare
|
Тут продолжим, когда ветку с пандас в мастер смержим |
Now we also plot observational data on old plots of velocities vs magnitude and velocities clouds diagrams