Python'da nasıl sınıf tasarlayabilirim?


143

Bir pençe içindeki pençeleri ve ayak parmaklarını tespit etmek için önceki sorularımda gerçekten harika bir yardım aldım , ancak tüm bu çözümler bir seferde sadece bir ölçüm için çalışıyor.

Şimdi oluşan veri var:

  • yaklaşık 30 köpek;
  • her birinin 24 ölçümü vardır (birkaç alt gruba ayrılmıştır);
  • her ölçümün en az 4 kontağı vardır (her bir pençe için bir tane) ve
    • her temas 5 bölüme ayrılmıştır ve
    • temas süresi, konum, toplam kuvvet vb.Gibi çeşitli parametrelere sahiptir.

alternatif metin

Açıkçası her şeyi büyük bir nesneye yapıştırmak onu kesmeyecek, bu yüzden mevcut işlevler yerine sınıfları kullanmam gerektiğini düşündüm. Ama Learning Python'un sınıflarla ilgili bölümünü okuduğum halde, bunu kendi koduma uygulayamıyorum ( GitHub bağlantısı )

Ayrıca, her bilgi almak istediğimde tüm verileri işlemenin oldukça garip olduğunu hissediyorum . Her pençenin yerini öğrendikten sonra, bunu tekrar hesaplamam için bir sebep yok. Ayrıca, hangi kontağın hangi penise ait olduğunu belirlemek için aynı köpeğin tüm pençelerini karşılaştırmak istiyorum (ön / arka, sol / sağ). Yalnızca işlevleri kullanmaya devam edersem bu bir karışıklık haline gelir.

Şimdi , verilerimi (bir köpeğin sıkıştırılmış verilerine bağlantı) işleyebileceğim sınıfları nasıl oluşturacağım konusunda tavsiye istiyorum .


4
Ayrıca bir veritabanı kullanmayı da düşünebilirsiniz (sqlite: docs.python.org/library/sqlite3.html gibi ). Büyük veri dosyalarınızı okuyan ve veritabanı tablolarındaki satırlara dönüştüren bir program yazabilirsiniz. Daha sonra ikinci aşama olarak, daha fazla analiz yapmak için verileri veritabanından çeken programlar yazabilirsiniz.
unutbu

Burada sorduğum gibi bir şey mi demek istediniz? Bunu yapmayı planlıyorum, ancak önce tüm verileri daha organize bir şekilde işleyebilmek istiyorum
Ivo Flipse

Yanıtlar:


434

Bir sınıf nasıl tasarlanır.

  1. Kelimeleri yaz. Bunu yapmaya başladın. Bazı insanlar neden sorun yaşadıklarını merak etmezler.

  2. Kelime kümenizi bu nesnelerin ne yapacağına dair basit ifadelere genişletin. Yani, bunlar üzerinde yapacağınız çeşitli hesaplamaları yazın. 30 köpeklik kısa listeniz, 24 ölçüm, 4 kişi ve kişi başına birkaç "parametre" ilginçtir, ancak hikayenin sadece bir kısmıdır. "Her bir pençenin konumları" ve "aynı köpeğin tüm pençelerini, hangi kişinin hangi pençeye ait olduğunu belirlemek için nesne tasarımında bir sonraki adımdır.

  3. İsimlerin altını çizin. Ciddi anlamda. Bazı insanlar bunun değerini tartışıyor, ancak ilk kez OO geliştiricileri için yardımcı olduğunu düşünüyorum. İsimlerin altını çizin.

  4. İsimleri inceleyin. "Parametre" ve "ölçüm" gibi genel adların, sorun alan adınızdaki sorununuz için geçerli olan somut adlarla değiştirilmesi gerekir. Ayrıntılar sorunu açıklığa kavuşturmaya yardımcı olur. Jenerikler sadece ayrıntıları seçerler.

  5. Her isim için ("temas", "pençe", "köpek" vb.), Bu ismin niteliklerini ve nesnenin dahil olduğu eylemleri not edin. Bunu kestirme. Her özellik. Örneğin "Veri Kümesi 30 Köpek İçeriyor" önemlidir.

  6. Her özellik için, bunun tanımlanmış bir isimle veya bir dize veya kayan nokta veya indirgenemez bir şey gibi başka bir tür "ilkel" veya "atomik" veri ile ilişkisi olup olmadığını belirleyin.

  7. Her eylem veya işlem için hangi ismin sorumluluğa sahip olduğunu ve hangi isimlerin sadece katıldığını tanımlamanız gerekir. Bu bir "değişebilirlik" meselesidir. Bazı nesneler güncellenir, bazıları güncellenmez. Değişken nesneler mutasyonları için toplam sorumluluğa sahip olmalıdır.

  8. Bu noktada, isimleri sınıf tanımlarına dönüştürmeye başlayabilirsiniz. Bazı kolektif isimler listeler, sözlükler, tuples, setler veya adlandırılmış tuples'tir ve çok fazla iş yapmanız gerekmez. Diğer sınıflar, ya karmaşık türetilmiş veriler ya da yapılan bazı güncelleme / mutasyonlar nedeniyle daha karmaşıktır.

