Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

การสืบทอดใน Python: คู่มือฉบับสมบูรณ์แบบทีละขั้นตอน

การสืบทอด (Inheritance) ใน Python คือเมื่อคลาสย่อย (subclass) ใช้โค้ดจากคลาสอื่น การสืบทอดเป็นคุณสมบัติสำคัญในภาษาเชิงวัตถุอย่าง Python ที่ช่วยให้การเขียนโค้ดง่ายขึ้นและมีประสิทธิภาพมากขึ้น


Python เป็นภาษาเชิงวัตถุ (object-oriented language) ซึ่งรองรับการสร้างบล็อกโค้ดที่นำกลับมาใช้ใหม่ได้ เพื่อช่วยทำให้โค้ดมีประสิทธิภาพมากขึ้น วิธีหนึ่งที่ทำได้คือการสืบทอด ซึ่งคลาสย่อยหนึ่งสามารถใช้โค้ดจากอีกคลาสหนึ่งได้

ตัวอย่างเช่น คุณอาจมีคลาสที่จัดเก็บ "บัญชีธนาคาร" และต้องการสร้างคลาสย่อยที่จัดเก็บ "บัญชีทองคำ" ซึ่งสามารถอ้างอิงแอตทริบิวต์จากคลาส "บัญชีธนาคาร" ที่มีอยู่แล้ว

ในบทช่วยสอนนี้ เราจะแยกวิเคราะห์พื้นฐานของการสืบทอดใน Python เราจะพูดถึงวิธีการทำงานของคลาสพาเรนต์ (parent) และคลาสชายลด์ (child) รวมถึงวิธีการลบล้าง (override) แอตทริบิวต์และเมธอดเมื่อทำงานกับออบเจ็กต์คลาส

การสืบทอดใน Python

การสืบทอดเป็นคำที่ใช้เรียกคลาส Python ภายในอีกคลาสหนึ่ง คลาสที่เรียกว่า subclasses หรือ child classes สามารถสืบทอดค่าจาก parent classes ได้ คล้ายกับเด็กที่สืบทอดลักษณะจากพ่อแม่ในโลกจริง

การสืบทอดมีประโยชน์เพราะช่วยให้เราสร้าง subclasses ที่มีประเภทค่าเหมือนกับคลาสพาเรนต์ โดยไม่ต้องประกาศประเภทเหล่านั้นซ้ำหลายครั้ง

คลาสพาเรนต์ (Parent Classes)

คลาสพาเรนต์ (ซึ่งเรียกอีกอย่างว่า base classes) สร้างโครงสร้างหลักที่คลาสย่อยสามารถเข้าถึงได้ เราสามารถสร้างคลาสย่อยหลายๆ คลาสได้โดยไม่ต้องประกาศชุดค่าทั่วไปเดิมซ้ำอีก ซึ่งเมธอดของคลาสย่อยมาจากคลาสพาเรนต์

มาใช้ตัวอย่างเพื่อแสดงให้เห็นว่าทำงานอย่างไร สมมติเรามีโปรแกรมที่ทำงานกับสองประเภทของบัญชีธนาคาร: พาเรนต์และชายลด์

ทั้งสองบัญชีนี้เป็นบัญชีธนาคาร จึงจะมีค่าที่คล้ายกัน แต่แต่ละบัญชีอาจต้องการข้อมูลเฉพาะตัวเอง ตัวอย่างเช่น บัญชีของพ่อแม่อาจรองรับการเบิกเกิน (overdraft) ในขณะที่บัญชีของลูกอาจทำงานกับยอดคงเหลือเท่านั้น

ดังนั้น หากเราต้องการสร้างโปรแกรมด้วยค่าเหล่านี้ เราจะกำหนดคลาสสำหรับบัญชีธนาคาร (คลาสพาเรนต์) ก่อน แล้วจึงสร้างคลาสชายลด์สำหรับสองประเภทของบัญชีธนาคารที่เราต้องการรองรับ

นี่คือตัวอย่างการประกาศคลาสพาเรนต์สำหรับบัญชีธนาคาร:

class BankAccount: def __init__(self, forename, surname, balance): self.forename = forename self.surname = surname self.balance = balance

เราสร้างคลาส BankAccount ซึ่งจัดเก็บสามค่า: forename, surname, และ balance เรายังสามารถเพิ่มเมธอดเข้าไปในคลาสได้เช่นกัน:

class BankAccount: def __init__(self, forename, surname, balance): self.forename = forename self.surname = surname self.balance = balance def getDetails(self): print("Forename: ", self.forename) print("Surname: ", self.surname) def getBalance(self): print("Balance: $ ", self.balance)

ตอนนี้คลาสของเราจัดเก็บสามค่า รวมถึงเมธอดใหม่สองเมธอด: details และ balance เมธอด details ใช้หาชื่อและนามสกุลของเจ้าของบัญชี ส่วนเมธอด balance จะคืนค่ายอดคงเหลือของเจ้าของบัญชี

