Data Warehouse

การรับทอด

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

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

การสร้างคลาสด้วยการรับทอด

จะขอยกตัวอย่างที่ต่อเนื่องจากบทที่แล้ว ในบทที่แล้วได้สร้างคลาส "ผู้กล้า" ขึ้น ซึ่งผู้กล้านี้ก็อาจสามารถแบ่งย่อยเป็นอาชีพต่างๆได้อีกเช่นนักรบหรือจอมเวทย์

ในบทนี้เราจะสร้างคลาสเหล่านี้ขึ้นมาใหม่โดยรับทอดจากคลาส "ผู้กล้า"

class  อาวุธ:  
def  __init__(self,ชื่อ,พลังโจมตีกายภาพ,พลังโจมตีเวทย์,ความทนทาน):  
self.ชื่อ = ชื่อ  
self.พลังโจมตีกายภาพ = พลังโจมตีกายภาพ  
self.พลังโจมตีเวทย์ = พลังโจมตีเวทย์  
self.ความทนทาน = ความทนทาน  
class  เสื้อผ้า:  
def  __init__(self,ชื่อ,พลังป้องกัน,ความทนทาน):  
self.ชื่อ = ชื่อ  
self.พลังป้องกัน = พลังป้องกัน  
self.ความทนทาน = ความทนทาน  
class  ผู้กล้า:  
def  __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,ความอดทน=4,hpสูงสุด=10):  
self.ชื่อ = ชื่อ  
self.เลเวล = เลเวล  
self.ความแข็งแรง = ความแข็งแรง  
self.ความอดทน = ความอดทน  
self.hpสูงสุด = hpสูงสุด  
self.hp = hpสูงสุด  
เสื้อผ้า = เสื้อผ้า('ชุดเก่าๆ',3,5)  
อาวุธ = อาวุธ('มีดสั้นเก่าๆ',3,0,5)  
เงินเดือน = 500  
def  พลังโจมตี(self):  
return  self.ความแข็งแรง +  self.อาวุธ.พลังโจมตีกายภาพ  
def  พลังป้องกัน(self):  
return  self.ความอดทน +  self.เสื้อผ้า.พลังป้องกัน  
def  ถูกโจมตี(self,ความเสียหาย):  
if(ความเสียหาย>self.พลังป้องกัน()):  
self.hp -= ความเสียหาย -  self.พลังป้องกัน()  
else:  
self.hp -= 1  
if(self.hp<0):  
self.hp = 0  
class  นักรบ(ผู้กล้า):  
เสื้อผ้า = เสื้อผ้า('ชุดนักรบฝึกหัด',5,5)  
อาวุธ = อาวุธ('ดาบฝึกหัด',5,0,5)  
class  จอมเวทย์(ผู้กล้า):  
def  __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังเวทย์=4,ความอดทน=4,hpสูงสุด=10,mpสูงสุด=10):  
self.ชื่อ = ชื่อ  
self.เลเวล = เลเวล  
self.พลังเวทย์ = พลังเวทย์  
self.ความแข็งแรง = ความแข็งแรง  
self.ความอดทน = ความอดทน  
self.hpสูงสุด = hpสูงสุด  
self.hp = hpสูงสุด  
self.mpสูงสุด = mpสูงสุด  
self.mp = mpสูงสุด  
เสื้อผ้า = เสื้อผ้า('ชุดจอมเวทย์ฝึกหัด',2,5)  
อาวุธ = อาวุธ('คฑาฝึกหัด',0,5,5)  
def  พลังโจมตีเวทย์(self):  
return  self.พลังเวทย์ +  self.อาวุธ.พลังโจมตีเวทย์

จะเห็นว่ามีการสร้างคลาสขึ้นใหม่ ๓ คลาส คือ “อาวุธ”, “เสื้อผ้า” และ “ผู้กล้า” ซึ่งใกล้เคียงกับบทที่แล้วแต่ต่างไปเล็กน้อย จากนั้นก็สร้างคลาสใหม่ขึ้นอีก ๒ คลาสโดยวิธีการรับทอด นั่นคือ “นักรบ” และ “จอมเวทย์” ซึ่งจะเห็นได้ว่ามีวงเล็บ “ผู้กล้า” อยู่ข้างหลัง ซึ่งแสดงถึงว่าทั้ง ๒ คลาสนี้รับทอดมาจาก “ผู้กล้า” นั่นเอง

