ฉันจะออกแบบคลาสใน Python ได้อย่างไร


143

ฉันมีความช่วยเหลือที่ยอดเยี่ยมสำหรับคำถามก่อนหน้านี้ของฉันในการตรวจจับอุ้งเท้าและนิ้วเท้าภายในอุ้งเท้าแต่วิธีแก้ปัญหาทั้งหมดเหล่านี้ใช้ได้กับการวัดครั้งละครั้งเท่านั้น

ตอนนี้ฉันมีข้อมูลที่ประกอบด้วย:

  • ประมาณ 30 ตัว;
  • แต่ละคนมี 24 วัด (แบ่งออกเป็นหลายกลุ่มย่อย);
  • การวัดแต่ละครั้งมีอย่างน้อย 4 หน้าสัมผัส (หนึ่งอันสำหรับแต่ละอุ้งเท้า) และ
    • การติดต่อแต่ละครั้งจะถูกแบ่งออกเป็น 5 ส่วนและ
    • มีพารามิเตอร์หลายอย่างเช่นเวลาติดต่อสถานที่แรงทั้งหมดเป็นต้น

ข้อความแสดงแทน

เห็นได้ชัดว่าการรวมทุกอย่างไว้ในวัตถุขนาดใหญ่ชิ้นเดียวจะไม่ตัดมันดังนั้นฉันจึงคิดว่าฉันต้องใช้คลาสแทนฟังก์ชั่นการฆ่าในปัจจุบัน แต่ถึงแม้ว่าฉันจะอ่านบทการเรียนรู้ของงูหลามเกี่ยวกับชั้นเรียนฉันไม่สามารถนำไปใช้กับโค้ดของตัวเอง ( ลิงค์ GitHub )

ฉันรู้สึกเหมือนมันค่อนข้างแปลกที่จะประมวลผลข้อมูลทุกครั้งที่ฉันต้องการได้รับข้อมูลบางอย่าง เมื่อฉันรู้ตำแหน่งของอุ้งเท้าแต่ละตัวแล้วฉันก็ไม่มีเหตุผลที่จะคำนวณสิ่งนี้อีก นอกจากนี้ฉันต้องการเปรียบเทียบอุ้งเท้าทั้งหมดของสุนัขตัวเดียวกันเพื่อกำหนดว่าผู้ติดต่อรายใดที่เป็นของอุ้งเท้าหน้า (หน้า / หลังหลังซ้าย / ขวา) สิ่งนี้จะกลายเป็นความยุ่งเหยิงถ้าฉันยังคงใช้ฟังก์ชั่นเท่านั้น

ดังนั้นตอนนี้ฉันกำลังมองหาคำแนะนำเกี่ยวกับวิธีการสร้างคลาสที่จะให้ฉันประมวลผลข้อมูลของฉัน ( ลิงก์ไปยังข้อมูลซิปของสุนัขตัวหนึ่ง ) อย่างสมเหตุสมผล


4
คุณอาจต้องการพิจารณาใช้ฐานข้อมูล (เช่น sqlite: docs.python.org/library/sqlite3.html ) คุณสามารถเขียนโปรแกรมที่อ่านไฟล์ข้อมูลขนาดใหญ่ของคุณและแปลงเป็นแถวในตารางฐานข้อมูล จากนั้นเป็นขั้นตอนที่สองคุณสามารถเขียนโปรแกรมที่ดึงข้อมูลออกจากฐานข้อมูลเพื่อทำการวิเคราะห์เพิ่มเติม
unutbu

คุณหมายถึงสิ่งที่ฉันถามที่นี่ @ubutbu? ฉันวางแผนที่จะทำให้มันเป็นอย่างนั้น แต่ก่อนอื่นฉันต้องการที่จะสามารถประมวลผลข้อมูลทั้งหมดในรูปแบบที่เป็นระเบียบมากขึ้น
Ivo Flipse

คำตอบ:


434