ทุกเมธอดและตัวแปรที่เราประกาศในคลาส BankAccount จะสามารถเข้าถึงได้ในคลาสชายลด์

เพื่อแสดงการทำงานของคลาส เรามาสร้างบัญชีธนาคารตัวอย่างกัน เราสามารถทำได้ด้วยโค้ดดังนี้:

account = BankAccount("John", "Appleseed", 100)

โค้ดนี้จะสร้างบัญชีสำหรับ John Appleseed ซึ่งมียอด $100 ตอนนี้ที่เรามีบัญชีพร้อมแล้ว เราสามารถเริ่มอ้างอิงมันได้เหมือนกับคลาสทั่วไป นี่คือตัวอย่างการเรียกใช้เมธอด getBalance() ภายในบัญชีธนาคารเพื่อดูว่ามีเงินเท่าไหร่:

print(account.getBalance())

โปรแกรมของเราคืนค่าดังนี้:

Balance: $ 100

คลาสชายลด์ (Child Classes)

ในตัวอย่างข้างต้น เราประกาศคลาสพาเรนต์ที่จัดเก็บบัญชีธนาคาร แต่ถ้าเราต้องการจัดเก็บข้อมูลเกี่ยวกับประเภทบัญชีเฉพาะ เช่น บัญชีเด็กอายุต่ำกว่า 18 ปี? นี่คือที่ที่คลาสชายลด์เข้ามามีบทบาท

คลาสชายลด์ หรือ subclasses สืบทอดค่าจากคลาสพาเรนต์ หมายความว่าแต่ละคลาสชายลด์จะสามารถอ้างอิงเมธอดและตัวแปรที่เราประกาศในคลาสพาเรนต์ได้

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

เพื่อสร้างคลาสชายลด์ เราสามารถใช้โค้ดดังนี้:

class ChildBankAccount(BankAccount):

คลาส ChildBankAccount กลายเป็นชายลด์ของคลาส BankAccount เพราะ BankAccount ถูกปิดอยู่ในวงเล็บ หากเราต้องการให้คลาสชายลด์มีค่าเหมือนกับคลาสพาเรนต์ BankAccount ทุกประการ เราสามารถใช้คีย์เวิร์ด pass ได้เช่นนี้:

class ChildBankAccount(BankAccount): pass

แต่ตามที่พูดไปก่อนหน้านี้ บัญชีเด็กของเราจะต้องจัดเก็บค่าพิเศษ: ตัวแปรที่บอกว่าบัญชีของผู้ใช้ถูกจำกัด

เพื่อบรรลุเป้าหมายนี้ เราจะเพิ่มค่าใหม่ชื่อ restricted เข้าไปในคลาสชายล็ด เราสามารถทำได้ด้วยโค้ดดังนี้:

class ChildBankAccount(BankAccount): def __init__(self, forename, surname, balance, restricted=True): self.forename = forename self.surname = surname self.balance = balance self.restricted = restricted def isRestricted(self): print("This account is restricted as the user is under 18.")

ตอนนี้มาแยกวิเคราะห์โค้ดของเรา

ในโค้ดข้างต้น เราระบุว่าเราต้องการให้ ChildBankAccounts จัดเก็บสี่ค่า: forename, surname, balance, และ restricted

สามค่าแรก—forename, surname, และ balance—เหมือนกับค่าของคลาสพาเรนต์ แต่ restricted เป็นค่าใหม่และเป็นของคลาสชายลด์อย่างเดียว โดยค่าเริ่มต้นของ restricted ถูกตั้งเป็น True

ดังนั้น หากเราสร้าง BankAccount มาตรฐาน (ไม่ใช่ ChildBankAccount) เราจะไม่สามารถเข้าถึงค่า restricted ได้ ทำนองเดียวกัน เราจะไม่สามารถเข้าถึงเมธอด isRestricted ได้หากไม่ได้สร้าง ChildBankAccount

นี่คือตัวอย่างการสร้าง ChildBankAccount และใช้เมธอด getBalance() และ isResticted() เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับคลาสของเรา:

child_account = ChildBankAccount("Bill", "Appleseed", 100) print(child_account.getBalance()) print(child_account.isRestricted())

โค้ดของเราคืนค่าดังนี้:

Balance: $ 100 This account is restricted as the user is under 18.

ตามที่เห็น โปรแกรมของเราก่อนอื่นกำหนด ChildBankAccount ใหม่สำหรับ Bill Appleseed ซึ่งบัญชีมี $100 จากนั้น โปรแกรมของเราทำการ execute เมธอด getBalance() ในคลาสของเรา ซึ่งคืนค่ายอดคงเหลือของผู้ใช้ getBalance() ถูกประกาศในคลาสพาเรนต์ BankAccount แต่สามารถเข้าถึงได้ใน ChildBankAccount ผ่าน การสืบทอด (inheritance)

