Як створити клас на Python?


143

У моїх попередніх запитаннях щодо виявлення лап і пальців ноги в лапі я мав справді приголомшливу допомогу , але всі ці рішення працюють лише за одне вимірювання.

Тепер у мене є дані, які складаються з:

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

alt текст

Очевидно, що стикання всього в одному великому об'єкті не збирається його різати, тому я зрозумів, що мені потрібно використовувати класи замість поточного функціонування. Але, хоча я читав розділ Learning Python про класи, я не можу застосувати його до власного коду ( посилання GitHub )

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

Отож зараз я шукаю поради щодо створення класів, які дозволять мені обробляти свої дані ( посилання на заархівовані дані однієї собаки ) розумним чином.


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

Ви маєте на увазі щось подібне, я запитав тут @ubutbu? Я планую зробити так, щоб це зробити, але спочатку я хотів би мати можливість обробляти всі дані більш організовано
Іво Фліпс

Відповіді:


434

Як спроектувати клас.

  1. Запишіть слова. Ви почали це робити. Деякі люди не цікавляться, чому вони мають проблеми.

  2. Розкрийте набір слів на прості висловлювання про те, що ці об’єкти будуть робити. Тобто запишіть різні розрахунки, які ви будете робити на ці речі. Ваш короткий список із 30 собак, 24 вимірювання, 4 контакти та кілька «параметрів» на контакт цікавий, але лише частина історії. Ваші "місця розташування кожної лапи" та "порівняйте всі лапи однієї собаки, щоб визначити, який контакт належить до якої лапи", є наступним кроком у проектуванні об'єкта.

  3. Підкресліть іменники. Серйозно. Деякі люди обговорюють цінність цього, але я вважаю, що для розробників OO, що вперше це допомагає, це допомагає. Підкресліть іменники.

  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)

Тепер, можливо, для вашого випадку вам потрібно прочитати у вашому файлі необроблених даних і обчислити місця кроків. Все це може бути приховано у функції стоп (), щоб це відбулося лише один раз. Щось на зразок:

 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), якщо ви вибрали занадто велику абстракцію (База даних, EmailManager, NetworkingManager), вона може стати занадто складною, щоб зрозуміти, як вона працює, або що робити очікувати. Мета - приховати складність , але певна складність необхідна. Добре правило - почати уникати Managerоб'єктів, а натомість створювати схожі класи structs- все, що вони роблять, - це зберігати дані, за допомогою деяких допоміжних методів створювати / маніпулювати даними, щоб полегшити ваше життя. Наприклад, у випадку EmailManagerзапуску з функцією, яка називається, sendEmailяка бере Emailоб'єкт. Це проста відправна точка, і код дуже легко зрозуміти.

Що стосується вашого прикладу, подумайте, які дані повинні бути разом, щоб обчислити те, що ви шукаєте. Якщо ви хочете дізнатися, як далеко ходить тварина, наприклад, ви могли б провести AnimalStepі AnimalTrip(колекція AnimalSteps) занять. Тепер, коли кожна поїздка має всі дані про крок, то, мабуть, AnimalTrip.calculateDistance()має сенс розібратися в цьому .


2

Після того, як ви продемонстрували свій зв'язаний код, мені здається, що вам краще не розробляти клас собаки на даний момент. Скоріше, ви повинні використовувати Pandas та фрейми даних . Рамка даних - це таблиця зі стовпцями. Ви 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.