Bagaimana cara mendesain kelas dengan Python?

143

Saya sudah mendapatkan bantuan yang sangat luar biasa pada pertanyaan saya sebelumnya untuk mendeteksi kaki dan kaki dalam satu kaki , tetapi semua solusi ini hanya bekerja untuk satu pengukuran pada satu waktu.

Sekarang saya memiliki data yang terdiri dari:

  • sekitar 30 anjing;
  • masing-masing memiliki 24 pengukuran (dibagi menjadi beberapa subkelompok);
  • setiap pengukuran memiliki setidaknya 4 kontak (satu untuk setiap kaki) dan
    • setiap kontak dibagi menjadi 5 bagian dan
    • memiliki beberapa parameter, seperti waktu kontak, lokasi, kekuatan total dll.

teks alternatif

Jelas menempel semuanya menjadi satu objek besar tidak akan memotongnya, jadi saya pikir saya perlu menggunakan kelas bukan fungsi membunuh saat ini. Tetapi meskipun saya sudah membaca bab Belajar Python tentang kelas, saya gagal menerapkannya pada kode saya sendiri ( tautan GitHub )

Saya juga merasa agak aneh untuk memproses semua data setiap kali saya ingin mendapatkan beberapa informasi. Setelah saya tahu lokasi masing-masing kaki, tidak ada alasan bagi saya untuk menghitung ini lagi. Selanjutnya, saya ingin membandingkan semua cakar anjing yang sama untuk menentukan kontak mana yang memiliki cakar (depan / belakang, kiri / kanan). Ini akan menjadi berantakan jika saya terus menggunakan fungsi saja.

Jadi sekarang saya mencari saran tentang cara membuat kelas yang memungkinkan saya memproses data saya ( tautan ke data zip dari satu anjing ) dengan cara yang masuk akal.

Ivo Flipse
sumber
4
Anda mungkin juga ingin mempertimbangkan untuk menggunakan basis data (seperti sqlite: docs.python.org/library/sqlite3.html ). Anda bisa menulis sebuah program yang membaca file data besar Anda dan mengubahnya menjadi baris dalam tabel database. Kemudian sebagai tahap kedua Anda dapat menulis program yang menarik data dari database untuk melakukan analisis lebih lanjut.
unutbu
Maksudmu sesuatu seperti saya bertanya di sini @ubutbu? Saya berencana membuatnya melakukannya, tetapi pertama-tama saya ingin dapat memproses semua data dengan cara yang lebih terorganisir
Ivo Flipse

Jawaban:

434

Bagaimana cara mendesain sebuah kelas.

  1. Tuliskan kata-katanya. Anda mulai melakukan ini. Beberapa orang tidak dan heran mengapa mereka memiliki masalah.

  2. Rentangkan serangkaian kata Anda menjadi pernyataan sederhana tentang apa yang akan dilakukan objek-objek ini. Artinya, tuliskan berbagai perhitungan yang akan Anda lakukan pada hal-hal ini. Daftar pendek 30 anjing, 24 pengukuran, 4 kontak, dan beberapa "parameter" per kontak menarik, tetapi hanya sebagian dari cerita. "Lokasi masing-masing kaki" dan "bandingkan semua kaki anjing yang sama untuk menentukan kontak mana yang memiliki kaki" adalah langkah selanjutnya dalam desain objek.

  3. Garis bawahi kata benda. Serius. Beberapa orang memperdebatkan nilai ini, tetapi saya menemukan bahwa untuk pengembang OO pertama kali itu membantu. Garis bawahi kata benda.

  4. Tinjau kata benda. Kata benda umum seperti "parameter" dan "pengukuran" perlu diganti dengan kata benda konkret tertentu yang berlaku untuk masalah Anda di domain masalah Anda. Spesifik membantu mengklarifikasi masalah. Obat generik hanya menghilangkan detail.

  5. Untuk setiap kata benda ("kontak", "paw", "dog", dll.) Tuliskan atribut dari kata benda itu dan tindakan yang dilakukan objek tersebut. Jangan pintas ini. Setiap atribut. "Kumpulan Data berisi 30 Anjing" misalnya penting.

  6. Untuk setiap atribut, identifikasi apakah ini merupakan hubungan dengan kata benda yang ditentukan, atau jenis lain dari data "primitif" atau "atom" seperti string atau float atau sesuatu yang tidak dapat direduksi.

  7. Untuk setiap tindakan atau operasi, Anda harus mengidentifikasi kata benda mana yang memiliki tanggung jawab, dan kata benda mana yang hanya berpartisipasi. Ini adalah pertanyaan tentang "mutabilitas". Beberapa objek diperbarui, yang lain tidak. Benda yang bisa berubah harus memiliki tanggung jawab total atas mutasinya.

  8. Pada titik ini, Anda dapat mulai mengubah kata benda menjadi definisi kelas. Beberapa kata benda kolektif adalah daftar, kamus, tuple, set, atau namesuple, dan Anda tidak perlu melakukan terlalu banyak pekerjaan. Kelas-kelas lain lebih kompleks, baik karena data turunan kompleks atau karena beberapa pembaruan / mutasi yang dilakukan.