Unittest kullanarak her sınıfı ayrı ayrı test etmeyi unutmayın.

Ayrıca, sınıfların değiştirilebilir olması gerektiğini söyleyen hiçbir yasa yoktur. Örneğin, sizin durumunuzda neredeyse hiç değiştirilebilir veri yok. Sahip olduğunuz, kaynak veri kümesinden dönüştürme işlevleri tarafından oluşturulan verilerdir.


24

Aşağıdaki tavsiyeler (@ S.Lott'un tavsiyesine benzer), Başlangıç ​​Python kitabından alınmıştır : Acemiden Profesyonel'e

  1. Sorununuzun bir açıklamasını yazın (sorun ne yapmalı?). Tüm isimlerin, fiillerin ve sıfatların altını çizin.

  2. İsimleri gözden geçirin, potansiyel sınıflar arayın.

  3. Potansiyel yöntemleri aramak için fiilleri gözden geçirin.

  4. Sıfatları gözden geçirin, potansiyel nitelikleri arayın

  5. Sınıflarınıza yöntem ve öznitelikler ayırın

Sınıfı daraltmak için kitap ayrıca şunları da yapabilmemizi önermektedir:

  1. Bir dizi kullanım örneğini (programınızın nasıl kullanılabileceğine dair senaryolar ) yazın (veya hayal edin) . Tüm fonksiyonel olarak örtmeye çalışın.

  2. İhtiyacımız olan her şeyin ele alındığından emin olarak her kullanım durumunu adım adım düşünün.


Yazmamız gereken cümlelerden bazı örneklere sahip olmak iyi olurdu.
endolith

14

TDD yaklaşımını seviyorum ... Bu nedenle, davranışın olmasını istediğiniz şeylere yönelik testler yazarak başlayın. Ve geçen kodu yazın. Bu noktada, tasarım hakkında çok fazla endişelenmeyin, sadece bir test paketi ve geçen bir yazılım alın. Tek bir büyük çirkin sınıfla, karmaşık yöntemlerle karşılaşırsanız endişelenmeyin.

Bazen, bu ilk işlem sırasında, test edilmesi zor ve ayrıştırılması gereken, sadece test edilebilirlik için bir davranış bulacaksınız. Bu, ayrı bir sınıfın gerekli olduğuna dair bir ipucu olabilir.

Sonra eğlenceli kısım ... yeniden düzenleme. Çalıştıktan sonra karmaşık parçaları görebilirsiniz. Genellikle yeni bir sınıf öneren küçük davranış cepleri belirginleşir, ancak değilse, sadece kodu basitleştirmenin yollarını arayın. Hizmet nesnelerini ve değer nesnelerini ayıklayın. Yöntemlerinizi basitleştirin.

Git'i düzgün bir şekilde kullanıyorsanız (git kullanıyorsunuz, değil mi?), Yeniden düzenleme sırasında belirli bir ayrışmayı çok hızlı bir şekilde deneyebilir ve daha sonra terk edip işleri basitleştirmezse geri dönebilirsiniz.

Önce test edilmiş çalışma kodunu yazarak, tasarım ilk yaklaşımı ile kolayca elde edemediğiniz sorun etki alanı hakkında samimi bir fikir edinmelisiniz. Testler ve kod yazmak sizi "nereden başlayacağım" felçinin ötesine iter.


1
Bu cevaba da katılıyorum, ancak sorunu çözmek ve olası sınıfları tanımlamak (yani "yeterli" yazılım mimarisi yapmak), sorun birkaç ekip üyesi tarafından paralel olarak çalışacaksa çok yararlı olabilir.
Ben Smith

3

OO tasarımının tüm fikri, kod haritanızı probleminize dönüştürmektir, bu nedenle, örneğin, bir köpeğin ilk adımını istediğinizde, aşağıdaki gibi bir şey yaparsınız:

dog.footstep(0)

Şimdi, sizin durumunuz için ham veri dosyanızı okumalı ve adım adım konumlarını hesaplamanız gerekebilir. Tüm bunlar footstep () işlevinde gizlenebilir, böylece yalnızca bir kez gerçekleşir. Gibi bir şey:

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

[Bu şimdi bir tür önbellek kalıbı. İlk kez ayak sesi verilerini okuduğunda, bunu daha sonra self._footsteps'ten alır.]

Ancak evet, OO tasarımını doğru yapmak zor olabilir. Verilerinize yapmak istediğiniz şeyler hakkında daha fazla düşünün ve bu, hangi sınıflara hangi yöntemleri uygulamanız gerektiğini bildirir.


2

İsimlerinizi, fiillerinizi, sıfatlarınızı yazmak harika bir yaklaşımdır, ancak sınıf tasarımını hangi verilerin gizlenmesi gerektiği sorusunu sormayı düşünmeyi tercih ederim ?

Bir Querynesneniz ve bir Databasenesneniz olduğunu düşünün :

QueryNesne oluşturmanıza yardımcı ve bir sorgu saklar - mağaza, bir işlev yardımcı olabilir gibi aynı kolaylıkla bir tane oluşturun, burada anahtarıdır. Belki kalabilirsin Query().select('Country').from_table('User').where('Country == "Brazil"'). Sözdizimi tam olarak önemli değil - bu sizin işiniz! - anahtar, nesnenin bir şeyi gizlemenize yardımcı olmasıdır , bu durumda bir sorguyu saklamak ve çıktılamak için gerekli veriler. Nesnenin gücü onu kullanma sözdiziminden gelir (bu durumda bazı akıllı zincirleme) ve çalışmasını sağlamak için ne sakladığını bilmeye gerek yoktur. Doğru yapılırsa Querynesne birden fazla veritabanı için sorgu çıktısı verebilir. Dahili olarak belirli bir formatı depolardı, ancak çıktı alırken kolayca diğer formatlara dönüşebilir (Postgres, MySQL, MongoDB).

Şimdi Databasenesne üzerinde düşünelim . Bu ne saklıyor ve saklıyor? Açıkçası veritabanının tüm içeriğini depolayamaz, çünkü bu yüzden bir veritabanımız var! Peki amaç ne? Amaç, nesneyi kullanan insanlardan veritabanının nasıl çalıştığını gizlemektirDatabase . İyi sınıflar içsel durumu manipüle ederken akıl yürütmeyi basitleştirecektir. Bu Databasenesne için , ağ çağrılarının nasıl çalıştığını gizleyebilir veya toplu sorgular veya güncellemeler ya da bir önbellek katmanı sağlayabilirsiniz.

Sorun, bu Databasenesnenin BÜYÜK olmasıdır. Bir veritabanına nasıl erişileceğini temsil eder, bu yüzden kapakların altında her şeyi ve her şeyi yapabilir. Açıkça ağ, önbellekleme ve yığınlama sisteminize bağlı olarak başa çıkmak oldukça zordur, bu nedenle onları gizlemek çok yardımcı olacaktır. Ancak, birçok kişinin not edeceği gibi, bir veritabanı inanılmaz derecede karmaşıktır ve elde ettiğiniz ham DB çağrılarından ne kadar uzak olursa, performansı ayarlamak ve işlerin nasıl çalıştığını anlamak daha zordur.

Bu, OOP'nin temel ödünleşmesidir. Doğru soyutlamayı seçerseniz kodlamayı kolaylaştırır (String, Array, Dictionary), çok büyük bir soyutlama seçerseniz (Veritabanı, EmailManager, NetworkingManager), nasıl çalıştığını veya ne yapılacağını gerçekten anlamak çok karmaşık hale gelebilir bekliyoruz. Amaç karmaşıklığı gizlemektir , ancak bazı karmaşıklık gereklidir. İyi bir kural, Managernesnelerden kaçınmaya başlamak ve bunun yerine benzer sınıflar oluşturmaktır structs- tek yaptıkları verileri tutmak, hayatınızı kolaylaştırmak için verileri oluşturmak / değiştirmek için bazı yardımcı yöntemlerle. Örneğin, EmailManagerbaşlangıçta sendEmailbir Emailnesne ile başlayan bir nesne alır . Bu basit bir başlangıç ​​noktasıdır ve kodun anlaşılması çok kolaydır.

Örneğinize göre, aradığınızı hesaplamak için hangi verilerin birlikte olması gerektiğini düşünün. Örneğin, bir hayvanın ne kadar yürüdüğünü bilmek isterseniz, AnimalStepve AnimalTrip(AnimalSteps koleksiyonu) dersleriniz olabilir. Artık her Yolculuğun tüm Adım verilerine sahip olduğuna göre, bu konuda bir şeyler bulabilmeli, belki AnimalTrip.calculateDistance()de mantıklı olmalı .


2

Bağlantılı kodu alındıktan sonra, size daha iyi durumda geliyor bana değil bu noktada Köpek sınıf tasarlama. Aksine, Pandalar ve veri çerçeveleri kullanmalısınız . Veri çerçevesi sütun içeren bir tablodur. Sen dataframe gibi sütunlar olurdu: dog_id, contact_part, contact_time, contact_location, vb Pandalar perde arkasında Numpy diziler kullanır ve sizin için birçok kolaylık yöntemleri vardır:

  • Örneğin ile bir köpek seçin: my_measurements['dog_id']=='Charly'
  • veriyi sakla: my_measurements.save('filename.pickle')
  • pandas.read_csv()Metin dosyalarını manuel olarak okumak yerine kullanmayı düşünün .
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.