Как мне разработать класс в Python?


143

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

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

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

альтернативный текст

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

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

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


4
Вы также можете рассмотреть возможность использования базы данных (например, sqlite: docs.python.org/library/sqlite3.html ). Вы можете написать программу, которая читает ваши огромные файлы данных и преобразует их в строки в таблицах базы данных. Затем на втором этапе вы можете написать программы, которые извлекают данные из базы данных для дальнейшего анализа.
unutbu

Вы имеете в виду что-то вроде того, что я спросил здесь @ubutbu? Я планирую заставить это сделать это, но сначала я бы хотел иметь возможность обрабатывать все данные более организованно
Ivo Flipse

Ответы:


434

Как спроектировать класс.

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

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

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

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

  5. Для каждого существительного («контакт», «лапа», «собака» и т. Д.) Запишите атрибуты этого существительного и действия, в которых участвует этот объект. Не сокращайте это. Каждый атрибут. Например, «Набор данных содержит 30 собак».

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

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

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

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

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


24

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

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

  2. Пройдите существительные в поисках потенциальных классов.

  3. Перебирайте глаголы в поисках потенциальных методов.

  4. Пройдите прилагательные, ища потенциальные признаки

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

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

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

  2. Шаг за шагом продумывайте каждый вариант использования, следя за тем, чтобы все необходимое нам было доступно.


Было бы хорошо иметь несколько примеров предложений, которые мы должны написать.
эндолит

14

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

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

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

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

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


1
Я тоже согласен с этим ответом, хотя разбивка проблемы и определение возможных классов (то есть выполнение «достаточно» архитектуры программного обеспечения) могут быть очень полезны, если над проблемой будут параллельно работать несколько членов команды.
Бен Смит

3

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

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.]

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


2

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

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

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

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

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

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

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


2

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

  • Выберите собаку, например: my_measurements['dog_id']=='Charly'
  • сохранить данные: my_measurements.save('filename.pickle')
  • Рассмотреть возможность использования pandas.read_csv() вместо чтения текстовых файлов вручную.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.