ในคลาส “นักรบ” ไม่ได้มีการเพิ่มหรือแก้ไขเมธอด แต่มีแค่การใส่ค่าแอตทริบิวต์ในคลาส นั่นคือ “อาวุธ” และ “เสื้อผ้า” ใหม่ ซึ่งแอตทริบิวต์นี้จะไปทับของที่มีอยู่แล้วในคลาส “ผู้กล้า” ส่วนคลาส “จอมเวทย์” ก็มีการเปลี่ยน “อาวุธ” และ “เสื้อผ้า” เหมือนกัน และยังมีการนิยามเมธอด __init__ ขึ้นใหม่ ซึ่งเมธอดนี้จะไปทับ __init__ ทำให้ใช้อันใหม่นี้แทน ซึ่งข้อแตกต่างจะเห็นว่ามีการเพิ่ม “พลังเวทย์” และ “mpสูงสุด” ขึ้นมา

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

class  จอมเวทย์(ผู้กล้า):  
def  __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังเวทย์=4,ความอดทน=4,hpสูงสุด=10,mpสูงสุด=10):  
ผู้กล้า.__init__(self,ชื่อ,เลเวล,ความแข็งแรง,ความอดทน,hpสูงสุด)  
self.พลังเวทย์ = พลังเวทย์  
self.mpสูงสุด = mpสูงสุด  
self.mp = mpสูงสุด

จะเห็นว่ามีการเรียกใช้ __init__ ของ “ผู้กล้า” ภายในเมธอด __init__ ของ “จอมเวทย์” อีกที โดยพารามิเตอร์ก็ต้องใส่ให้สัมพันธ์กันด้วย พารามิเตอร์ที่ซ้ำกับคลาส “ผู้กล้า” จะถูกป้อนค่าให้กับแอตทริบิวต์ของออบเจ็กต์ด้วย __init__ ด้านในนี้ ส่วนพารามิเตอร์ที่เหลือซึ่งเพิ่มเข้ามาก็ค่อยมาป้อนให้กับแอตทริบิวต์ของออบเจ็กต์อีกที นอกนี้ยังมีวิธีเขียนอีกแบบ นั่นคือใช้ฟังก์ชัน super

class  จอมเวทย์(ผู้กล้า):  
def  __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังเวทย์=4,ความอดทน=4,hpสูงสุด=10,mpสูงสุด=10):  
super(จอมเวทย์,self).__init__(ชื่อ,เลเวล,ความแข็งแรง,ความอดทน,hpสูงสุด)  
self.พลังเวทย์ = พลังเวทย์  
self.mpสูงสุด = mpสูงสุด  
self.mp = mpสูงสุด

ฟังก์ชัน super ต้องการอาร์กิวเมนต์ ๒ ตัว ตัวแรกคือคลาส ตัวหลังคือออบเจ็กต์ ในที่นี้ออบเจ็กต์ถูกแทนด้วย self ผลที่ได้คือเป็นการที่ออบเจ็กต์ของคลาสนี้เรียกเมธอดของซุเปอร์คลาสของคลาส มัน ในที่นี้คืออบเจ็กต์ของคลาส “จอมเวทย์” เรียกใช้เมธอดของคลาส “ผู้กล้า” ซึ่งเป็นซูเปอร์คลาส สังเกตได้ว่าพอใช้ super แล้ว ในอาร์กิวเมนต์ของ init ก็ไม่ต้องมี self แล้ว อย่างไรก็ตามกรณีที่ฟังก์ชัน super นี้ถูกใช้ในโครงสร้างคลาสจะสามารถละอาร์กิวเมนต์ได้ ดังนั้นจึงเขียนแค่นี้ได้

class  จอมเวทย์(ผู้กล้า):  
def  __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังเวทย์=4,ความอดทน=4,hpสูงสุด=10,mpสูงสุด=10):  
super().__init__(ชื่อ,เลเวล,ความแข็งแรง,ความอดทน,hpสูงสุด)  
self.พลังเวทย์ = พลังเวทย์  
self.mpสูงสุด = mpสูงสุด  
self.mp = mpสูงสุด

