Как создать класс в Python?

У меня была действительно потрясающая помощь по моим предыдущим вопросам для обнаружения лап и ног в лапе, но все эти решения работают только для одного измерения за раз.

Теперь у меня есть данные, которые состоят из:

  • около 30 собак;
  • каждый из которых имеет 24 измерения (разделенных на несколько подгрупп);
  • каждое измерение имеет по меньшей мере 4 контакта (по одному для каждой лапы) и
    • каждый контакт разделен на 5 частей и
    • имеет несколько параметров, таких как время контакта, местоположение, общая сила и т.д.

alt text

Очевидно, что все в одном крупном объекте не собирается сокращать его, поэтому я решил, что мне нужно использовать классы вместо текущего количества функций. Но даже если я прочитал главу Learning Python о классах, я не могу применить ее к своему собственному коду (ссылка GitHub)

Я также чувствую, что довольно странно обрабатывать все данные каждый раз, когда я хочу получить некоторую информацию. Как только я узнаю расположение каждой лапы, мне нечего подсчитывать. Кроме того, я хочу сравнить все лапы одной и той же собаки, чтобы определить, какой контакт принадлежит той лапе (передняя/задняя, левая/правая). Это будет беспорядок, если я буду продолжать использовать только функции.

Итак, теперь я ищу советы о том, как создавать классы, которые позволят мне обрабатывать мои данные (ссылку на данные на молнии одной собаки) разумным образом.

Ответ 1

Как создать класс.

  • Запишите слова. Вы начали это делать. Некоторые люди не интересуются, почему у них проблемы.

  • Разверните свой набор слов в простые утверждения о том, что будут делать эти объекты. То есть запишите различные расчеты, которые вы будете делать по этим вещам. Ваш короткий список из 30 собак, 24 измерения, 4 контакта и несколько "параметров" для каждого контакта интересны, но только часть истории. Ваши "местоположения каждой лапы" и "сравнить все лапы той же собаки, чтобы определить, какой контакт принадлежит той лапе", являются следующим шагом в дизайне объекта.

  • Подчеркните существительные. Шутки в сторону. Некоторые люди обсуждают ценность этого, но я нахожу, что для разработчиков OO для начинающих это помогает. Подчеркните существительные.

  • Просмотрите существительные. Общие имена, такие как "параметр" и "измерение", должны быть заменены конкретными, конкретными существительными, которые относятся к вашей проблеме в вашей проблемной области. Специфика помогает прояснить проблему. Дженерики просто детализируют детали.

  • Для каждого существительного ( "контакт", "лапа", "собака" и т.д.) записывают атрибуты этого существительного и действия, в которых участвует этот объект. Не сокращайте это. Каждый атрибут. "Набор данных содержит 30 собак", например, важен.

  • Для каждого атрибута укажите, является ли это отношением к определенному существительному или некоторым другим "примитивным" или "атомным" данным, таким как строка или поплавок или что-то неприводимое.

  • Для каждого действия или операции вы должны определить, какое существительное несет ответственность, а какие существительные просто участвуют. Это вопрос "изменчивости". Некоторые объекты обновляются, другие - нет. Мутируемые объекты должны нести полную ответственность за свои мутации.

  • На этом этапе вы можете начать преобразовывать существительные в определения классов. Некоторые коллективные существительные - это списки, словари, кортежи, наборы или именованные элементы, и вам не нужно много работать. Другие классы более сложны либо из-за сложных производных данных, либо из-за некоторых обновлений/мутаций, которые выполняются.

Не забудьте протестировать каждый класс отдельно, используя unittest.

Кроме того, нет закона, согласно которому классы должны быть изменчивыми. Например, в вашем случае вы почти не изменяете данные. У вас есть производные данные, созданные функциями преобразования из исходного набора данных.

Ответ 2

Следующие советы (похожие на советы @S.Lott) взяты из книги Начало Python: от новичка до профессионала

  • Запишите описание вашей проблемы (что должно быть с проблемой?). Подчеркните все существительные, глаголы и прилагательные.

  • Пройдите через существительные, ищите потенциальные классы.

  • Пройдитесь по глаголам, ищите потенциальные методы.

  • Пройдите через прилагательные, ищите потенциальные атрибуты

  • Выделить методы и атрибуты для ваших классов

Чтобы уточнить класс, в книге также говорится, что мы можем сделать следующее:

  • Запишите (или придумайте) набор вариантов использования - сценарии использования вашей программы. Попытайтесь охватить все функционально.

  • Постепенно продумайте каждый прецедент, убедившись, что все, что нам нужно, покрывается.

Ответ 3

Мне нравится подход TDD... Поэтому начните с написания тестов для того, что вы хотите, чтобы это было. И напишите код, который проходит. На данный момент, не беспокойтесь слишком много о дизайне, просто получите комплект тестов и программное обеспечение, которое проходит. Не волнуйтесь, если вы закончите с одним большим уродливым классом со сложными методами.

Иногда во время этого начального процесса вы обнаружите поведение, которое трудно проверить, и его нужно разложить, просто для проверки. Это может быть намек на то, что отдельный класс гарантирован.