วิธีการออกแบบชั้นเรียน

  1. เขียนคำ คุณเริ่มทำสิ่งนี้แล้ว บางคนไม่สงสัยว่าทำไมพวกเขาถึงมีปัญหา

  2. ขยายชุดคำของคุณไปเป็นข้อความธรรมดาเกี่ยวกับสิ่งที่วัตถุเหล่านี้จะทำ กล่าวคือเขียนการคำนวณต่าง ๆ ที่คุณจะทำกับสิ่งเหล่านี้ รายการสั้น ๆ ของคุณสำหรับสุนัข 30 ตัวการวัด 24 ตัวผู้ติดต่อ 4 คนและ "พารามิเตอร์" หลายตัวต่อการติดต่อเป็นสิ่งที่น่าสนใจ แต่เป็นเพียงส่วนหนึ่งของเรื่องราว "สถานที่ตั้งของแต่ละอุ้งเท้า" และ "เปรียบเทียบอุ้งเท้าทั้งหมดของสุนัขตัวเดียวกันเพื่อกำหนดรายชื่อผู้ติดต่อที่เป็นของอุ้งเท้า" เป็นขั้นตอนต่อไปในการออกแบบวัตถุ

  3. ขีดเส้นใต้คำนาม อย่างจริงจัง. บางคนถกเถียงถึงคุณค่าของสิ่งนี้ แต่ฉันพบว่าสำหรับนักพัฒนา OO เป็นครั้งแรกมันช่วยได้ ขีดเส้นใต้คำนาม

  4. ตรวจสอบคำนาม คำนามทั่วไปเช่น "พารามิเตอร์" และ "การวัด" จะต้องถูกแทนที่ด้วยคำนามเฉพาะที่เป็นรูปธรรมที่ใช้กับปัญหาของคุณในโดเมนปัญหาของคุณ ข้อมูลเฉพาะช่วยชี้แจงปัญหา ยาสามัญเพียงแค่อธิบายรายละเอียด

  5. สำหรับคำนามแต่ละคำ ("ผู้ติดต่อ", "อุ้งเท้า", "สุนัข" ฯลฯ ) ให้เขียนคุณลักษณะของคำนามนั้นและการกระทำที่วัตถุนั้นมีส่วนร่วม อย่าตัดสิ่งนี้ทิ้ง ทุกแอตทริบิวต์ ตัวอย่างเช่น "ชุดข้อมูลมี 30 Dogs" เป็นสิ่งสำคัญ

  6. สำหรับแต่ละแอ็ตทริบิวต์ระบุว่านี่เป็นความสัมพันธ์กับคำนามที่กำหนดหรือข้อมูล "ดั้งเดิม" หรือ "อะตอมมิก" อื่น ๆ เช่นสตริงหรือโฟลตหรือสิ่งที่ลดทอนไม่ได้

  7. สำหรับการกระทำหรือการปฏิบัติการแต่ละอย่างคุณต้องระบุว่าคำนามใดมีความรับผิดชอบและคำนามใดที่มีส่วนร่วม มันเป็นคำถามของ "ความผันแปร" วัตถุบางอย่างได้รับการปรับปรุงอื่น ๆ ทำไม่ได้ อ็อบเจกต์ที่ไม่แน่นอนต้องเป็นเจ้าของโดยรวมสำหรับการกลายพันธุ์

  8. ณ จุดนี้คุณสามารถเริ่มเปลี่ยนคำนามเป็นคำจำกัดความของชั้นเรียนได้ คำนามรวมบางคำคือรายการพจนานุกรม tuples ชุดหรือตั้งชื่อ tuples และคุณไม่จำเป็นต้องทำงานมากนัก คลาสอื่นมีความซับซ้อนมากกว่าเนื่องจากข้อมูลที่ได้มาที่ซับซ้อนหรือเนื่องมาจากการปรับปรุง / การกลายพันธุ์บางอย่างที่ดำเนินการ

อย่าลืมที่จะทดสอบแต่ละคลาสด้วยการแยกจากกันโดยใช้สิ่งที่เหมาะสม