***การละแบบนี้ทำได้เฉพาะในไพธอน 3 ส่วนในไพธอน 2 ไม่สามารถละอาร์กิวเมนต์แบบนี้ได้ ต้องเขียนเต็มเท่านั้น รายละเอียด ลองสร้างออบเจ็กต์มาทดสอบการใช้กันดูได้เลย

ผู้เล่นA = จอมเวทย์('มานะ',1,5,8,4,12,11)  
ผู้เล่นB = นักรบ('พากเพียร',1,7,6,14)  
print(ผู้เล่นA.เสื้อผ้า.ชื่อ)  # ได้ ชุดจอมเวทย์ฝึกหัด  
print(ผู้เล่นB.เสื้อผ้า.ชื่อ)  # ได้ ชุดนักรบฝึกหัด  
print(ผู้เล่นA.hp)  # ได้ 12  
ผู้เล่นA.ถูกโจมตี(10)  
print(ผู้เล่นA.hp)  # ได้ 8

การรับทอดหลายต่อ

การรับทอดสามารถทำได้หลายต่อเป็นทอดๆกี่ครั้งก็ได้ เช่นนักรบก็อาจแบ่งเป็นนักดาบและนักธนู จอมเวทย์ก็อาจแบ่งเป็นจอมเวทมนตร์ขาวและจอมเวทมนตร์ดำ เป็นต้น

class  นักดาบ(นักรบ):  
0  
class  นักธนู(นักรบ):  
อาวุธ = อาวุธ('ธนูฝึกหัด',6,0,5)  
class  จอมเวทมนตร์ดำ(จอมเวทย์):  
0  
class  จอมเวทมนตร์ขาว(จอมเวทย์):  
0

ในที่นี้ขอละการเขียนรายละเอียดของคลาสใหม่นี้เพิ่มเติม แต่หลักการก็คล้ายๆเดิม คือ “นักดาบ” และ “นักธนู” จะรับทอดเมธอดและแอตทริบิวต์ของ “นักรบ” ซึ่งรับทอดแอตทริบิวต์ของ “ผู้กล้า” มาอีกต่อ ซับคลาสของซับคลาสของคลาสหนึ่งก็ถือเป็นซับคลาสของคลาสนั้นด้วย ดังนั้น “นักดาบ” ถือเป็นซับคลาสของ “ผู้กล้า” ไปด้วย และออบเจ็กต์ของคลาส “นักดาบ” ก็จะเป็นอินสแตนซ์ของคลาส “นักรบ” แล้วก็เป็นอินสแตนซ์ของคลาส “ผู้กล้า” ไปด้วย ความจริงแล้วคลาสที่เราสร้างขึ้นมาจากเริ่มต้นโดยไม่ได้ใส่วงเล็บไว้ข้างหลัง นั้นก็ถือเป็นการรับทอดคลาสที่ชื่อว่า “object” อยู่แล้วในตัว เพียงแต่สามารถละได้เท่านั้น โดยทั่วไปจึงไม่ต้องเขียนวงเล็บ แต่ถ้าจะเขียนก็จะเห็น

class  ผู้กล้า(object):

ซึ่งก็มีค่าเหมือนเขียน

class  ผู้กล้า:

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

object > ผู้กล้า > นักรบ > นักดาบ  

การรับทอดจากหลายคลาส

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

class  นักรบเวทย์(นักรบ,จอมเวทย์):  
0

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

ผู้เล่นD = นักรบเวทย์('หรรษา')  
print(นักรบเวทย์.เสื้อผ้า.ชื่อ)  # ได้ ชุดนักรบฝึกหัด  
print(นักรบเวทย์.อาวุธ.ชื่อ)  # ได้ ดาบฝึกหัด  
print(ผู้เล่นD.mpสูงสุด)  # ได้ 10

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

เมธอดหรือแอตทริบิวต์ที่ชื่อขึ้นต้นด้วย __