Тогда интересная часть... рефакторинг. После того, как у вас есть программное обеспечение, вы можете увидеть сложные фрагменты. Часто появляются небольшие карманы поведения, предлагая новый класс, но если нет, просто ищите способы упростить код. Извлечь объекты службы и объекты значений. Упростите свои методы.

Если вы используете git правильно (вы используете git, не так ли?), вы можете очень быстро поэкспериментировать с определенной декомпозицией во время рефакторинга, а затем отказаться от него и вернуться обратно, упростить вещи.

Вначале написав тестовый рабочий код, вы должны получить интимное представление о проблемном домене, с которым вы не могли легко получить подход, основанный на дизайне. Написание тестов и кода проталкивают вас назад, что "где я начинаю" паралич.

Ответ 4

Вся идея дизайна OO - сделать вашу кодовую карту вашей проблемой, поэтому, когда вы, например, хотите первый шаг собаки, вы делаете что-то вроде:

dog.footstep(0)

Теперь может случиться так, что для вашего случая вам нужно прочитать в своем необработанном файле данных и вычислить местоположения шагов. Все это может быть скрыто в функции footstep(), так что это происходит только один раз. Что-то вроде:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[Это теперь своего рода шаблон кэширования. В первый раз, когда он идет и читает данные о шагах, в последующие моменты он просто получает его от self._footsteps.]

Но да, получение права на дизайн OO может быть сложным. Подумайте больше о том, что вы хотите сделать с вашими данными, и это сообщит, какие методы вам нужно применить к тем классам.

Ответ 5

Выписывание ваших существительных, глаголов, прилагательных - отличный подход, но я предпочитаю думать о дизайне классов, задавая вопрос, какие данные должны быть скрыты?

Представьте, что у вас есть объект Query и объект Database:

Объект Query поможет вам создать и сохранить хранилище запросов, является ключевым здесь, поскольку функция может помочь вам создать его так же легко. Может быть, вы можете остаться: Query().select('Country').from_table('User').where('Country == "Brazil"'). Неважно, какой именно синтаксис - это ваша работа! - ключ - это объект, который помогает вам скрыть что-то, в этом случае данные, необходимые для хранения и вывода запроса. Сила объекта исходит из синтаксиса его использования (в данном случае некоторой умной цепочки) и не нужно знать, что он хранит, чтобы заставить его работать. Если все сделано правильно, объект Query может выводить запросы для более одной базы данных. Он внутренне хранит определенный формат, но может легко конвертировать в другие форматы при выводе (Postgres, MySQL, MongoDB).

Теперь рассмотрим объект Database. Что это скрывает и хранит? Хорошо, что он не может хранить полное содержимое базы данных, потому что именно поэтому у нас есть база данных! Так в чем смысл? Цель состоит в том, чтобы скрыть, как работает база данных от людей, которые используют объект Database. Хорошие классы упрощают рассуждения при манипулировании внутренним состоянием. Для этого объекта Database вы можете скрыть, как работают сетевые вызовы, или пакетные запросы или обновления, или обеспечить уровень кэширования.

Проблема в том, что этот объект Database HUGE. Он представляет собой доступ к базе данных, поэтому под обложками он может делать все и вся. Очевидно, что создание сетей, кэширование и пакетная обработка довольно сложно решать в зависимости от вашей системы, поэтому их скрыть было бы очень полезно. Но, как многие люди заметят, база данных безумно сложна, и чем дальше от необработанных БД вы получаете, тем сложнее настраиваться на производительность и понимать, как все работает.

Это фундаментальный компромисс ООП. Если вы выберете правильную абстракцию, она упростит кодирование (String, Array, Dictionary), если вы выберете слишком большую абстракцию (Database, EmailManager, NetworkingManager), она может стать слишком сложной, чтобы действительно понять, как она работает, или что ожидать. Цель состоит в том, чтобы скрыть сложность, но необходима определенная сложность. Хорошее эмпирическое правило состоит в том, чтобы начать избегать Manager объектов и вместо этого создавать классы, которые похожи на structs - все, что они делают, это данные удержания, с некоторыми вспомогательными методами для создания/управления данными, чтобы сделать вашу жизнь проще. Например, в случае EmailManager начните с функции с именем sendEmail, которая примет объект Email. Это простая отправная точка, и код очень легко понять.

Что касается вашего примера, подумайте о том, какие данные должны быть вместе, чтобы рассчитать то, что вы ищете. Если вы хотите узнать, как далеко продвигается животное, вы можете иметь классы AnimalStep и AnimalTrip (коллекция AnimalSteps). Теперь, когда каждая Поездка имеет все данные Step, тогда она должна быть способна отображать информацию об этом, возможно, AnimalTrip.calculateDistance() имеет смысл.

Ответ 6

После скрещивания связанного кода мне кажется, что вам лучше не разрабатывать класс Dog в этот момент. Скорее, вы должны использовать Pandas и dataframes. Dataframe - это таблица со столбцами. У вас в dataframe будут такие столбцы, как: dog_id, contact_part, contact_time, contact_location и т.д. Pandas использует массивы Numpy за кулисами, и у вас есть много удобных методов для вас:

  • Выберите собаку, например.: my_measurements['dog_id']=='Charly'
  • сохранить данные: my_measurements.save('filename.pickle')
  • Подумайте об использовании pandas.read_csv() вместо ручного чтения текстовых файлов.