นอกจากนี้ยังไม่มีกฎหมายที่ระบุว่าชั้นเรียนต้องไม่แน่นอน ตัวอย่างเช่นในกรณีของคุณคุณเกือบจะไม่มีข้อมูลที่ไม่แน่นอน สิ่งที่คุณได้รับคือข้อมูลที่สร้างขึ้นโดยฟังก์ชันการแปลงจากชุดข้อมูลต้นฉบับ


24

คำแนะนำต่อไปนี้ (คล้ายกับคำแนะนำของ @ S.Lott) มาจากหนังสือ Beginner Python: จาก Novice ถึง Professional

  1. เขียนคำอธิบายปัญหาของคุณ (ปัญหาควรทำอย่างไร) ขีดเส้นใต้คำนามคำกริยาและคำคุณศัพท์ทั้งหมด

  2. ผ่านคำนามมองหาชั้นเรียนที่มีศักยภาพ

  3. ผ่านคำกริยามองหาวิธีที่มีศักยภาพ

  4. ผ่านคำคุณศัพท์มองหาคุณลักษณะที่เป็นไปได้

  5. จัดสรรวิธีการและคุณสมบัติให้กับชั้นเรียนของคุณ

ในการปรับแต่งชั้นเรียนหนังสือยังแนะนำว่าเราสามารถทำสิ่งต่อไปนี้:

  1. จดบันทึกชุดการใช้งาน (หรือฝัน) - สถานการณ์ของการใช้โปรแกรมของคุณ พยายามครอบคลุมทุกหน้าที่

  2. คิดถึงทุกกรณีการใช้งานทีละขั้นตอนทำให้แน่ใจว่าครอบคลุมทุกสิ่งที่เราต้องการ


มันจะเป็นการดีถ้ามีตัวอย่างของประโยคที่เราควรจะเขียน
endolith

14

ฉันชอบวิธีการ TDD ... ดังนั้นเริ่มต้นด้วยการเขียนแบบทดสอบสำหรับสิ่งที่คุณต้องการให้พฤติกรรมเป็น และเขียนรหัสที่ผ่าน ถึงตอนนี้ไม่ต้องกังวลกับการออกแบบมากนักเพียงแค่มีชุดทดสอบและซอฟต์แวร์ที่ผ่าน ไม่ต้องกังวลถ้าคุณจบด้วยชั้นที่น่าเกลียดขนาดใหญ่เพียงครั้งเดียวด้วยวิธีการที่ซับซ้อน

บางครั้งในระหว่างกระบวนการเริ่มต้นนี้คุณจะพบพฤติกรรมที่ยากต่อการทดสอบและจำเป็นต้องสลายตัวเพียงเพื่อการทดสอบ นี่อาจเป็นคำใบ้ว่าคลาสที่แยกต่างหากนั้นได้รับการรับประกัน

จากนั้นส่วนที่สนุก ... การเปลี่ยนโฉม หลังจากที่คุณมีซอฟต์แวร์ที่ใช้งานได้คุณจะเห็นชิ้นส่วนที่ซับซ้อน บ่อยครั้งที่พฤติกรรมเล็ก ๆ น้อย ๆ จะปรากฏชัดเจนแนะนำคลาสใหม่ แต่ถ้าไม่ใช่เพียงมองหาวิธีการทำให้โค้ดง่ายขึ้น แยกวัตถุบริการและวัตถุค่า ลดความซับซ้อนของวิธีการของคุณ

หากคุณใช้คอมไพล์อย่างถูกต้อง (คุณใช้คอมไพล์ไม่ใช่คุณใช่ไหม) คุณสามารถทดลองกับการสลายตัวบางอย่างในระหว่างการรีแฟคเตอร์ใหม่ได้อย่างรวดเร็วแล้วละทิ้งมันและย้อนกลับหากไม่ทำให้สิ่งต่าง ๆ ง่ายขึ้น