โดยทั่วไปแล้วเมธอดและแอตทริบิวต์จะตั้งชื่อเป็นอะไรก็ได้ หลักการตั้งชื่อก็เหมือนตัวแปรทั่วไป แต่ว่าจะมีกรณีพิเศษเกิดขึ้นหากตั้งชื่อโดยมีขีดล่างสองอันนำหน้า
เมธอดหรือแอตทริบิวต์ที่ชื่อขึ้นต้นด้วย __ จะไม่สามารถเข้าถึงได้โดยตรงจากภายนอกคลาส ยกตัวอย่าง ประกาศคลาส "ผู้กล้า" ใหม่โดยเปลี่ยนแค่ให้แอตทริบิวต์ “เงินเดือน” มี __ นำหน้า

class  ผู้กล้า:  
def  __init__(self,ชื่อ):  
self.ชื่อ = ชื่อ  
__เงินเดือน = 500

จากนั้นลองสร้างออบเจ็กต์แล้วเข้าถึงค่า

ผู้เล่นJ = ผู้กล้า('เจ')  
print(ผู้เล่นJ.__เงินเดือน)

จะขึ้นว่า

AttributeError: 'ผู้กล้า' object has no attribute '__เงินเดือน'

หรือแม้แต่เข้าถึงผ่านคลาสก็ได้ผลแบบเดียวกัน คือจะขึ้นว่าไม่มีแอตทริบิวต์นี้

print(ผู้กล้า.__เงินเดือน)

โดยปกติแล้วเป็นธรรมเนียมปฏิบัติที่จะตั้งชื่อแอตทริบิวต์หรือเมธอดที่ไม่ต้องการให้มีการเข้าถึงโดยตรงจากภายนอกโดยขึ้นต้นด้วยขีดล่าง แต่การใส่ขีดล่างเพียงขีดเดียวไม่มีผลอะไรในทางปฏิบัติ จึงเป็นเพียงแค่ธรรมเนียมปฏิบัติ* ส่วนการใส่ขีดล่างสองขีดจึงจะมีผลจริงๆ (*ความจริงแล้วมีผลอยู่เล็กน้อย แต่ไม่ได้สำคัญดังนั้นจะยังไม่พูดถึงในตอนนี้) แต่การเข้าถึงโดยใช้จากภายในยังสามารถทำได้ตามปกติ เช่นลองสร้างเมธอดที่ใช้ค่า "__เงินเดือน" ขึ้นมา

class  ผู้กล้า:  
def  __init__(self,ชื่อ):  
self.ชื่อ = ชื่อ  
__เงินเดือน = 500  
def  แสดงเงินเดือน(self):  
return  self.__เงินเดือน  
ผู้เล่นK = ผู้กล้า('เค')  
print(ผู้เล่นK.แสดงเงินเดือน())  # ได้ 500

จะเห็นว่าจากภายนอกเข้าถึงโดยตรงไม่ได้แต่สามารถเห็นผลของค่านั้นได้โดยอ้อมเช่นผ่านเมธอด ในที่นี้ใช้เมธอด “แสดงเงินเดือน” อย่างไรก็ตามจริงๆแล้วไม่ใช่ว่าจะเข้าถึงโดยตรงไม่ได้ หากต้องการก็สามารถทำได้เช่นกัน แต่ชื่อจะถูกเปลี่ยนโดยต้องขึ้นต้นด้วยขีดล่างหนึ่งขีดตามด้วยชื่อคลาส ในที่นี้จะเป็น _ผู้กล้า__เงินเดือน ดังนั้นลอง

print(ผู้เล่นK._ผู้กล้า__เงินเดือน)  # ได้ 500

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

class  ผู้กล้า:  
def  __init__(self,ชื่อ):  
self.ชื่อ = ชื่อ  
__เงินเดือน = 500  
def  แสดงเงินเดือน(self):  
return  self.__เงินเดือน  
class  นักรบ(ผู้กล้า):  
__เงินเดือน = 1000  
ผู้เล่นL = นักรบ('แอล')  
print(ผู้เล่นL.แสดงเงินเดือน())  # ได้ 500

