Я действительно не пишу большие проекты. Я не поддерживаю огромную базу данных и не имею дело с миллионами строк кода.
Мой код - это, в основном, "скриптовые" вещи - вещи для проверки математических функций или для моделирования чего-либо - "научное программирование". Самые длинные программы, над которыми я работал до сих пор, - это пара сотен строк кода, а большинство программ, над которыми я работаю, - около 150.
Мой код тоже дерьмо. Я понял это на днях, когда пытался найти файл, который написал некоторое время назад, но который я, вероятно, переписал и что я не использую контроль версий, что, вероятно, заставляет многих из вас мучиться от моей глупости.
Стиль моего кода запутан и наполнен устаревшими комментариями, отмечающими альтернативные способы сделать что-то или скопированные строки кода. Хотя имена переменных всегда начинаются очень хорошо и описательно, так как я добавляю или изменяю что-то, например, что-то новое, что кто-то хочет протестировать, код накладывается поверх и перезаписывается, и потому что я чувствую, что эта вещь должна быть быстро протестирована теперь, когда я у меня есть рамки, я начинаю использовать дрянные имена переменных, и файл отправляется в банк.
В проекте, над которым я сейчас работаю, я нахожусь в фазе, когда все это возвращается, чтобы сильно укусить меня. Но проблема в том, что (кроме использования контроля версий, создания нового файла для каждой новой итерации и записи всего этого в текстовый файл где-нибудь, что, вероятно, значительно поможет в ситуации), я не знаю, как продолжить улучшение мой настоящий стиль кодирования.
Нужно ли модульное тестирование для написания небольших кусков кода? Как насчет ООП? Какие подходы хороши для быстрого написания хорошего, чистого кода при «научном программировании», а не над большими проектами?
Я задаю эти вопросы, потому что часто само программирование не очень сложно. Это больше о математике или науке, которые я тестирую или исследую с помощью программирования. Например, нужен ли класс, когда две переменные и функция могут позаботиться об этом? (Рассмотрим также, как правило, ситуации, когда скорость программы предпочтительнее, чем на более быстром конце - когда вы выполняете более 25 000 000 временных шагов моделирования, вы как бы этого хотите.)
Возможно, это слишком широко, и если так, я прошу прощения, но, глядя на книги по программированию, они, кажется, часто рассматриваются в более крупных проектах. Мой код не нуждается в ООП, и он уже чертовски короткий, поэтому он не похож на «о, но файл будет уменьшен на тысячу строк, если мы сделаем это!» Я хочу знать, как «начать все сначала» и аккуратно программировать эти небольшие, более быстрые проекты.
Я был бы рад предоставить более конкретные детали, но чем более общий совет, тем более полезным, я думаю. Я программирую на Python 3.
Кто-то предложил дубликат. Позвольте мне пояснить, что я не говорю о прямом игнорировании стандартных стандартов программирования. Очевидно, есть причина, по которой эти стандарты существуют. Но, с другой стороны, действительно ли имеет смысл писать код, который говорит, что ООП, когда некоторые стандартные вещи могли бы быть выполнены, было бы гораздо быстрее писать, и был бы схожим уровнем читаемости из-за короткости программа?
Есть исключения. Кроме того, есть, вероятно, стандарты для научного программирования, помимо простых стандартов. Я спрашиваю и о них. Дело не в том, что нормальные стандарты кодирования должны игнорироваться при написании научного кода, а в написании чистого научного кода!
Обновить
Просто подумал, что добавлю обновление "не совсем на неделю позже". Все ваши советы были чрезвычайно полезны. Сейчас я использую контроль версий - git, с git kraken для графического интерфейса. Он очень прост в использовании и значительно очистил мои файлы - больше нет необходимости в старых файлах или в старых версиях кода, закомментированных «на всякий случай».
Я также установил Pylint и запустил его на весь мой код. Один файл изначально получил отрицательную оценку; Я даже не уверен, как это было возможно. Мой основной файл начинался с отметки ~ 1.83 / 10, а сейчас - ~ 9.1 / 10. Весь код теперь довольно хорошо соответствует стандартам. Я также пробежался по нему своими собственными глазами, обновляя имена переменных, которые стали ... хм ... ошибочными, и искал разделы для рефакторинга.
В частности, я недавно задал вопрос на этом сайте о рефакторинге одной из моих основных функций, и теперь она намного чище и намного короче: вместо длинной раздутой функции, заполненной если / еще, теперь она меньше половины Размер и гораздо проще понять, что происходит.
Мой следующий шаг - реализация «модульного тестирования». Под этим я подразумеваю файл, который я могу запустить в своем главном файле, который просматривает все функции в нем с помощью операторов assert и try / excepts, что, вероятно, не лучший способ сделать это, и приводит к большому количеству дублирующегося кода, но я буду продолжать читать и попытаюсь выяснить, как сделать это лучше.
Я также значительно обновил документацию, которую уже написал, и добавил дополнительные файлы, такие как электронная таблица Excel, документация и связанный документ, в репозиторий github. Сейчас это похоже на настоящий программный проект.
Итак ... Я думаю, это все, чтобы сказать: спасибо .
Ответы:
Это довольно распространенная проблема для ученых. Я много видел это, и это всегда связано с тем, что программирование - это то, что вы выбираете в качестве инструмента для выполнения своей работы.
Так что ваши сценарии - беспорядок. Я собираюсь пойти против здравого смысла и сказать, что, если вы программируете в одиночку, это не так уж плохо! Вы никогда больше не будете касаться большей части того, что пишете, поэтому тратить слишком много времени на написание красивого кода вместо того, чтобы создавать «значение» (то есть результат вашего скрипта), мало что вам поможет.
Тем не менее, наступит время, когда вам нужно вернуться к тому, что вы сделали, и посмотреть, как именно это работает. Кроме того, если другим ученым потребуется пересмотреть ваш код, очень важно, чтобы он был максимально четким и лаконичным, чтобы каждый мог его понять.
Ваша главная проблема - удобочитаемость, поэтому вот несколько советов по улучшению:
Имена переменных:
Ученые любят использовать краткие обозначения. Все математические уравнения обычно используют отдельные буквы в качестве переменных, и я не удивлюсь, увидев в вашем коде множество очень коротких переменных. Это сильно ухудшает читабельность. Когда вы вернетесь к своему коду, вы не вспомните, что представляют собой эти y, i и x2, и вы потратите много времени, пытаясь выяснить это. Вместо этого попробуйте явно назвать ваши переменные, используя имена, которые точно представляют, какие они есть.
Разделите ваш код на функции:
Теперь, когда вы переименовали все свои переменные, ваши уравнения выглядят ужасно и имеют длину в несколько строк.
Вместо того чтобы оставлять его в своей основной программе, переместите это уравнение в другую функцию и назовите его соответствующим образом. Теперь, вместо огромной и испорченной строки кода, у вас будут короткие инструкции, рассказывающие, что именно происходит и какое уравнение вы использовали. Это улучшает как вашу основную программу, так как вам даже не нужно смотреть на фактическое уравнение, чтобы узнать, что вы сделали, так и на сам код уравнения, так как в отдельной функции вы можете называть свои переменные по своему усмотрению и возвращаться к более знакомые отдельные буквы.
С этой точки зрения постарайтесь выяснить все фрагменты кода, которые представляют что-то, особенно если это что-то, что вы должны делать несколько раз в своем коде, и разбить их на функции. Вы обнаружите, что ваш код быстро станет проще для чтения, и вы сможете использовать те же функции без написания большего количества кода.
Обледенение, если эти функции необходимы в большинстве ваших программ, вы можете просто создать для них библиотеку, и они будут всегда доступны.
Глобальные переменные:
Когда я был новичком, я думал, что это отличный способ для передачи данных, которые мне нужны во многих пунктах моей программы. Оказывается, есть много других способов обойти вещи, и единственные вещи, которые глобальные переменные делают, - это причиняет людям головную боль, поскольку, если вы попадете в произвольную точку вашей программы, вы никогда не узнаете, когда это значение использовалось в последний раз или редактировалось, и выследить его будет больно. Старайтесь избегать их, когда это возможно.
Если вашим функциям нужно возвращать или изменять несколько значений, либо создайте класс с этими значениями и передайте их в качестве параметра, либо заставьте функцию возвращать несколько значений (с именованными кортежами) и назначьте эти значения в коде вызывающей стороны.
Контроль версий
Это напрямую не улучшает читаемость, но помогает вам делать все вышеперечисленное. Всякий раз, когда вы вносите какие-либо изменения, фиксируйте управление версиями (локальный репозиторий Git будет достаточно хорош), а если что-то не работает, посмотрите, что вы изменили, или просто откатитесь! Это облегчит рефакторинг вашего кода и станет защитной сетью, если вы случайно сломаете что-то.
Помня об этом, вы сможете писать более понятный и эффективный код, а также быстрее находить возможные ошибки, поскольку вам не придется разбираться с гигантскими функциями и беспорядочными переменными.
источник
Физик здесь. Был там.
Я бы сказал, что ваша проблема не в выборе инструментов или парадигм программирования (модульное тестирование, ООП и т. Д.). Это касается отношения , мышления. Тот факт, что имена ваших переменных вначале были правильно выбраны и в конечном итоге оказались дерьмовыми, достаточно показателен. Если вы думаете о своем коде как «запустить один раз, а затем выбросить», то это неизбежно приведет к путанице. Если вы думаете о нем как о продукте ремесла и любви, это будет красиво.
Я считаю, что есть только один рецепт для написания чистого кода: пишите его для человека, который будет его читать, а не для интерпретатора, который его запустит. Интерпретатору все равно, если ваш код беспорядок, но читателю все равно.
Вы ученый. Вы, вероятно, можете потратить много времени на подготовку научной статьи. Если ваш первый черновик выглядит запутанным, вы будете рефакторировать его до тех пор, пока логика не станет наиболее естественной. Вы хотите, чтобы ваши коллеги прочитали это и нашли аргументы кристально ясными. Вы хотите, чтобы ваши ученики могли учиться на этом.
Написание чистого кода точно так же. Думайте о своем коде как о подробном объяснении алгоритма, который только случайно оказывается машиночитаемым. Представьте, что вы собираетесь опубликовать это как статью, которую люди будут читать. Вы даже собираетесь показать это на конференции и пройтись по аудитории построчно. Теперь репетируйте свою презентацию . Да, строка за строкой ! Смущает, не так ли? Так что почистите свои слайды (ошибаюсь ... я имею в виду, ваш код) и повторите репетицию. Повторяйте, пока не будете удовлетворены результатом.
Было бы еще лучше, если бы после репетиций вы могли показать свой код реальным людям, а не просто воображаемым людям и вашему будущему я. Проходить его построчно называется «обход кода», и это не глупая практика.
Конечно, все это обходится ценой. Написание чистого кода занимает намного больше времени, чем написание одноразового кода. Только вы можете оценить, перевешивают ли выгоды затраты для вашего конкретного варианта использования.
Что касается инструментов, я сказал прежде , чем они не являются , что важны. Однако, если бы мне пришлось выбирать один, я бы сказал, что управление версиями является наиболее полезным.
источник
Контроль версий, вероятно, даст вам максимальную отдачу. Это не только для долговременного хранения, оно отлично подходит для отслеживания ваших краткосрочных экспериментов и возврата к последней работающей версии, сохраняя заметки по пути.
Следующим наиболее полезным являются юнит-тесты. Суть модульных тестов в том, что даже кодовые базы с миллионами строк кода проверяются модулем по одной функции за раз. Модульное тестирование проводится в небольшом, на самом низком уровне абстракции. Это означает, что принципиально нет разницы между модульными тестами, написанными для небольших баз кода, и теми, которые используются для больших. Их просто больше.
Модульные тесты - лучший способ не сломать то, что уже работало, когда вы исправляете что-то другое, или, по крайней мере, быстро сказать вам, когда вы это делаете. На самом деле они более полезны, когда вы не настолько опытный программист или не знаете, как или не хотите писать более подробный код, структурированный так, чтобы ошибки были менее вероятными или более очевидными.
По мере продвижения между контролем версий и написанием модульных тестов ваш код, естественно, станет намного чище. Другие методы для более чистого кодирования могут быть изучены, когда вы попадаете на плато.
источник
Вы, наверное, сами бы это поняли, но если вам нужно « записать все это в текстовый файл где-то », вы не используете систему контроля версий в полной мере. Используйте что-то вроде Subversion, git или Mercurial и пишите хорошее сообщение о коммите с каждым коммитом, и у вас будет журнал, который служит цели текстового файла, но не может быть отделен от хранилища.
Кроме того, использование контроля версий является наиболее важной вещью, которую вы можете сделать по причине, о которой ни один из существующих ответов не упоминает: воспроизводимость результатов . Если вы можете использовать свои сообщения журнала или добавить к результатам заметку с номером ревизии, тогда вы можете быть уверены, что сможете восстановить результаты, и у вас будет больше возможностей для публикации кода вместе со статьей.
Модульное тестирование никогда не требуется, но полезно, если (а) код достаточно модульный, чтобы вы могли тестировать модули, а не все; (б) вы можете создавать тесты. В идеале вы могли бы написать ожидаемый результат вручную, а не сгенерировать его с помощью кода, хотя генерация его с помощью кода может, по крайней мере, дать вам регрессионные тесты, которые сообщают вам, изменило ли что-то свое поведение. Просто подумайте, не являются ли тесты более ошибочными, чем код, который они тестируют.
ООП это инструмент. Используйте его, если это поможет, но это не единственная парадигма. Я предполагаю, что вы действительно знаете только процедурное программирование: если это так, то в описанном контексте, я думаю, вы выиграете больше от изучения функционального программирования, чем ООП, и, в частности, от дисциплины избегания побочных эффектов, где это возможно. Python может быть написан в очень функциональном стиле.
источник
В аспирантуре я сам написал несколько алгоритмически сложного кода. Это немного крепкий орешек. Грубо говоря, многие соглашения о программировании строятся вокруг идеи помещения информации в базу данных, ее извлечения в нужное время, а затем массирования этих данных, чтобы представить их пользователю, обычно используя библиотеку для любой математики или Алгоритм тяжелые части этого процесса. Для этих программ все, что вы слышали об ООП, разбивая код на короткие функции и делая все легко понятным с первого взгляда, где это возможно, является отличным советом. Но это не совсем работает для кода, насыщенного алгоритмами, или кода, который реализует сложные математические вычисления и мало чего еще.
Если вы пишете сценарии для выполнения научных расчетов, у вас, вероятно, есть документы с уравнениями или алгоритмами, которые вы используете, написанные в них. Если вы используете новые идеи, которые вы открыли самостоятельно, вы надеетесь опубликовать их в своих собственных статьях. В этом случае правило таково: вы хотите, чтобы ваш код читался как можно больше опубликованных уравнений. Вот ответ на Software Engineering.SE с более чем 200 ответами, поддерживающими этот подход и объясняющими, как он выглядит: Есть ли оправдание коротким именам переменных?
В качестве другого примера, есть несколько отличных фрагментов кода в Simbody , инструменте моделирования физики, который используется для исследований и разработок в области физики. Эти фрагменты содержат комментарий, показывающий уравнение, используемое для расчета, за которым следует код, который читает как можно ближе к реализуемым уравнениям.
ContactGeometry.cpp
:ContactGeometry_Sphere.cpp
:источник
λ
илиφ
вместо уродливыхlambda_
илиphy
...Итак, моя дневная работа заключается в публикации и сохранении данных исследований для системы Университета Калифорнии. Несколько человек упомянули о воспроизводимости, и я думаю, что это действительно основная проблема: документирование вашего кода так, как вы бы документировали все, что кому-то нужно для воспроизведения вашего эксперимента, и, в идеале, написание кода, который делает его простым для кого-то другого, одновременно воспроизвести свой эксперимент и проверить свои результаты на наличие источников ошибок.
Но кое-что, о чем я не упомянул, и которое я считаю важным, заключается в том, что финансирующие агентства все чаще рассматривают публикацию программного обеспечения как часть публикации данных, а также то, что публикация программного обеспечения является требованием открытой науки.
В связи с этим, если вы хотите что-то конкретное, ориентированное на исследователей, а не на разработчиков программного обеспечения, я не могу рекомендовать организацию Software Carpentry достаточно высоко. Если вы можете посетить один из их семинаров , отлично; если все, что у вас есть время / доступ, это прочитать некоторые из их статей о передовых методах научных вычислений , это тоже хорошо. Из последнего:
Краткое описание методов, которые они рекомендуют:
В статье подробно рассматриваются все эти вопросы.
источник
Личный ответ:
я тоже много пишу для научных целей. Для небольших сценариев я просто стараюсь следовать общепринятым нормам программирования (например, используя контроль версий, практикуя самоконтроль с именами переменных). Если я просто пишу что-то, чтобы быстро открыть или визуализировать набор данных, я не беспокоюсь об ООП.
Общий ответ:
«Это зависит». Но если вы изо всех сил пытаетесь понять, когда использовать концепцию программирования или парадигмы, вот пара вещей, о которых стоит подумать:
# 1: Познакомьтесь с тем, что там происходит:
даже если вы «просто» пишете сценарии (и вы действительно заботитесь о научном компоненте), вам нужно некоторое время, чтобы узнать о различных концепциях и парадигмах программирования. Таким образом, вы сможете лучше понять, что вы должны / не должны использовать и когда. Это может звучать немного устрашающе. И у вас все еще может возникнуть вопрос: "С чего мне начать / на что мне начать смотреть?" Я пытаюсь объяснить хорошую отправную точку в следующих двух пунктах.
# 2: Начните исправлять то, что, как вы знаете, неправильно.
Лично я бы начал с того, что, как я знаю, неправильно. Получите некоторый контроль версий и начните дисциплинировать себя, чтобы стать лучше с этими именами переменных (это серьезная борьба). Исправление того, что вы знаете неправильно, может показаться очевидным. Однако по своему опыту я обнаружил, что исправление одной вещи приводит меня к чему-то другому и так далее. Прежде чем я это узнал, я представил 10 разных вещей, которые я делал неправильно, и выяснил, как их исправить или как правильно их реализовать.
№3. Найдите партнера по программированию.
Если «начинать заново» для вас не предполагает формальных занятий, подумайте о том, чтобы объединиться с разработчиком и попросить его проверить ваш код. Даже если они не понимают научную часть того, что вы делаете, они могут рассказать вам, что вы могли бы сделать, чтобы сделать ваш код более элегантным.
# 4: Ищите консорциумы:
я не знаю, в какой области науки вы находитесь. Но в зависимости от того, что вы делаете в научном мире, попробуйте поискать консорциумы, рабочие группы или участников конференции. Затем посмотрите, есть ли стандарты, над которыми они работают. Это может привести вас к некоторым стандартам кодирования. Например, я делаю много геопространственных работ. Просмотр документов конференции и рабочих групп привел меня в Открытый геопространственный консорциум . Одна из вещей, которые они делают, это работа над стандартами для геопространственного развития.
Надеюсь, это поможет!
Примечание: я знаю, что вы просто использовали ООП в качестве примера. Я не хотел, чтобы вы думали, что я застрял на том, как обрабатывать написание кода с использованием ООП. Просто было проще написать ответ, продолжая этот пример.
источник
Я бы порекомендовал придерживаться принципа Unix: Keep It Simple, Stupid! (ПОЦЕЛУЙ)
Или, говоря по-другому: делай одну вещь за раз и делай это хорошо.
Что это обозначает? Ну, во-первых, это значит, что ваши функции должны быть короткими. Любая функция, которая не может быть полностью понята по назначению, использованию и реализации в течение нескольких секунд, определенно является слишком длинной. Скорее всего, он делает сразу несколько вещей, каждая из которых должна быть функцией их собственного. Так что разбейте это.
С точки зрения строк кода, моя эвристика заключается в том, что 10 строк - это хорошая функция, а все, что больше 20, - скорее всего дерьмо. У других людей есть другая эвристика. Важная часть состоит в том, чтобы сократить длину до того, что вы на самом деле можете понять в одно мгновение.
Как вы разделяете длинную функцию? Ну, во-первых, вы ищете повторяющиеся шаблоны кода. Затем вы выделяете эти шаблоны кода, даете им описательное имя и наблюдаете, как ваш код сокращается . Действительно, лучший рефакторинг - это рефакторинг, который уменьшает размер кода.
Это особенно верно, когда рассматриваемая функция была запрограммирована с помощью copy-paste. Всякий раз, когда вы видите такой повторяющийся паттерн, вы сразу же знаете, что его, скорее всего, следует превратить в собственную функцию. Это принцип Не повторяйся (СУХОЙ) . Всякий раз, когда вы нажимаете «копировать-вставить», вы делаете что-то не так! Вместо этого создайте функцию.
Некоторые функции могут быть длинными, потому что они делают несколько разных вещей одну за другой. Это не СУХИЕ нарушения, но они также могут быть разделены. Результатом часто является функция высокого уровня, которая вызывает множество функций, которые реализуют отдельные шаги исходных функций. Это обычно увеличивает размер кода, но имена добавленных функций творит чудеса, делая код более читабельным. Потому что теперь у вас есть функция верхнего уровня, все шаги которой явно названы. Кроме того, после этого разделения ясно, какой шаг влияет на какие данные. (Аргументы функции. Вы не используете глобальные переменные?)
Хорошая эвристика для такого вида разделения функций по разделам - всякий раз, когда у вас возникает желание написать комментарий к разделу или когда вы найдете комментарий к разделу в своем коде. Это очень вероятно одна из точек, где ваша функция должна быть разделена. Комментарий раздела также может послужить вдохновением для названия новой функции.
Принципы KISS и DRY могут пройти долгий путь. Вам не нужно начинать с ООП и т. Д. Сразу, часто вы можете добиться больших упрощений, просто применив эти два. Тем не менее, в конечном итоге полезно знать об ООП и других парадигмах, потому что они дают вам дополнительные инструменты, которые вы можете использовать, чтобы сделать свой программный код более понятным.
Наконец, запишите каждое действие с коммитом. Вы вносите что-то в новую функцию, это коммит . Вы объединяете две функции в одну, потому что они действительно делают одно и то же, это коммит . Если вы переименуете переменную, это будет коммит . Фиксируйте часто. Если проходит день, а вы не совершаете, вы, вероятно, сделали что-то не так.
источник
Я согласен с остальными, что контроль версий сразу решит многие ваши проблемы. В частности:
Я бы сказал, не думайте об этом: просто используйте git. Придерживайтесь простых команд (например, только одна
master
ветвь), возможно, используйте графический интерфейс, и у вас все будет хорошо. В качестве бонуса вы можете использовать gitlab, github и т. Д. Для бесплатной публикации и резервного копирования;)Причина, по которой я написал этот ответ, заключалась в том, чтобы рассмотреть две вещи, которые вы могли бы попробовать, которые я не видел вышеупомянутыми. Первый заключается в использовании утверждений в качестве легкой альтернативы модульному тестированию. Модульные тесты, как правило, находятся «вне» функции / модуля / независимо от того, что тестируется: они обычно отправляют некоторые данные в функцию, получают результат обратно, а затем проверяют некоторые свойства этого результата. Как правило, это хорошая идея, но может быть неудобно (особенно для «выброшенного» кода) по нескольким причинам:
Утверждения не имеют этих недостатков, так как они проверяются во время обычного выполнения программы. В частности:
Вы упоминаете скорость как фактор, и в этом случае проверка утверждений может быть нежелательной в этом цикле (но все же полезной для проверки настройки и последующей обработки). Однако почти все реализации утверждений предоставляют способ отключить их; например, в Python их можно отключить, запустив с
-O
опцией (я не знал этого, так как никогда раньше не чувствовал необходимости отключать какие-либо из моих утверждений). Я бы порекомендовал вам оставить их напо умолчанию; если ваш цикл кодирования / отладки / тестирования замедляется, вам может быть лучше проводить тестирование с меньшим подмножеством ваших данных или выполнять меньше итераций некоторого моделирования во время тестирования или чего-либо еще. Если вы в конечном итоге отключаете утверждения в не тестовых прогонах по соображениям производительности, первое, что я рекомендую вам сделать, это определить , действительно ли они являются источником замедления! (Очень легко обмануть себя, когда дело касается узких мест в производительности)Мой последний совет - использовать систему сборки, которая управляет вашими зависимостями. Я лично использую Nix для этого, но слышал и хорошие отзывы о Guix . Есть также альтернативы, такие как Docker, которые гораздо менее полезны с научной точки зрения, но, возможно, немного более знакомы.
Такие системы, как Nix, только недавно стали (немного) популярными, и некоторые могут счесть их излишними для «выброшенного» кода, как вы описываете, но их выгода для воспроизводимости научных вычислений огромна. Рассмотрим сценарий оболочки для запуска эксперимента, например так
run.sh
:Вместо этого мы можем переписать его в «деривацию» Nix, например, так
run.nix
:Между ними
''...''
есть код bash, такой же, как у нас раньше, за исключением того, что${...}
его можно использовать для «склеивания» содержимого других строк (в этом случае./.
, которое расширится до пути к каталогу, содержащемуrun.nix
).with import ...
Линия импортирует Никс в стандартную библиотеку , которая обеспечиваетrunCommand
для запуска кода Баш. Мы можем запустить наш эксперимент, используяnix-build run.nix
, который выдаст путь, как/nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csv
.Так, что это покупает нас? Nix автоматически настроит «чистую» среду, в которой есть доступ только к тем вещам, которые мы явно просили. В частности, он не имеет доступа к таким переменным, как
$HOME
или к системному программному обеспечению, которое мы установили. Это делает результат независимым от деталей нашего текущего компьютера, таких как содержимое~/.config
или версии программ, которые мы установили; АКА то, что мешает другим людям копировать наши результаты! Вот почему я добавил, чтоcp
команда, так как проект не будет доступен по умолчанию. Может показаться раздражающим, что программное обеспечение системы недоступно для сценария Nix, но оно идет и другим путем: нам не нужно ничего устанавливать в нашей системе (кроме Nix), чтобы использовать его в сценарии; мы просто просим об этом, и Nix отключится и извлечет / скомпилирует / все, что необходимо (большинство вещей будет загружено в виде двоичных файлов; стандартная библиотека также огромна!). Например, если нам нужна группа конкретных пакетов Python и Haskell для некоторых конкретных версий этих языков, а также некоторые другие ненужные файлы (потому что почему бы и нет?):То же самое
nix-build run.nix
запустит это, получая сначала все, что мы просили (и кэшируя все на случай, если мы захотим позже). Вывод (любой вызванный файл / каталог$out
) будет сохранен Nix, который является путем, который он выбрасывает. Он идентифицируется криптографическим хэшем всех запрошенных нами входных данных (содержимое скрипта, другие пакеты, имена, флаги компилятора и т. Д.); эти другие пакеты идентифицируются по хэшам их входных данных, и так далее, чтобы у нас была полная цепочка всего, вплоть до версии GCC, которая компилировала версию GCC, которая компилировала bash, и так далее!Надеюсь, я показал, что это очень дорого для научного кода, и с ним довольно легко начать работу. Это также начинает восприниматься очень серьезно учеными, например (топ Google) https://dl.acm.org/citation.cfm?id=2830172, поэтому может стать ценным навыком для совершенствования (как программирование)
источник
Не вдаваясь в полноценное управление версиями + упаковка + модульные тесты (что является хорошей практикой программирования, которой вы должны попытаться достичь в какой-то момент), одно промежуточное решение, которое, я думаю, подойдет, - это использование Jupiter Notebook . Кажется, это лучше интегрируется с научными вычислениями.
Преимущество в том, что вы можете смешивать свои мысли с кодом; объяснение, почему подход лучше другого, и оставление старого кода без изменений в специальном разделе. Кроме того, правильное использование ячеек естественным образом приведет вас к фрагментации кода и организации его в функции, которые могут помочь его пониманию.
источник
Лучшие ответы уже хороши, но я хотел бы ответить на некоторые ваши вопросы напрямую.
Размер кода напрямую не связан с необходимостью модульных тестов. Это связано косвенно: модульные тесты более ценны в сложных кодовых базах, а небольшие кодовые базы обычно не так сложны, как большие.
Модульные тесты сияют для кода, где легко ошибиться или когда у вас будет много реализаций этого кода. Модульные тесты мало помогают вам в текущей разработке, но они в значительной степени предотвращают ваши ошибки в будущем, которые приводят к тому, что существующий код внезапно начинает плохо себя вести (даже если вы этого не касались).
Допустим, у вас есть приложение, в котором библиотека A выполняет возведение в квадрат чисел, а библиотека B применяет теорему Пифагора. Очевидно, что B зависит от A. Вам нужно что-то исправить в библиотеке A, и, скажем, вы вводите ошибку, которая кубизирует числа вместо их возведения в квадрат.
Библиотека B внезапно начнет плохо себя вести, возможно, выбрасывает исключения или просто выдает неправильный вывод. И когда вы смотрите на историю версий библиотеки B, вы видите, что она не тронута. Проблематичным конечным результатом является то, что у вас нет признаков того, что может пойти не так, и вам придется отлаживать поведение B, прежде чем вы поймете, что проблема в A. Это напрасная трата усилий.
Введите юнит-тесты. Эти тесты подтверждают, что библиотека A работает как задумано. Если вы внесете ошибку в библиотеку A, которая приведет к тому, что она выдаст плохие результаты, тогда ваши модульные тесты это поймут. Поэтому вы не будете застревать, пытаясь отладить библиотеку B.
Это выходит за рамки вашей компетенции, но в процессе непрерывной интеграции модульные тесты выполняются всякий раз, когда кто-то фиксирует какой-то код, что означает, что вы будете знать, что что-то сломали как можно скорее.
Специально для сложных математических операций юнит-тесты могут быть благословением. Вы делаете несколько примеров вычислений, а затем пишете модульные тесты, в которых сравнивались ваши рассчитанные результаты и фактические результаты (на основе тех же входных параметров).
Однако обратите внимание, что модульные тесты не помогут вам создать хороший код, а скорее поддержат его. Если вы обычно пишете код один раз и никогда не возвращаетесь к нему, модульные тесты будут менее полезными.
ООП - это способ думать об отдельных объектах, например:
Сравните это с тем, как функциональный программист думает о вещах:
Яблоки и апельсины. Ни один из них объективно не лучше другого. Интересно отметить, что для ООП
Vendor
упоминается дважды, но это относится к одной и той же вещи. Впрочем, для функционального программированияtalktoVendor()
иpayVendor()
есть две разные вещи.Это демонстрирует разницу между подходами. Если между этими двумя действиями существует много общей логики, специфичной для поставщика, то ООП поможет уменьшить дублирование кода. Однако, если между ними нет общей логики, объединение их в один
Vendor
- бесполезная работа (и, следовательно, функциональное программирование более эффективно).Чаще всего математические и научные вычисления представляют собой отдельные операции, которые не основаны на неявной общей логике / формулах. Из-за этого функциональное программирование используется чаще, чем ООП.
Ваш вопрос подразумевает, что определение «хорошего, чистого кода» меняется независимо от того, занимаетесь ли вы научным программированием или работаете над большими (я предполагаю, вы имеете в виду корпоративные) проектами.
Определение хорошего кода не меняется. Однако необходимость избегать сложности (которая может быть сделана путем написания чистого кода) меняется.
Тот же аргумент возвращается сюда.
Я понял различие, которое вы здесь делаете, но когда вы оглядываетесь на существующий код, вы смотрите и на математику, и на программирование. Если либо надуманный или сложным, то вы будете бороться , чтобы прочитать его.
Помимо принципов ООП, основная причина, по которой я пишу классы для хранения нескольких значений данных, заключается в том, что это упрощает объявление параметров метода и возвращаемых значений. Например, если у меня много методов, которые используют местоположение (широта / долгота), я быстро устану печатать
float latitude, float longitude
и предпочитаю писатьLocation loc
.Это усугубляется, если учесть, что методы обычно возвращают одно значение (если только для языка не существует специфических функций для возврата большего количества значений), и такие вещи, как местоположение, требуют, чтобы вы возвращали два значения (широта + долгота). Это побуждает вас создавать
Location
класс для упрощения вашего кода.Еще одна интересная вещь, которую стоит отметить, это то, что вы можете использовать ООП, не смешивая значения и методы данных Не каждый разработчик соглашается здесь (некоторые называют это антипаттерном), но у вас могут быть анемичные модели данных, в которых у вас есть отдельные классы данных (поля значений) и логические классы (методы методов).
Это, конечно, по спектру. Вам не нужно быть абсолютно анемичным, вы можете использовать его, когда считаете нужным.
Например, метод, который просто объединяет имя и фамилию человека, все еще может быть размещен в самом
Person
классе, потому что это на самом деле не «логика», а скорее вычисленное значение.Класс всегда такой большой, как сумма его полей. Возьмем
Location
снова пример , который состоит из двухfloat
значений, здесь важно отметить, что одинLocation
объект будет занимать столько же памяти, сколько два отдельныхfloat
значения.В этом смысле не имеет значения, используете ли вы ООП или нет. Объем памяти остается прежним.
Само исполнение также не является большим препятствием для пересечения. Разница между, например, использованием глобального метода или метода класса не имеет ничего общего с производительностью во время выполнения, но имеет отношение к генерации байт-кода во время компиляции.
Подумайте об этом так: то, пишу ли я свой рецепт торта на английском или испанском, не меняет того факта, что для выпечки торта потребуется 30 минут (= производительность во время выполнения). Единственное, что меняет язык рецепта - это то, как повар смешивает ингредиенты (= компилирует байт-код).
В частности, для Python вам не нужно явно предварительно компилировать код перед его вызовом. Однако, когда вы не выполняете предварительную компиляцию, компиляция будет происходить при попытке выполнить код. Когда я говорю «время выполнения», я имею в виду само выполнение, а не компиляцию, которая могла предшествовать выполнению.
источник
Преимущества чистого научного кода
Может быть полезно рассмотреть ваш код с точки зрения будущего кодера.
Из моего опыта,
Чистый код должен облегчить проверку ваших результатов
Возможно, вы захотите разделить вашу программу, чтобы отдельные алгоритмы можно было сравнить отдельно.
Избегайте написания функций с нелогичными побочными эффектами, когда одна не связанная операция приводит к тому, что другая операция ведет себя по-другому. Если вы не можете избежать этого, запишите, что нужно вашему коду и как его настроить.
Чистый код может служить примером кода для будущих кодеров
Четкие комментарии (в том числе те, которые показывают, как следует вызывать функции) и хорошо разделенные функции могут существенно повлиять на то, сколько времени понадобится кому-то, только начинающему (или будущему вам), сделать что-то полезное из вашей работы.
В дополнение к этому, создание настоящего «API» для вашего алгоритма может сделать вас лучше подготовленным, если вы решите превратить свои скрипты в настоящую библиотеку, которую кто-то другой сможет использовать.
рекомендации
«Цитировать» математические формулы, используя комментарии.
John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180
, что , если вы нашли формулу на веб-сайте или в газете, приведите и ее.Используйте комментарии с умом
Если вы можете улучшить читабельность своего кода, используя правильные имена переменных / имен функций, сделайте это в первую очередь. Помните, что комментарии будут сохраняться до тех пор, пока вы их не удалите, поэтому постарайтесь делать комментарии, которые не устареют.
Используйте описательные имена переменных
xBar_AverageVelocity
Напишите код для запуска вашей программы против известных хороших и известных плохих данных.
Я думаю, что модульное тестирование может быть полезным, я думаю, что лучшая форма модульного тестирования для научного кода - это серия тестов, которые работают на известных плохих и хороших данных.
Напишите некоторый код для запуска вашего алгоритма и проверьте, насколько далеко результат отличается от ожидаемого. Это поможет вам найти (потенциально очень плохие и трудные для поиска) проблемы, когда вы случайно жестко кодируете что-то, что приводит к ложному положительному результату, или допускаете ошибку, из-за которой функция всегда возвращает одно и то же значение.
Обратите внимание, что это может быть сделано на любом уровне абстракции. Например, вы можете протестировать весь алгоритм сопоставления с образцом или проверить функцию, которая просто вычисляет расстояние между двумя результатами в процессе оптимизации. Сначала начните с областей, которые наиболее важны для ваших результатов, и / или частей кода, которые вас больше всего волнуют.
Упростите добавление новых тестовых случаев, рассмотрите возможность добавления вспомогательных функций и эффективно структурируйте входные данные. Это может означать, возможно, сохранение входных данных в файл, чтобы вы могли легко повторно запускать тесты, хотя будьте очень осторожны, чтобы избежать ложных срабатываний или предвзятых / тривиально разрешенных тестовых случаев.
Подумайте об использовании чего-то вроде перекрестной проверки , см. Этот пост о перекрестной проверке для получения дополнительной информации.
Использовать контроль версий
Я бы рекомендовал использовать контроль версий и разместить свой репозиторий на внешнем сайте. Есть сайты, на которых будут размещаться репозитории бесплатно.
Преимущества:
Будьте осторожны при копировании / вставке кода
Копирование / вставка кода может сэкономить ваше время, но это одна из самых опасных вещей, которые вы можете сделать, особенно если это код, который вы не написали самостоятельно (например, если это код от коллеги).
Как только вы получите работающий и протестированный код, я рекомендую пройти его очень внимательно, чтобы переименовать любые переменные или прокомментировать то, что вы не понимаете.
источник
Инструменты торговли обычно изобретаются, чтобы решить потребность. Если вам нужно использовать инструмент, если нет, вам, скорее всего, не нужно.
В частности, научные программы не являются конечной целью, они являются средством. Вы пишете программу, чтобы решить проблему, которая у вас есть сейчас - вы не ожидаете, что эта программа будет использоваться другими (и ее нужно будет обслуживать) через десять лет. Это само по себе означает, что вам не нужно вдаваться в какие-либо инструменты, которые позволяют текущему разработчику записывать историю для других, например, контроль версий, или записывать функциональность в коде, например, в модульных тестах.
Что тогда принесет вам пользу?
источник
В дополнение к хорошему совету, который уже здесь, вы можете рассмотреть цель вашего программирования и, следовательно, что важно для вас.
«Это больше о математике или науке, которые я тестирую или исследую с помощью программирования».
Если цель состоит в том, чтобы поэкспериментировать и протестировать что-то для вашего собственного понимания, и вы знаете, какими должны быть результаты, тогда ваш код, по сути, быстро отбрасывается, и ваш текущий подход может быть достаточным, хотя и может быть улучшен. Если результаты не соответствуют ожидаемым, вы можете вернуться и просмотреть.
Однако, если результаты вашего кодирования определяют направление вашего исследования, и вы не знаете, какими должны быть результаты , то правильность становится особенно важной. Ошибка в вашем коде может привести к тому, что вы сделаете неправильные выводы из своего эксперимента, что приведет к ряду плохих последствий для вашего общего исследования.
В этом случае разбиение вашего кода на легко понятные и проверяемые функции с помощью модульных тестов даст вам более прочные строительные кирпичики, придаст вам больше уверенности в ваших результатах и может избавить вас от многих разочарований позже.
источник
Управление версиями и модульное тестирование не только помогают поддерживать организованность и функциональность всего кода, но и не помогают писать более чистый код.
Если вы хотите , чтобы остановить себя от написания грязного кода, вам нужен инструмент , который работает , где мур происходит: когда вы писать код. Популярный инструмент, который делает это, называется линтером. Я не разработчик Python, но похоже, что Pylint может быть хорошим вариантом.
Линтер смотрит на код, который вы написали, и сравнивает его с настраиваемым набором лучших практик. Если у линтера есть правило, что переменные должны быть
camelCase
, и вы записываете ихsnake_case
, это помечается как ошибка. У хороших линтеров есть правила, варьирующиеся от «должны использоваться объявленные переменные» до «Цикломатическая сложность функций должна быть меньше 3».Большинство редакторов кода могут быть настроены для запуска linter каждый раз, когда вы сохраняете, или просто в целом, когда вы печатаете, и указывают на проблемы в строке. Если вы наберете что-то вроде
x = 7
,x
будет выделено, с инструкцией, чтобы использовать более длинное, лучшее имя (если это то, что вы настроили). Это работает как проверка орфографии в большинстве текстовых процессоров, затрудняя игнорирование и помогая выработать лучшие привычки.источник
Все, что вы перечислили, является метафорическим инструментом. Как и все в жизни, разные инструменты подходят для разных задач.
По сравнению с другими областями разработки, программное обеспечение работает с кучей отдельных частей, которые сами по себе довольно просты. Заявление о назначении не оценивается по-разному в зависимости от колебаний температуры в помещении.
if
Заявление не ржавеет на место и снова возвращается в том же самом через некоторое время. Но поскольку отдельные элементы настолько просты, а программное обеспечение создано людьми, эти элементы объединяются в большие и большие части, пока результат не станет настолько большим и сложным, что не достигнет пределов того, что люди могут мысленно управлять.Поскольку программные проекты росли и росли, люди изучали их и создавали инструменты, чтобы попытаться справиться с этой сложностью. ООП является одним из примеров. Все больше и больше абстрактных языков программирования являются еще одним средством. Поскольку большая часть денег в программном обеспечении делает все больше и больше , инструменты для достижения этого - то, что вы увидите и прочитаете. Но, похоже, эти ситуации не относятся к вам.
Так что не думайте, что вам нужно что-то делать. В конце концов, код - это всего лишь средство для достижения цели. К сожалению, то, что лучше всего даст вам правильное представление о том, что есть, а что нет, - это работать над некоторыми более крупными проектами, так как гораздо сложнее понять, чего не хватает, когда набор инструментов - ваш разум.
В любом случае, я не стал бы беспокоиться о том, чтобы не использовать ООП или другие методы, если ваши скрипты маленькие. Многие из проблем, которые вы описали, являются просто общими профессиональными организационными навыками, то есть не потерять старый файл - это то, с чем сталкиваются все поля.
источник
В дополнение ко всем хорошим предложениям, предоставленным до сих пор, одна практика, которую я изучил с течением времени и считаю необходимой, состоит в том, чтобы очень подробно добавить подробный комментарий к вашему коду. Это самая важная вещь для меня, когда я возвращаюсь к чему-то после долгого промежутка времени. Объясните себе, что вы думаете. Это занимает немного времени, но это относительно легко и в основном безболезненно.
Иногда у меня в два-три раза больше строк комментариев, чем у кода, особенно когда концепции или методы являются новыми для меня и требуют объяснений для меня.
Делайте контроль версий, улучшайте свои методы и т. Д. ... все вышеперечисленное. Но объясни себе что-то по ходу дела. Это работает очень хорошо.
источник
Какие качества важны для такой программы?
Вероятно, не имеет значения, легко ли его поддерживать или развивать, потому что есть вероятность, что этого не произойдет.
Вероятно, не имеет значения, насколько это эффективно.
Вероятно, не имеет значения, имеет ли он отличный пользовательский интерфейс или защищен от злоумышленников.
Может иметь значение, что он читабелен: что кто-то, читающий ваш код, может легко убедить себя, что он делает то, что заявляет.
Это, безусловно, важно, что это правильно. Если программа дает неверные результаты, это ваши научные выводы из окна. Но ему нужно только правильно обработать ввод, который вы фактически запрашиваете для обработки; на самом деле не имеет большого значения, перепадет ли он, если даны отрицательные значения входных данных, если все ваши значения данных положительны.
Также важно, чтобы вы поддерживали некоторый уровень контроля над изменениями. Ваши научные результаты должны быть воспроизводимыми, а это значит, что вам нужно знать, какая версия программы дала результаты, которые вы намерены опубликовать. Поскольку есть только один разработчик, управление изменениями не должно быть очень сложным, но вам нужно убедиться, что вы можете вернуться к определенному моменту времени и воспроизвести свои результаты.
Так что не беспокойтесь о парадигмах программирования, объектной ориентации, алгоритмической элегантности. Не беспокойтесь о ясности и удобочитаемости, а также о прослеживаемости ваших изменений с течением времени. Не беспокойтесь о пользовательском интерфейсе. Не беспокойтесь о тестировании каждой возможной комбинации входных параметров, но достаточно тестируйте, чтобы быть уверенными (и убедить других), что ваши результаты и выводы верны.
источник
Я работал в аналогичной среде с учеными, которые пишут много кода (математика / наука), но их продвижение идет медленно из-за тех же причин, которые вы описали. Однако я заметил одну конкретную вещь, которая прошла хорошо, и я думаю, что она также может вам помочь: создать и поддерживать коллекцию специализированных библиотек, которые можно использовать в нескольких проектах. Эти библиотеки должны предоставлять служебные функции и, следовательно, помогут сохранить ваш текущий проект специфичным для проблемной области.
Например, вам, возможно, придется иметь дело с множеством преобразований координат в вашей области (ECEF, NED, lat / lon, WGS84 и т. Д.), Поэтому такая функция
convert_ecef_to_ned()
должна войти в новый проект с именемCoordinateTransformations
. Поместите проект под контроль версий и разместите его на серверах вашего отдела, чтобы другие люди могли его использовать (и, надеюсь, улучшить). Затем через несколько лет у вас должна появиться надежная коллекция библиотек, в которых ваши проекты содержат только код, специфичный для конкретной проблемы / области исследования.Еще несколько общих советов:
источник
Ниже приведены мои мнения и очень сильно зависит от моего собственного пути.
Кодирование часто порождает догматические взгляды на то, как вы должны делать вещи. Вместо методов и инструментов, я думаю, вам нужно взглянуть на совокупные значения и затраты, чтобы принять решение о соответствующей стратегии.
Написание хорошего, удобочитаемого, отлаживаемого и надежного кода занимает много времени и усилий. Во многих случаях, учитывая ограниченный горизонт планирования, это не стоит делать (анализ паралича).
У одного коллеги было эмпирическое правило; если вы в третий раз делаете то же самое, то приложите усилия, в противном случае уместна быстрая и грязная работа.
Тестирование в некотором роде важно, но для одноразовых проектов может быть достаточно простого взгляда. Для чего-то существенного, тесты и тестовая инфраструктура имеют важное значение. Значение в том, что оно освобождает вас при кодировании, а цена в том, что если тест сфокусирован на конкретной реализации, то тесты тоже нуждаются в обслуживании. Тесты также напоминают вам о том, как все должно работать.
Для моих собственных одноразовых сценариев (часто для таких вещей, как проверка оценки вероятности и т. П.) Я обнаружил две очень полезные вещи: 1. Включите комментарий, показывающий, как используется этот код. 2. Включите краткое описание того, почему вы написали код. Эти вещи ужасно очевидны, когда вы пишете код, но очевидность теряется со временем :-).
ООП - это повторное использование кода, абстрагирование, инкапсуляция, факторинг и т. Д. Очень полезно, но легко потеряться, если создание качественного кода и дизайна не является вашей конечной целью. Требуется время и усилия, чтобы производить качественные вещи.
источник
Хотя я думаю, что модульные тесты имеют свои достоинства, они представляют сомнительную ценность для научных разработок - они часто просто слишком малы, чтобы предлагать большую ценность.
Но мне действительно нравятся интеграционные тесты для научного кода:
Выделите небольшой фрагмент своего кода, который может работать самостоятельно, например, конвейер ETL. Затем напишите тест, который предоставляет данные, запустите конвейер etl (или просто шаг), а затем проверьте, что результат соответствует вашим ожиданиям. Несмотря на то, что в тестируемом фрагменте может быть много кода, тест дает еще одно значение:
Я часто использую эту технику и часто получаю релятивно читаемую основную функцию, но подфункции часто бывают довольно длинными и некрасивыми, но их можно быстро изменить и перестроить из-за надежных границ ввода / вывода.
источник
Я обычно работаю на очень большой исходной базе. Мы используем все инструменты, которые вы упоминаете. Недавно я начал работать над некоторыми сценариями Python для стороннего проекта. Максимум от нескольких десятков до нескольких сотен строк. По привычке я передал свои скрипты в систему контроля версий. Это было полезно, потому что я могу создавать ветки для экспериментов, которые могут не работать. Я могу раскошелиться, если мне нужно продублировать код и изменить его для другой цели. Это оставляет оригинал в такте на случай, если мне понадобится снова его выпустить.
Для «модульных тестов» у меня просто есть несколько входных файлов, которые предназначены для получения некоторого известного результата, который я проверяю вручную. Я мог бы, вероятно, автоматизировать это, но мне кажется, что это заняло бы больше времени, чем я бы сэкономил на этом. Вероятно, это зависит от того, как часто мне приходится изменять и запускать скрипты. В любом случае, если это работает, сделайте это. Если это больше проблем, чем стоит, не тратьте свое время.
источник
С написанием кода - как и с написанием в целом - главный вопрос:
Такие вещи, как официальные правила кодирования, не имеют смысла, когда вы являетесь вашей единственной аудиторией.
При этом, с другой стороны, было бы полезно написать код таким образом, ваше будущее, вы можете понять это сразу.
Таким образом, «хороший стиль» будет тем, который поможет вам больше всего. Как должен выглядеть этот стиль - ответ, который я не могу дать.
Я думаю, что вам не нужны ООП или юнит-тесты для файлов 150 LOC. Выделенный VCS будет интересен, когда у вас есть развивающийся код. В противном случае это
.bak
делает трюк. Эти инструменты являются лекарством от болезни, вы можете даже не иметь.Возможно, вам следует написать свой код таким образом, чтобы даже если вы читали его, будучи пьяным, вы могли читать, понимать и изменять его.
источник