โดยการเขียนรหัสการทำงานที่ผ่านการทดสอบก่อนอื่นคุณควรได้รับข้อมูลเชิงลึกอย่างใกล้ชิดในโดเมนปัญหาที่คุณไม่สามารถเข้าถึงได้ด้วยวิธีการออกแบบขั้นแรก การทดสอบการเขียนและรหัสผลักคุณผ่านที่ "ฉันจะเริ่มต้น" อัมพาต


1
ฉันก็เห็นด้วยกับคำตอบนี้แม้ว่าการแยกแยะปัญหาและการระบุคลาสที่เป็นไปได้ (เช่นการทำสถาปัตยกรรมซอฟต์แวร์ "เพียงพอ") จะมีประโยชน์มากหากปัญหาจะเกิดขึ้นพร้อมกันโดยสมาชิกในทีมหลายคน
เบ็นสมิ ธ

3

แนวคิดทั้งหมดของการออกแบบ OO คือการทำรหัสแผนที่ให้กับปัญหาของคุณดังนั้นเมื่อเช่นคุณต้องการก้าวเท้าแรกของสุนัขคุณต้องทำสิ่งต่อไปนี้

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]

[ตอนนี้เป็นรูปแบบการแคช ครั้งแรกที่มันไปและอ่านข้อมูลฝีเท้าในครั้งต่อมามันเพิ่งได้รับจากตัวเอง _ ฟุตเท้า]

แต่ใช่การออกแบบที่ถูกต้องของ OO อาจเป็นเรื่องยุ่งยาก คิดเกี่ยวกับสิ่งที่คุณต้องการทำกับข้อมูลของคุณมากขึ้นและจะแจ้งวิธีการที่คุณจะต้องนำไปใช้กับคลาสใด


2

การเขียนคำนามคำกริยาคำคุณศัพท์เป็นวิธีการที่ยอดเยี่ยม แต่ฉันชอบคิดถึงการออกแบบชั้นเรียนเพื่อถามคำถามว่าข้อมูลใดควรถูกซ่อนอยู่ ?

ลองนึกภาพคุณมีQueryวัตถุและDatabaseวัตถุ:

Queryวัตถุจะช่วยให้คุณสร้างและเก็บแบบสอบถาม - เก็บเป็นกุญแจสำคัญที่นี่เป็นฟังก์ชั่นสามารถช่วยให้คุณสร้างได้อย่างง่ายดายเพียง บางทีคุณอาจจะอยู่Query().select('Country').from_table('User').where('Country == "Brazil"')ต่อไป. มันไม่สำคัญว่าไวยากรณ์ - นั่นคืองานของคุณ! - ที่สำคัญคือวัตถุจะช่วยให้คุณซ่อนบางสิ่งบางอย่างในกรณีนี้ข้อมูลที่จำเป็นในการจัดเก็บและส่งออกแบบสอบถาม พลังของวัตถุมาจากไวยากรณ์ของการใช้มัน (ในกรณีนี้มีการผูกมัดที่ฉลาด) และไม่จำเป็นต้องรู้ว่ามันเก็บไว้เพื่อให้มันทำงาน หากทำถูกต้องQueryวัตถุสามารถส่งออกแบบสอบถามได้มากกว่าหนึ่งฐานข้อมูล ภายในจะเก็บรูปแบบเฉพาะ แต่สามารถแปลงเป็นรูปแบบอื่นได้อย่างง่ายดายเมื่อแสดงผล (Postgres, MySQL, MongoDB)

ทีนี้ลองคิดถึงDatabaseวัตถุ สิ่งนี้ซ่อนและจัดเก็บอะไร เห็นได้ชัดว่ามันไม่สามารถจัดเก็บเนื้อหาทั้งหมดของฐานข้อมูลเนื่องจากเป็นเหตุผลที่เรามีฐานข้อมูล! ดังนั้นประเด็นคืออะไร? เป้าหมายคือเพื่อซ่อนวิธีการทำงานของฐานข้อมูลจากผู้ที่ใช้Databaseวัตถุ คลาสที่ดีจะทำให้การใช้เหตุผลง่ายขึ้นเมื่อจัดการกับสถานะภายใน สำหรับDatabaseวัตถุนี้คุณสามารถซ่อนวิธีการทำงานของเครือข่ายการโทรหรือแบบสอบถามหรือการปรับปรุงแบบชุด