จะเห็นว่าเมธอด “แสดงเงินเดือน” ซึ่งมีไว้ใช้คืนค่า __เงินเดือน นั้นคืนค่าเงินเดือน 500 ซึ่งเป็นค่าของคลาส “ผู้กล้า” ทั้งๆที่โดยปกติแล้วหากรับทอดมาแล้วมีการนิยามแอตทริบิวต์ซ้ำมันก็น่าจะถูก เขียนทับ ดังนั้นจึงควรได้ค่า 1000 ซึ่งเป็นค่าใหม่ นั่นเป็นเพราะว่าแอตทริบิวต์หรือเมธอดที่ขึ้นต้นด้วย __ จะไม่ถูกเขียนทับคลาสที่รับทอดมา เพราะชื่อจริงๆของมันจะต้องมีชื่อคลาสนำหน้า ดังนั้นจึงแบ่งแยกกันชัดเจน

print(ผู้เล่นL._ผู้กล้า__เงินเดือน)  # ได้ 500  
print(ผู้เล่นL._นักรบ__เงินเดือน)  # ได้ 1000

เท่ากับว่ามีเงินเดือนของผู้กล้าแล้วก็ยังมีเงินเดือนของนักรบอีกแยกต่างหาก เมธอด “แสดงเงินเดือน” ถูกประกาศในคลาส “ผู้กล้า” ดังนั้นจึงใช้ "__เงินเดือน" ของคลาส “ผู้กล้า” แต่หากนิยามเมธอดใหม่ในคลาส "นักรบ" ค่า "__เงินเดือน" ในคลาส “นักรบ” จะถูกใช้แทน ลองเขียนใหม่โดยเปลี่ยนจาก "__เงินเดือน" เป็น “เงินเดือน” ซึ่งไม่มีขีดล่างสองขีดนำหน้า หรือจะมีขีดเดียว หรือจะเป็นชื่ออื่นก็ได้ จะพบว่าผลลัพธ์ที่ได้นั้นจะต่างกันออกไป โดยจะได้ค่า 1000 แทนที่จะเป็น 500

class  ผู้กล้า:  
def  __init__(self,ชื่อ):  
self.ชื่อ = ชื่อ  
เงินเดือน = 500  
def  แสดงเงินเดือน(self):  
return  self.เงินเดือน  
class  นักรบ(ผู้กล้า):  
เงินเดือน = 1000  
ผู้เล่นM = นักรบ('เอ็ม')  
print(ผู้เล่นM.แสดงเงินเดือน())  # ได้ 1000  
print(ผู้เล่นM.เงินเดือน)  # ได้ 1000

ตัวอย่างข้างต้นเป็นกรณีของแอตทริบิวต์ สำหรับเมธอดเองก็สามารถทำได้ในลักษณะเดียวกัน

class  ผู้กล้า:  
def  __init__(self,ชื่อ):  
self.ชื่อ = ชื่อ  
def  __แสดงเงินเดือน(self):  
return  500  
class  นักรบ(ผู้กล้า):  
def  __แสดงเงินเดือน(self):  
return  1000  
ผู้เล่นN = นักรบ('เอ็น')  
print(ผู้เล่นN._ผู้กล้า__แสดงเงินเดือน())  # ได้ 500  
print(ผู้เล่นN._นักรบ__แสดงเงินเดือน())  # ได้ 1000

อนึ่ง หากชื่อเมธอดลงท้ายด้วย __ ต่อให้ขึ้นต้นด้วย __ ก็จะไม่มีคุณสมบัติดังที่ว่านี้ เช่นพวกเมธอดพิเศษอย่าง __init__ จะเข้าถึงได้ด้วยชื่อเมธอดตามปกติ

อ้างอิง

http://docs.python.jp/3/reference/datamodel.html
http://www.lifewithpython.com/2014/01/python-super-function.html
http://shin.hateblo.jp/entry/2013/11/01/211716
http://methane.hatenablog.jp/entry/20081227/1230400144
http://yut.hatenablog.com/entry/20110120/1295533994
http://qiita.com/icoxfog417/items/e8f97a6acad07903b5b0
http://www.geocities.jp/m_hiroi/light/abcruby12.html

Reference : https://phyblas.hinaboshi.com/tsuchinoko23