Jangan lupa untuk menguji setiap kelas secara terpisah menggunakan unittest.

Juga, tidak ada hukum yang mengatakan bahwa kelas harus bisa berubah. Dalam kasus Anda, misalnya, Anda hampir tidak memiliki data yang dapat diubah. Apa yang Anda miliki adalah data turunan, dibuat oleh fungsi transformasi dari dataset sumber.

S.Lott
sumber
24

Nasihat berikut (mirip dengan nasihat S.Lott) berasal dari buku, Beginning Python: From Novice to Professional

  1. Tuliskan deskripsi masalah Anda (apa yang harus dilakukan masalah?). Garisbawahi semua kata benda, kata kerja, dan kata sifat.

  2. Pergi melalui kata benda, mencari kelas potensial.

  3. Pergi melalui kata kerja, mencari metode potensial.

  4. Pergi melalui kata sifat, mencari atribut potensial

  5. Alokasikan metode dan atribut ke kelas Anda

Untuk memperbaiki kelas, buku ini juga menyarankan agar kita dapat melakukan hal berikut:

  1. Tulis (atau impikan) satu set kasus penggunaan - skenario bagaimana program Anda dapat digunakan. Coba tutupi semua secara fungsional.

  2. Pikirkan setiap kasus penggunaan selangkah demi selangkah, memastikan bahwa semua yang kita butuhkan tercakup.

mitchelllc
sumber
Akan lebih baik untuk memiliki beberapa contoh jenis kalimat yang seharusnya kita tulis.
endolith
14

Saya suka pendekatan TDD ... Jadi mulailah dengan menulis tes untuk apa perilaku yang Anda inginkan. Dan tulis kode yang lewat. Pada titik ini, jangan terlalu khawatir tentang desain, cukup dapatkan test suite dan perangkat lunak yang lulus. Jangan khawatir jika Anda berakhir dengan satu kelas jelek besar, dengan metode kompleks.

Terkadang, selama proses awal ini, Anda akan menemukan perilaku yang sulit untuk diuji dan perlu diurai, hanya untuk diuji. Ini mungkin merupakan petunjuk bahwa kelas yang terpisah dijamin.

Kemudian bagian yang menyenangkan ... refactoring. Setelah Anda memiliki perangkat lunak yang berfungsi, Anda dapat melihat bagian yang rumit. Seringkali kantong kecil perilaku akan menjadi jelas, menyarankan kelas baru, tetapi jika tidak, cari saja cara untuk menyederhanakan kode. Ekstrak objek layanan dan nilai objek. Sederhanakan metode Anda.

Jika Anda menggunakan git dengan benar (Anda menggunakan git, bukan?), Anda dapat dengan cepat bereksperimen dengan beberapa dekomposisi selama refactoring, dan kemudian meninggalkannya dan kembali lagi jika itu tidak menyederhanakan banyak hal.

Dengan menulis kode kerja yang teruji terlebih dahulu, Anda harus mendapatkan wawasan yang mendalam tentang domain masalah yang tidak mudah Anda dapatkan dengan pendekatan desain-pertama. Tes tertulis dan kode mendorong Anda melewati kelumpuhan "di mana saya mulai".

Les Nightingill
sumber
1
Saya juga setuju dengan jawaban ini, walaupun memecah masalah dan mengidentifikasi kelas yang mungkin (yaitu melakukan arsitektur perangkat lunak "cukup") dapat sangat berguna jika masalah akan dikerjakan secara paralel oleh beberapa anggota tim.
Ben Smith
3

Seluruh ide desain OO adalah membuat peta kode untuk masalah Anda, jadi ketika, misalnya, Anda ingin langkah pertama seekor anjing, Anda melakukan sesuatu seperti:

dog.footstep(0)

Sekarang, mungkin untuk kasus Anda, Anda perlu membaca di file data mentah Anda dan menghitung lokasi jejak. Semua ini bisa disembunyikan dalam fungsi footstep () sehingga hanya terjadi sekali. Sesuatu seperti:

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

[Ini sekarang semacam pola caching. Pertama kali ia pergi dan membaca data footstep, kali berikutnya ia hanya mendapatkannya dari self._footsteps.]

Tapi ya, mendapatkan desain OO yang benar bisa sulit. Pikirkan lebih lanjut tentang hal-hal yang ingin Anda lakukan untuk data Anda, dan itu akan menginformasikan metode apa yang perlu Anda terapkan ke kelas apa.

Spacedman
sumber
2