ปัญหาคือDatabaseวัตถุนี้มีขนาดใหญ่มาก มันแสดงให้เห็นถึงวิธีการเข้าถึงฐานข้อมูลดังนั้นภายใต้ครอบคลุมมันสามารถทำอะไรและทุกอย่าง การเชื่อมต่อเครือข่ายแคชและการแบทช์ค่อนข้างชัดเจนยากที่จะจัดการขึ้นอยู่กับระบบของคุณดังนั้นการซ่อนมันไว้จะมีประโยชน์มาก แต่ตามที่หลาย ๆ คนจะทราบว่าฐานข้อมูลมีความซับซ้อนอย่างบ้าคลั่งและยิ่งไปกว่านั้นจากฐานข้อมูลดิบที่คุณเรียกคุณจะได้รับสิ่งที่ยากกว่าคือการปรับแต่งเพื่อประสิทธิภาพและเข้าใจว่าสิ่งต่าง ๆ ทำงานอย่างไร

นี่คือการแลกเปลี่ยนขั้นพื้นฐานของ OOP หากคุณเลือกนามธรรมที่ถูกต้องมันจะทำให้การเขียนโค้ดง่ายขึ้น (String, Array, Dictionary) ถ้าคุณเลือกนามธรรมที่มีขนาดใหญ่เกินไป (ฐานข้อมูล EmailManager, NetworkingManager) มันอาจซับซ้อนเกินกว่าที่จะเข้าใจว่ามันทำงานอย่างไร คาดหวัง เป้าหมายคือการซ่อนความซับซ้อนแต่จำเป็นต้องมีความซับซ้อนบางอย่าง กฎง่ายๆคือเริ่มจากการหลีกเลี่ยงManagerวัตถุและสร้างคลาสที่เหมือนกันstructs- ทั้งหมดที่พวกเขาทำคือเก็บข้อมูลด้วยวิธีการช่วยเหลือในการสร้าง / จัดการข้อมูลเพื่อทำให้ชีวิตของคุณง่ายขึ้น ตัวอย่างเช่นในกรณีที่EmailManagerเริ่มต้นด้วยฟังก์ชั่นที่เรียกsendEmailว่าใช้Emailวัตถุ นี่เป็นจุดเริ่มต้นที่ง่ายและรหัสนั้นง่ายต่อการเข้าใจ

สำหรับตัวอย่างของคุณให้คิดถึงว่าข้อมูลใดที่จะต้องอยู่ด้วยกันเพื่อคำนวณสิ่งที่คุณกำลังมองหา หากคุณต้องการทราบว่าสัตว์เดินไปได้ไกลแค่ไหนคุณสามารถมีคลาสAnimalStepและAnimalTrip(คอลเลกชันของ AnimalSteps) ได้ ตอนนี้การเดินทางแต่ละครั้งมีข้อมูลขั้นตอนทั้งหมดแล้วก็ควรจะสามารถหาข้อมูลเกี่ยวกับมันAnimalTrip.calculateDistance()ได้


2

หลังจากอ่านโค้ดที่เชื่อมโยงของคุณได้แล้วดูเหมือนว่าคุณจะดีกว่าถ้าไม่ออกแบบคลาส Dog ในตอนนี้ คุณควรใช้Pandasและdataframesแทน dataframe เป็นตารางที่มีคอลัมน์ คุณ dataframe จะมีคอลัมน์เช่น: dog_id, contact_part, contact_time, contact_locationฯลฯ นุ่นใช้อาร์เรย์ Numpy เบื้องหลังและจะมีวิธีการอำนวยความสะดวกมากมายสำหรับคุณ:

  • เลือกสุนัขโดย: my_measurements['dog_id']=='Charly'
  • บันทึกข้อมูล: my_measurements.save('filename.pickle')
  • พิจารณาใช้pandas.read_csv()แทนการอ่านไฟล์ข้อความด้วยตนเอง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.