โปรแกรมของเรายัง execute เมธอด isRestricted() ซึ่งระบุว่าบัญชีของเด็กถูกจำกัดเพราะอายุต่ำกว่า 18 ปี คลาสนี้ถูกประกาศใน ChildBankAccount ซึ่งหมายความว่ามันไม่สามารถเข้าถึงได้ในคลาสพาเรนต์ BankAccount หากเราต้องการสร้าง BankAccount ที่ถูกจำกัด เราจะต้องเปลี่ยนแปลงคลาสพาเรนต์

การลบล้าง (Redefining) เมธอดของพาเรนต์

ในตัวอย่างข้างต้น เราประกาศคลาสพาเรนต์ชื่อ BankAccount ซึ่งจัดเก็บข้อมูลสำหรับบัญชีธนาคาร เรายังสร้างคลาสชายลด์ชื่อ ChildBankAccount ซึ่งจัดเก็บข้อมูลสำหรับเจ้าของบัญชีอายุต่ำกว่า 18 ปี คลาสนี้สืบทอดเมธอดจากคลาส BankAccount และสร้างเมธอด isRestricted() เพิ่มเติม

แต่ถ้าเราต้องการแก้ไขเมธอดของคลาสพาเรนต์ที่มีอยู่แล้ว? สมมติเราต้องการให้ยอดคงเหลือของ BankAccount คืนค่าข้อความพร้อมยอดคงเหลือและข้อความว่า You are eligible for an overdraft แต่ไม่ต้องการให้ข้อความนี้ปรากฏสำหรับเจ้าของ ChildBankAccount

เพื่อทำเช่นนี้ เราต้อง override เมธอดพาเรนต์ของเรา

นี่คือโค้ดสำหรับเมธอดพาเรนต์ที่แก้ไขแล้ว BankAccount ซึ่งตอนนี้พิมพ์ข้อความให้เจ้าบัญชีทราบว่าพวกเขามีสิทธิ์เบิกเกินเมื่อตรวจสอบยอดคงเหลือ:

class BankAccount: def __init__(self, forename, surname, balance): self.forename = forename self.surname = surname self.balance = balance def getDetails(self): print("Forename: ", self.forename) print("Surname: ", self.surname) def getBalance(self): print("Balance: $ ", self.balance) print("You are eligible for an overdraft.")

ตอนนี้ หากเราสร้าง BankAccount ใหม่สำหรับ John Appleseed และพิมพ์ยอดคงเหลือของเขา เราจะเห็นข้อความระบุว่า You are eligible for an overdraft นี่คือตัวอย่างการประกาศบัญชีใหม่และตรวจสอบยอดคงเหลือ:

account = BankAccount("John", "Appleseed", 100) print(account.getBalance())

โค้ดของเราคืนค่าดังนี้:

Balance: $ 100 You are eligible for an overdraft.

แต่การเปลี่ยนแปลงนี้หมายความว่า ChildBankAccount ของเรา ซึ่งสืบทอดค่าจาก BankAccount จะเห็นข้อความนี้เช่นกัน นี่คือตัวอย่างการสร้าง ChildBankAccount และตรวจสอบยอดคงเหลือตอนที่คลาสพาเรนต์ BankAccount เปลี่ยนไปแล้ว:

child_account = ChildBankAccount("Bill", "Appleseed", 100) print(child_account.getBalance())

โค้ดของเราคืนค่า:

Balance: $ 100 You are eligible for an overdraft.

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

นี่คือโค้ดสำหรับ ChildBankAccount ของเราจากข้างต้น:

class ChildBankAccount(BankAccount): def __init__(self, forename, surname, balance, restricted=True): self.forename = forename self.surname = surname self.balance = balance self.restricted = restricted def isRestricted(self): print("This account is restricted as the user is under 18.")

เราต้องเปลี่ยนโค้ดนี้เพื่อเพิ่มฟังก์ชันใหม่: getBalance() ฟังก์ชันนี้จะทำงานเหมือนกับที่เราประกาศในคลาสพาเรนต์ แต่จะไม่รวมข้อความเกี่ยวกับการเบิกเกิน นี่คือโค้ดที่เราจะเพิ่มเพื่อประกาศฟังก์ชันใหม่ของเรา:

... def getBalance(self): print("Balance: $ ", self.balance)

ลองดึงยอดคงเหลือของลูกของเราตอนนี้:

child_account = ChildBankAccount("Bill", "Appleseed", 100) print(child_account.getBalance())

โค้ดของเราคืนค่าดังนี้:

Balance: $ 100

ในตัวอย่างข้างต้น เราได้ลบล้าง (overwritten) เมธอด getBalance() ในคลาสชายลด์ ChildBankAccount เมธอด getBalance() ใหม่ของ ChildBankAccount แสดงเฉพาะยอดคงเหลือของผู้ใช้ แต่ในคลาส BankAccount เมธอด getBalance() แสดงทั้งยอดคงเหลือของผู้ใช้และข้อความ You are eligible for an overdraft

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

สรุป

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

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