Menuliskan kata benda, kata kerja, kata sifat Anda merupakan pendekatan yang hebat, tetapi saya lebih suka menganggap desain kelas sebagai mengajukan pertanyaan data apa yang harus disembunyikan ?

Bayangkan Anda memiliki Queryobjek dan Databaseobjek:

The Queryobjek akan membantu Anda membuat dan menyimpan query - toko, adalah kunci di sini, sebagai fungsi bisa membantu Anda membuat satu dengan mudah. Mungkin Anda bisa tinggal: Query().select('Country').from_table('User').where('Country == "Brazil"'). Tidak masalah persis sintaksisnya - itu adalah pekerjaan Anda! - kuncinya adalah objek membantu Anda menyembunyikan sesuatu , dalam hal ini data yang diperlukan untuk menyimpan dan menampilkan kueri. Kekuatan objek berasal dari sintaks menggunakannya (dalam hal ini beberapa rantai pintar) dan tidak perlu tahu apa yang disimpannya untuk membuatnya bekerja. Jika dilakukan dengan benar, Queryobjek dapat menampilkan kueri untuk lebih dari satu basis data. Secara internal ia akan menyimpan format tertentu tetapi dapat dengan mudah dikonversi ke format lain saat mengeluarkan (Postgres, MySQL, MongoDB).

Sekarang mari kita pikirkan Databaseobjeknya. Apa yang disembunyikan dan disimpan oleh ini? Yah jelas itu tidak bisa menyimpan isi penuh dari database, karena itu sebabnya kami punya database! Jadi apa gunanya? Tujuannya adalah untuk menyembunyikan cara kerja database dari orang yang menggunakan Databaseobjek. Kelas yang baik akan menyederhanakan penalaran ketika memanipulasi keadaan internal. Untuk Databaseobjek ini Anda bisa menyembunyikan cara kerja panggilan jaringan, atau permintaan batch atau pembaruan, atau memberikan lapisan caching.

Masalahnya adalah Databaseobjek ini BESAR. Ini mewakili cara mengakses database, jadi di balik selimut itu bisa melakukan apa saja. Jelas jaringan, caching, dan batching sangat sulit untuk ditangani tergantung pada sistem Anda, jadi menyembunyikannya akan sangat membantu. Tetapi, seperti yang diketahui oleh banyak orang, sebuah basis data sangat kompleks, dan semakin jauh dari panggilan DB mentah yang Anda dapatkan, semakin sulit untuk menyesuaikan kinerja dan memahami cara kerja berbagai hal.

Ini adalah tradeoff mendasar dari OOP. Jika Anda memilih abstraksi yang benar, membuat pengodean menjadi lebih mudah (String, Array, Dictionary), jika Anda memilih abstraksi yang terlalu besar (Database, EmailManager, NetworkingManager), mungkin menjadi terlalu rumit untuk benar-benar memahami cara kerjanya, atau apa yang harus dilakukan. mengharapkan. Tujuannya adalah untuk menyembunyikan kompleksitas , tetapi beberapa kompleksitas diperlukan. Aturan praktis yang baik adalah mulai menghindari Managerobjek, dan sebaliknya membuat kelas yang seperti structs- yang mereka lakukan hanyalah memegang data, dengan beberapa metode pembantu untuk membuat / memanipulasi data untuk membuat hidup Anda lebih mudah. Misalnya, dalam kasus EmailManagermulai dengan fungsi yang dipanggil sendEmailyang mengambil Emailobjek. Ini adalah titik awal yang sederhana dan kodenya sangat mudah dimengerti.

Sebagai contoh Anda, pikirkan tentang data apa yang perlu disatukan untuk menghitung apa yang Anda cari. Jika Anda ingin tahu seberapa jauh seekor binatang berjalan, misalnya, Anda dapat memiliki AnimalStepdan AnimalTrip(mengumpulkan AnimalSteps) kelas. Sekarang setiap perjalanan memiliki semua data Langkah, maka harus bisa mencari tahu tentang hal itu, mungkin AnimalTrip.calculateDistance()masuk akal.

Evan Moran
sumber
2

Setelah membaca kode tertaut Anda, sepertinya Anda lebih baik tidak merancang kelas Anjing pada saat ini. Sebaliknya, Anda harus menggunakan Panda dan bingkai data . Kerangka data adalah tabel dengan kolom. Anda dataframe akan memiliki kolom seperti: dog_id, contact_part, contact_time, contact_location, dll Panda menggunakan Numpy array di belakang layar, dan memiliki banyak metode kenyamanan untuk Anda:

  • Pilih seekor anjing dengan mis: my_measurements['dog_id']=='Charly'
  • simpan data: my_measurements.save('filename.pickle')
  • Pertimbangkan untuk menggunakan pandas.read_csv()daripada membaca file teks secara manual.
cyborg
sumber