φυβλαςのβλογ
phyblasのブログ



ภาษา python เบื้องต้น บทที่ ๒๒: การสร้างคลาส
เขียนเมื่อ 2016/03/24 23:11
แก้ไขล่าสุด 2021/09/28 16:42
ภาษาไพธอนเป็นภาษาที่เน้นรูปแบบการเขียนในเชิงวัตถุ ข้อมูลชนิดต่างๆในภาษาไพธอนอยู่ในรูปของสิ่งที่เรียกว่า ออบเจ็กต์ (object) และออบเจ็กต์ก็มีรูปแบบชนิดต่างๆมากมายไม่จำกัด เพราะนอกเหนือจากออบเจ็กต์มาตรฐานแล้วเราสามารถสร้างออบเจ็กต์ชนิดใหม่ขึ้น มาได้เองตามที่ต้องการ

ในการเขียนโปรแกรมเชิงวัตถุ ออบเจ็กต์จะถูกแบ่งเป็นหมวดหมู่ที่เรียกว่า คลาส (class)



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

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


ภาพจาก SAO (ที่มาของภาพ)



ตัวละครที่ผู้เล่นใช้นั้นก็มักจะประกอบไปด้วยค่าสถานะต่างๆ เช่น พลังโจมตี, พลังป้องกัน, HP, ฯลฯ

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

การสร้างคลาสทำได้โดยใช้คำสั่ง class แล้วตามด้วยชื่อคลาสที่ต้องการตั้ง ตามด้วยโคลอน : จากนั้นก็ตามด้วยบรรทัดที่ร่นเข้าซึ่งในนั้นใส่เนื้อหาโครงสร้างต่างๆของคลาส
class ชื่อคลาส:
    เนื้อหาของคลาส

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

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

ในการสร้างคลาส คลาสที่สร้างขึ้นอาจมีความซับซ้อน ใส่แอตทริบิวต์และเมธอดไปมากมายอย่างละเอียด แต่ในขณะเดียวกันก็สามารถสร้างคลาสที่โล่งๆไม่มีอะไรเลยก็ได้

คือสร้างขึ้นมาได้โดยไม่ต้องกำหนดอะไรเลย เป็นคลาสเปล่าๆ ในตอนเริ่มต้นนี้ขอเริ่มต้นยกตัวอย่างจากการสร้างคลาสว่างๆ
class ผู้กล้า:
    0

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

เลข 0 ที่อยู่ในบรรทัดที่ร่นเข้ามานั้นในที่นี้ไม่ได้ทำหน้าที่อะไรแค่ใส่เอาไว้เฉยๆเพื่อไม่ให้โครงสร้างคลาสว่างเปล่า

***บางคนก็นิยมใช้ pass ซึ่งเป็นคำสั่งที่ไม่มีการทำงานแต่ใส่เอาไว้เฉยๆเพื่อกันไม่ให้โครงสร้าง ว่างเปล่า แต่ก็ไม่ได้มีความจำเป็นต้องใช้ดังนั้นในที่นี้จะใช้ 0 แทนเมื่อต้องการโครงสร้างที่ว่างเปล่า

เพราะโดยปกติแล้วเวลาสร้าง คลาสควรจะมีอะไรอยู่ภายในโครงสร้างคลาส (คือบริเวณบรรทัดที่ร่นอยู่หลังโคลอน :) แต่ถ้าไม่ต้องการใส่อะไรเลยก็อาจใส่อะไรสักอย่างที่ไม่แสดงผล

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

เท่านี้ก็จะได้ออบเจ็กต์ที่ชื่อว่า "ผู้เล่น1" ซึ่งเป็นออบเจ็กต์ในคลาส "ผู้กล้า"

ออบเจ็กต์ที่ได้จากการเรียกใช้คลาสในลักษณะนี้จะถูกเรียกว่าคลาสอินสแตนซ์ (class instance, หรือเรียก "อินสแตนซ์" เฉยๆ) ของคลาสนั้น ในกรณีนี้ก็คือ ผู้เล่น1 เป็นคลาสอินสแตนซ์ของคลาส ผู้กล้า

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

ก็ผู้กล้าอาจมีกี่คนก็ได้ คือจะสร้างผู้กล้าขึ้นมาใหม่กี่คนก็ได้ สมมุติว่ามีผู้เล่นใหม่เพิ่มเข้ามาอีกคนก็สร้างตัวละครขึ้นมาใหม่
ผู้เล่น2 = ผู้กล้า()

เท่านี้ก็ได้ผู้กล้ามาอีกคนแล้ว ทั้ง "ผู้เล่น1" และ "ผู้เล่น2" ต่างก็เป็น "ผู้กล้า" ทั้งคู่ ลองใช้ type เพื่อหาชนิดดูได้ ก็จะได้ชนิดเป็นคลาสนั้น
print(type(ผู้เล่น1)) # ได้ <class '__main__.ผู้กล้า'>

__main__. ที่เห็นอยู่ด้านหน้าชื่อคลาสที่เราตั้งนี้คือส่วนที่แสดงว่าคลาสนี้ถูกสร้าง ขึ้นภายในโปรแกรมหลัก ไม่ได้เป็นคลาสที่ถูกสร้างขึ้นจากในมอดูลไหนที่เราเรียกใช้เข้ามา

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

แต่ว่าแม้แต่คลาสที่ว่างเปล่าก็สามารถที่จะป้อนค่าแอตทริบิวต์ให้เมื่อไหร่ก็ได้
class ผู้กล้า:
    0
ผู้เล่น1 = ผู้กล้า()
ผู้เล่น2 = ผู้กล้า()
ผู้เล่น1.ชื่อ = 'เก่งกล้า'
ผู้เล่น1.พลังโจมตี = 5
ผู้เล่น1.พลังป้องกัน = 4
ผู้เล่น2.ชื่อ = 'ชำนาญ'
ผู้เล่น2.พลังโจมตี = 6
ผู้เล่น2.พลังป้องกัน = 3

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

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

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

เรื่องของการสร้างเมธอดจะอธิบายต่อไป ตอนนี้ขอพูดถึงแอตทริบิวต์ก่อน

แอตทริบิวต์ของออบเจ็กต์ที่สร้างขึ้นมานี้จะมีคุณสมบัติเหมือนกับตัวแปรทั่วไป สามารถใช้ print แล้วก็นำมาคำนวณได้ และสามารถแก้ค่าได้ตามที่ต้องการ
print(ผู้เล่น1.พลังโจมตี) # ได้ 5
ผู้เล่น1.พลังโจมตี = ผู้เล่น1.พลังโจมตี - 1
print(ผู้เล่น1.พลังโจมตี) # ได้ 4

และถ้าหาชนิดของแอตทริบิวต์ดูก็จะได้เป็นชนิดเหมืนกับข้อมูลทั่วๆไป เช่นในที่นี้เป็นจำนวนเต็ม
print(type(ผู้เล่น1.พลังโจมตี)) # ได้ <class 'int'>

พอพูดแบบนี้ก็อาจมีคนสงสัยว่า ถ้าอย่างนั้นแล้วจะอุตส่าห์สร้างเป็นออบเจ็กต์และแอตทริบิวต์ทำไม ต่างอะไรกับการสร้างตัวแปรใหม่ เช่น
พลังโจมตีของผู้เล่น1 = 5

อะไรทำนองนี้ก็จะได้ตัวแปรที่ทำหน้าแสดงค่าพลังโจมตีของผู้เล่น1 ได้ไม่ต่างกันเลย

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

หมายความว่าถ้าเราเห็นแอตทริบิวต์
ผู้เล่น1.พลังโจมตี = 5
ผู้เล่น1.พลังป้องกัน = 4

เราสามารถโยงความสัมพันธ์ได้ทันทีว่าทั้งสองค่านี้เป็นค่าที่เกี่ยวข้องหรือเป็นของ "ผู้เล่น1"

ในขณะที่หากเก็บในตัวแปรธรรมดาแยกกันเช่น
พลังโจมตีของผู้เล่น1 = 5
พลังป้องกันของผู้เล่น1 = 4

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

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



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

และจะเห็นว่าออบเจ็กต์แต่ละชนิดก็มีเมธอดต่างกันออกไป เช่นสายอักขระสามารถใช้เมธอด split() upper() จำนวนจริงสามารถใช้ is_integer() เป็นต้น

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

การสร้างเมธอดจะคล้ายกับการสร้างฟังก์ชัน นั่นคือใช้คำสั่ง def เพียงแต่ต่างกันแค่ว่า def จะต้องอยู่ภายในโครงสร้างของคลาส

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

ในที่นี้จะขอยกตัวอย่างต่อจากตัวอย่างที่แล้ว โดยจะสร้างคลาสเหมือนเดิมแต่ใส่เมธอดลงไปให้มีอะไรขึ้นมา หากคลาสไม่มีเมธอดอะไรเลยแอตทริบิวต์ภายในออบเจ็กต์ของคลาสนั้นก็ไม่ต่างจาก ตัวแปรธรรมดาที่ไม่ได้ถูกใช้
class ผู้กล้า:
    def ถูกโจมตี(self,ความเสียหาย):
        self.hp -= ความเสียหาย - self.พลังป้องกัน

เพียงเท่านี้เราก็ได้คลาส "ผู้กล้า" ซึ่งมีเมธอด "ถูกโจมตี" ซึ่งมีไว้สำหรับคำนวณหักลบค่า hp ของผู้กล้าเวลาที่ถูกโจมตีนั่นเอง

ก่อนที่จะอธิบายสิ่งที่เขียนลองมาดูก่อนว่าเมธอดนี้ทำงานยังไง โดยขอยกตัวอย่างการทำงานของเมธอดนี้ด้วยการสร้างผู้เล่นขึ้นมาคนหนึ่งแล้ว ป้อนค่า hp และพลังป้องกันให้ จากนั้นก็ลองใช้เมธอด "ถูกโจมตี"
ผู้เล่น3 = ผู้กล้า()
ผู้เล่น3.hp = 35
ผู้เล่น3.พลังป้องกัน = 30
ผู้เล่น3.ถูกโจมตี(40)
print(ผู้เล่น3.hp) # ได้ 25
ผู้เล่น3.ถูกโจมตี(45)
print(ผู้เล่น3.hp) # ได้ 10

จากตัวอย่างนี้จะเห็นว่า "ผู้เล่น3" เป็น "ผู้กล้า" คนหนึ่ง ซึ่งถูกป้อนค่าให้มี hp เป็น 35 และพลังป้องกันเป็น 30

จากนั้นก็สมมุติว่า "ผู้เล่น3" ไปสู้กับปีศาจแล้วโดนโจมตีได้รับความเสียหาย 40 จึงใช้เมธอด "ถูกโจมตี" โดยใส่อาร์กิวเมนต์เป็น 40

ค่า 40 นี้จะกลายเป็น "ความเสียหาย" ซึ่งจะถูกนำไปลบกับพลังป้องกัน กลายเป็น 40-30=10 จากนั้นค่านี้ก็จะนำไปหักลบ hp ดังนั้น hp จึงกลายเป็น 35-10=25 ซึ่งค่านี้ก็จะถูก print ออกมา

จากนั้น "ผู้เล่น3" ก็ถูกโจมตีอีกครั้งคราวนี้ 45 ก็ใช้เมธอด "ถูกโจมตีอีกครั้ง" แต่คราวนี้ hp จะลด 45-30=15 จึงเหลือ hp เป็น 25-15=10

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

โครงสร้างจะเห็นว่าคล้ายการนิยามฟังก์ชัน คือใช้ def ตามด้วยชื่อเมธอด แล้วก็วงเล็บซึ่งภายในวงเล็บมีพารามิเตอร์อยู่

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

โดยทั่วไปแล้วพารามิเตอร์ตัวแรกที่เอาไว้ใช้แทนตัวออบเจ็กต์เองนี้จะถูกเขียนแทนด้วยคำว่า self

อย่างไรก็ตามจริงๆแล้วนี่เป็นเพียงธรรมเนียมปฏิบัติที่ยึดถือกันมาเท่านั้น ถึงจะใช้คำอื่นแทน self ก็ให้ผลเหมือนกัน เช่นลองเขียนใหม่เป็น
class ผู้กล้า:
    def ถูกโจมตี(ตัวเอง,ความเสียหาย):
        ตัวเอง.hp -= ความเสียหาย - ตัวเอง.พลังป้องกัน

แบบนี้ก็จะให้ผลไม่ต่างกัน อย่างไรก็ตามนี่เป็นธรรมเนียมปฏิบัติที่ค่อนข้างจะตายตัว ดังนั้นในที่นี้ก็จะขอใช้เป็น self ด้วย จะไม่มีการใช้คำอื่นแทน

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

จะเห็นว่าตอนที่เรียกใช้นั้นพิมพ์เป็น
ผู้เล่น3.ถูกโจมตี(40)

จะสังเกตได้ว่ามีการใส่อาร์กิวเมนต์แค่ตัวเดียวเท่านั้นคือ 40 ซึ่งถูกนำไปแทนค่าพารามิเตอร์ "ความเสียหาย" ซึ่งเป็นพารามิเตอร์ตัวที่สอง

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

ดังนั้นบรรทัดต่อมาซึ่งเป็นเนื้อหาของเมธอด คือสิ่งที่ต้องการให้มีการทำเมื่อมีการเรียกใช้เมธอดนี้
self.hp -= ความเสียหาย - self.พลังป้องกัน

นี้จึงให้ผลเหมือนการพิมพ์ว่า
ผู้เล่น3.hp -= ความเสียหาย - ผู้เล่น3.พลังป้องกัน

ซึ่งก็จะเป็น ผู้เล่น3.hp -= 40-30 ดังนั้น hp ของ "ผู้เล่น3" จึงลดลงไป 10 นั่นเอง

ให้สังเกตว่า hp กับ "พลังป้องกัน" จะมี self. นำหน้าเพราะทั้งสองค่านี้เป็นแอตทริบิวต์ของออบเจ็กต์ ส่วน "ความเสียหาย" เป็นตัวแปรที่รับค่าเข้ามา ไม่ได้เกี่ยวข้องกับออบเจ็กต์ จึงไม่ต้องใส่ self.

อนึ่ง การเรียกใช้เมธอดอาจเรียกใช้โดย
ชื่อคลาส.ชื่อเมธอด(ออบเจ็กต์,อาร์กิวเมนต์อื่นๆ)

ซึ่งในตัวอย่างนี้จะเขียนเป็น
ผู้กล้า.ความเสียหาย(ผู้เล่น3,40)

ซึ่งความจริงแล้วการเขียนแบบนี้อาจเห็นภาพชัดกว่าด้วย เพราะอาร์กิวเมนต์ตัวแรกคือตัวออบเจ็กต์ ซึ่งจะถูกแทนด้วยพารามิเตอร์ self ในเมธอด

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



เมธอดจะสร้างกี่อันก็ได้ ถ้าจะเพิ่มเมธอดอีกก็เขียน def อีกอัน เช่นคราวนี้ลองเพิ่มเมธอด "ฟื้นพลัง" เอาไว้ใช้เวลาที่ได้รับคาถาฟื้นพลังหรือกินยาฟื้นพลัง
class ผู้กล้า:
    def ถูกโจมตี(self,ความเสียหาย):
        self.hp -= ความเสียหาย - self.พลังป้องกัน
    def ฟื้นพลัง(self,พลังฟื้นฟู):
        self.hp += พลังฟื้นฟู

จะเห็นว่าเมธอด "ฟื้นพลัง" ก็คล้ายๆเมธอด "ถูกโจมตี" แต่เรียบง่ายกว่า เพราะฟื้นเท่าไหร่ก็บวก hp ไปเท่านั้นเอย

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

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

ดังนั้นควรเขียนใหม่เป็น
class ผู้กล้า:
    def ถูกโจมตี(self,ความเสียหาย):
        if(ความเสียหาย>self.พลังป้องกัน()):
            self.hp -= ความเสียหาย - self.พลังป้องกัน
        else:
            self.hp -= 1
        if(self.hp<0):
            self.hp = 0
    def ฟื้นพลัง(self,พลังฟื้นฟู):
        self.hp += พลังฟื้นฟู
        if(self.hp>self.hpสูงสุด):
            self.hp = self.hpสูงสุด

เท่านี้เวลาถูกโจมตี hp ก็ไม่มีทางต่ำกว่า 0 และเวลาฟื้นพลังก็จะไม่มีทางเกิน hp สูงสุด

แต่แน่นอนว่าจำเป็นต้องกำหนดค่า "hpสูงสุด" ให้กับผู้กล้าด้วย ไม่เช่นนั้นเมื่อเรียกใช้เมธอด "ฟื้นพลัง" ก็จะขัดข้องทันที

ต้องระวังว่าก่อนที่จะเรียกใช้เมธอด จะต้องกำหนดค่าให้กับแอตทริบิวต์ที่จะเกี่ยวข้องกับเมธอดนั้นทั้งหมดให้เรียบร้อยก่อนแล้ว



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

เช่นถ้าพิมพ์เป็นว่า
ก = ผู้กล้า
print(ก) # ได้ <class '__main__.ผู้กล้า'>

จะเห็นว่า "ก" กลายเป็นตัวคลาส "ผู้กล้า" ไปเสียแล้ว

และถ้าเอาไปใช้เพื่อสร้างออบเจ็กต์ขึ้นใหม่อีกทีจะเป็นยังไง ลองดู
ผู้เล่น3 = ก()
print(type(ผู้เล่น3)) # ได้ <class '__main__.ผู้กล้า'>

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



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

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

__init__ เป็นเมธอดที่จะทำงานทันทีที่ออบเจ็กถูกประกาศสร้างขึ้นจากคลาส

ตัวอย่างการใช้งาน
class ผู้กล้า:
    def __init__(self):
        print('ผู้กล้าได้ถือกำเนิดขึ้นแล้ว')

ลองใช้งานดูโดยประกาศสร้างผู้กล้า
ผู้เล่น4 = ผู้กล้า()

เสร็จแล้วก็จะมีข้อความ "ผู้กล้าได้ถือกำเนิดขึ้นแล้ว" ปรากฏขึ้นมา

จะเห็นว่าเราไม่ได้เรียกเมธอด __init__() โดยตรง แค่สร้างออบเจ็กต์ขึ้นมาเมธอด __init__() ก็ทำงานแล้ว

แน่นอนว่าเราสามารถเรียกใช้มันโดยตรงโดยพิมพ์ชื่อเมธอดได้เช่นกัน เช่น
ผู้เล่น4.__init__()

เมธอด __init__ ก็เช่นเดียวกับเมธอดทั่วไปๆ คือจะต้องมีพารามิเตอร์อย่างหน่อยหนึ่งตัว โดยที่ตัวแรกคือตัวแปรที่แทนตัวออบเจ็กต์เอง (ซึ่งก็คือ self)

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

ตัวอย่างการใช้ เช่น
class ผู้กล้า:
    def __init__(self,ชื่อ,เลเวล,พลังโจมตี,พลังป้องกัน,hpสูงสุด):
        self.ชื่อ = ชื่อ
        self.เลเวล = เลเวล
        self.พลังโจมตี = พลังโจมตี
        self.พลังป้องกัน = พลังป้องกัน
        self.hpสูงสุด = hpสูงสุด
        self.hp = hpสูงสุด
        print('ผู้กล้าได้ถือกำเนิดขึ้นแล้ว')

จากนั้นเวลาที่จะสร้าง "ผู้กล้า" ขึ้นมาก็จะจำเป็นต้องใส่อาร์กิวเมนต์ไปด้วย ซึ่งก็จะกลายมาเป็นค่าเริ่มต้น
ผู้เล่น4 = ผู้กล้า('ยืนยัน',1,5,6,20)

หรือจะเขียนในรูปแบบคีย์เวิร์ดก็ได้เช่นกัน
ผู้เล่น4 = ผู้กล้า(ชื่อ='ยืนยัน',เลเวล=1,พลังโจมตี=5,พลังป้องกัน=6,hpสูงสุด=20)

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

ผลที่ได้ก็จะได้ว่า "ผู้เล่น4" ชื่อ "ยืนยัน" เลเวล 1 พลังโจมตี 5 พลังป้องกัน 6 hpสูงสุด 20

การทำแบบนี้สะดวกกว่าการที่จะต้องมาค่อยๆป้อนค่าให้แอตทริบิวต์ทีละตัว เพราะถ้าเป็นแบบเดิมจะต้องพิมพ์
ผู้เล่น4.ชื่อ = 'ยืนยัน'
ผู้เล่น4.เลเวล = 1
ผู้เล่น4.พลังโจมตี = 5
ผู้เล่น4.พลังป้องกัน = 6
ผู้เล่น4.hpสูงสุด = 20
ผู้เล่น4.hp = ผู้เล่น4.hpสูงสุด

แต่พอใช้ __init__ ก็แค่ใส่ค่าเป็นอาร์กิวเมนต์ ค่านี้ก็จะถูกนำมาป้อนให้กับแอตทริบิวต์ทันที

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

แต่ในบางครั้งเราก็อาจจะไม่จำเป็นต้องมาใส่ค่าของแอตทริบิวต์ทั้งหมด ในกรณีแบบนั้นก็อาจใช้การตั้งค่าเริ่มต้น
class ผู้กล้า:
    def __init__(self,ชื่อ,เลเวล=1,พลังโจมตี=4,พลังป้องกัน=4,hpสูงสุด=10):
        self.ชื่อ = ชื่อ
        self.เลเวล = เลเวล
        self.พลังโจมตี = พลังโจมตี
        self.พลังป้องกัน = พลังป้องกัน
        self.hpสูงสุด = hpสูงสุด
        self.hp = hpสูงสุด
        print('ผู้กล้าได้ถือกำเนิดขึ้นแล้ว')

พอทำแบบนี้แล้วก็จะไม่จำเป็นต้องใส่ค่าเลเวล, พลังโจมตี, พลังป้องกัน และ hpสูงสุด ถ้าไม่ใส่ก็จะได้ค่าเป็นค่าเป็น 1,4,4,10 ตามลำดับ แต่ถ้าใส่ก็จะได้ค่าตามที่ใส่

เช่น
ผู้เล่น5 = ผู้กล้า(ชื่อ='มั่นคง',พลังโจมตี=7)

แบบนี้ก็จะได้ว่าผู้เล่น5 ชื่อ "มั่นคง" เลเวล 1 พลังโจมตี 7 พลังป้องกัน 4 และ hpสูงสุด 10

เมธอดพิเศษนอกจาก __init__ แล้วก็ยังมีอีกหลายตัว จะแนะนำเพิ่มเติมในบทที่ ๒๔



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

ดังนั้นคราวนี้ลองมาสร้างคลาสใหม่ขึ้นอีกคลาสเพื่อใช้เป็นแอตทริบิวต์ในอีกคลาสหนึ่งกันดู

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

ในตัวอย่างก่อนหน้านี้เรา ให้ "พลังโจมตี" เป็นแอตทริบิวต์หนึ่งของ "ผู้กล้า" แต่คราวนี้จะลองเปลี่ยนใหม่โดยให้พลังโจมตีคำนวณจาก "ความแข็งแรง" และ "พลังโจมตีกายภาพ" ของ "อาวุธ"
class อาวุธ:
    def __init__(self,ชื่อ,พลังโจมตีกายภาพ,พลังโจมตีเวทย์,ความทนทาน):
        self.ชื่อ = ชื่อ
        self.พลังโจมตีกายภาพ = พลังโจมตีกายภาพ
        self.พลังโจมตีเวทย์ = พลังโจมตีเวทย์
        self.ความทนทาน = ความทนทาน
class ผู้กล้า:
    def __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังป้องกัน=4,hpสูงสุด=10):
        self.ชื่อ = ชื่อ
        self.เลเวล = เลเวล
        self.ความแข็งแรง = ความแข็งแรง
        self.พลังป้องกัน = พลังป้องกัน
        self.hpสูงสุด = hpสูงสุด
        self.hp = hpสูงสุด
    def พลังโจมตี(self):
        return self.ความแข็งแรง + self.อาวุธที่ถือ.พลังโจมตีกายภาพ
# เริ่มการใช้งาน
ผู้เล่น5 = ผู้กล้า('มั่นคง',1,7,6,15)
ดาบ1 = อาวุธ('ดาบเก่าขึ้นสนิม',6,0,10)
ผู้เล่น5.อาวุธที่ถือ = ดาบ1
print(ผู้เล่น5.อาวุธที่ถือ.ชื่อ) # ได้ ดาบเก่าขึ้นสนิม
print(ผู้เล่น5.อาวุธที่ถือ.พลังโจมตีกายภาพ) # ได้ 6
print(ผู้เล่น5.อาวุธที่ถือ.พลังโจมตีเวทย์) # ได้ 0
print(ผู้เล่น5.อาวุธที่ถือ.ความทนทาน) # ได้ 10
print(ผู้เล่น5.พลังโจมตี()) # ได้ 13

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

จากนั้นก็ให้ "ผู้เล่น5" ถือ "ดาบ1" ด้วยการตั้งให้แอตทริบิวต์ "อาวุธที่ถือ" ของ "ผู้เล่น5" เป็น "ดาบ1"

พอแอตทริบิวต์ "อาวุธที่ถือ" ของ "ผู้เล่น5" กลายเป็น "ดาบ1" ไปแล้วแบบนี้แอตทริบิวต์ของอาวุธก็กลายเป็นแอตทริบิวต์ของแอตทริบิวต์ เราสามารถเข้าถึงได้ด้วยการใส่จุด . สองต่อดังที่เห็น

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

ให้สังเกตว่า "พลังโจมตี" ในตัวอย่างนี้ต่างจากตัวอย่างที่ผ่านมา คือพลังโจมตีไม่ได้เป็นแอตทริบิวต์แล้วแต่กลายเป็นเมธอด ทำให้ต้องมีวงเล็บเปิดปิด () ต่อท้ายด้วย

นั่นเพราะในกรณีนี้เราไม่ได้เก็บค่าพลังโจมตีของผู้กล้าเอาไว้เป็นตัวแปรหนึ่งแล้วแต่เปลี่ยนให้มัน เป็นค่าที่ได้จากการคำนวณโดยรวมความแข็งแรงกับพลังโจมตีกายภาพของอาวุธแทน

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



ต่อไปลองเพิ่มคลาสและเมธอดลงไปให้หลากหลายขึ้นดู
class อาวุธ:
    def __init__(self,ชื่อ,พลังโจมตีกายภาพ,พลังโจมตีเวทย์,ความทนทาน):
        self.ชื่อ = ชื่อ
        self.พลังโจมตีกายภาพ = พลังโจมตีกายภาพ
        self.พลังโจมตีเวทย์ = พลังโจมตีเวทย์
        self.ความทนทาน = ความทนทาน
class เสื้อผ้า:
    def __init__(self,ชื่อ,พลังป้องกัน,ความทนทาน):
        self.ชื่อ = ชื่อ
        self.พลังป้องกัน = พลังป้องกัน
        self.ความทนทาน = ความทนทาน
class ผู้กล้า:
    def __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,พลังเวทย์=4,ความอดทน=4,hpสูงสุด=10):
        self.ชื่อ = ชื่อ
        self.เลเวล = เลเวล
        self.ความแข็งแรง = ความแข็งแรง
        self.พลังเวทย์ = พลังเวทย์
        self.ความอดทน = ความอดทน
        self.hpสูงสุด = hpสูงสุด
        self.hp = hpสูงสุด
    def พลังโจมตี(self):
        return self.ความแข็งแรง + self.อาวุธที่ถือ.พลังโจมตีกายภาพ
    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
# เริ่มการใช้งาน
ผู้เล่น6 = ผู้กล้า('กระสัน',1,4,8,6,15)
คฑา1 = อาวุธ('คฑาเก่าผุๆ',1,6,10)
เกราะ1 = เสื้อผ้า('เกราะเก่าผุๆ',4,10)
ผู้เล่น6.อาวุธที่ถือ = คฑา1
ผู้เล่น6.เสื้อผ้าที่ใส่ = เกราะ1
print(ผู้เล่น6.อาวุธที่ถือ.ชื่อ) # ได้ คฑาเก่าผุๆ
print(ผู้เล่น6.เสื้อผ้าที่ใส่.ชื่อ) # ได้ เกราะเก่าผุๆ
print(ผู้เล่น6.พลังโจมตี()) # ได้ 5
print(ผู้เล่น6.พลังโจมตีเวทย์()) # ได้ 14
print(ผู้เล่น6.พลังป้องกัน()) # ได้ 10

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

นอกจากนี้ยังได้เพิ่มเมธอด "พลังโจมตีเวทย์" ซึ่งคำนวณจาก "พลังโจมตีเวทย์" ของ "อาวุธที่ถือ" บวกกับ "พลังเวทย์" ของ "ผู้กล้า"

ในที่นี้จะคฑาหรือดาบก็กำหนดให้เป็นอาวุธเหมือนกัน มีพลังโจมตีกายภาพและเวทย์เหมือนกันเพียงแต่ค่าจะสูงต่ำต่างกันเท่านั้น

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

และสุดท้าย ใส่เมธอด "ถูกโจมตี" ไปด้วย โดยเขียนคล้ายกับเมธอดก่อนหน้านี้ ต่างกันแค่ "พลังป้องกัน" ในที่นี้ใช้เป็นเมธอดแล้วจึงต้องใส่วงเล็บ () ไว้ข้างหลัง



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

ลองใส่ตัวแปรคลาสให้กับคลาส "ผู้กล้า" ดู เช่นผู้กล้าแต่ละคนก็น่าจะมี "เงินเดือน" ให้สักหน่อย
class ผู้กล้า:
    def __init__(self,ชื่อ,เลเวล=1,ความแข็งแรง=4,ความอดทน=4,hpสูงสุด=10):
        self.ชื่อ = ชื่อ
        self.เลเวล = เลเวล
        self.ความแข็งแรง = ความแข็งแรง
        self.ความอดทน = ความอดทน
        self.hpสูงสุด = hpสูงสุด
        self.hp = hpสูงสุด
    เงินเดือน = 500

พอทำแบบนี้แล้วก็จะได้ว่าออบเจ็กต์ในคลาส "ผู้กล้า" ทุกคนจะมีแอตทริบิวต์ "เงินเดือน" ซึ่งมีค่า 500

พูดสั้นๆคือ "ผู้กล้า" ทุกคนมี "เงินเดือน" เป็น 500

แต่ไม่ใช่แค่นั้น ตัวคลาสเองก็มีค่าแอตทริบิวต์นี้เช่นกัน ลองดูตัวอย่าง
print(ผู้กล้า.เงินเดือน) # ได้ 500
ผู้เล่น7 = ผู้กล้า('มั่งมี')
print(ผู้เล่น7.เงินเดือน) # ได้ 500
ผู้เล่น8 = ผู้กล้า('เป็นสุข')
print(ผู้เล่น8.เงินเดือน) # ได้ 500

"ผู้เล่น7" และ "ผู้เล่น8" เป็นอินสแตนซ์ของคลาส "ผู้กล้า" จึงมีแอตทริบิวต์ "เงินเดือน" เป็น 500 และตัวคลาส "ผู้กล้า" เองก็มีแอตทริบิวต์ 500 เช่นกัน

แอตทริบิวต์ของคลาสสามารถเปลี่ยนแปลงได้หลังจากประกาศคลาสแล้ว สมมุติว่า "ผู้กล้า" ถูกประกาศลดเงินเดือนทั้งหมด
ผู้กล้า.เงินเดือน = 400
print(ผู้เล่น7.เงินเดือน) # ได้ 400
print(ผู้เล่น8.เงินเดือน) # ได้ 400
print(ผู้กล้า.เงินเดือน) # ได้ 400

จะเห็นว่าพอเปลี่ยนค่า "เงินเดือน" ซึ่งเป็นแอตทริบิวต์ของคลาส "เงินเดือน" ของออบเจ็กต์ในคลาสก็เปลี่ยนตาม

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

เช่น สมมุติว่า "ผู้เล่น7" ทำผลงานดีเลยได้ขึ้นเงินเดือน ลองประกาศเพิ่มเงินเดือนให้กับ "ผู้เล่น7" คนเดียว
ผู้เล่น7.เงินเดือน = 3000
print(ผู้เล่น7.เงินเดือน) # ได้ 3000
print(ผู้เล่น8.เงินเดือน) # ได้ 400
print(ผู้กล้า.เงินเดือน) # ได้ 400

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

แล้วในตอนนี้หากลองเปลี่ยนค่า "เงินเดือน" ของ "ผู้กล้า" อีกครั้งจะเกิดอะไรขึ้น ก็จะพบว่า "เงินเดือน" ของ "ผู้เล่น7" ไม่มีการเปลี่ยนแปลงใดๆ แต่ของ "ผู้เล่น8" จะยังเปลี่ยนแปลงตามอยู่
ผู้กล้า.เงินเดือน = 600
print(ผู้เล่น7.เงินเดือน) # ได้ 3000
print(ผู้เล่น8.เงินเดือน) # ได้ 600
print(ผู้กล้า.เงินเดือน) # ได้ 600

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

แต่ของ "ผู้เล่น8" ยังไม่มีการป้อนค่าแอตทริบิวต์ "เงินเดือน" ให้ ดังนั้นแอตทริบิวต์ "เงินเดือน" ก็ยังเป็นแอตทริบิวต์ของคลาสอยู่

ในตอนนี้ถ้ามีการสร้างออบเจ็กต์ในคลาสขึ้นมาใหม่ก็จะได้ค่าเงินที่เปลี่ยนแปลงไปแล้วนี้เช่นกัน
ผู้เล่น9 = ผู้กล้า('พอใจ')
print(ผู้เล่น9.เงินเดือน) # ได้ 600

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

ลองใช้กับอย่างอื่น เช่นกำหนดอาวุธกับเสื้อผ้าให้กับผู้เล่นเริ่มต้น
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,0,5)
    เสื้อผ้าที่ใส่ = เสื้อผ้า('ชุดเก่าๆ',3,5)
ผู้เล่น10 = ผู้กล้า('มีฤทธิ์')
print(ผู้เล่น10.อาวุธที่ถือ.ชื่อ) # ได้ มีดสั้นเก่าๆ
print(ผู้เล่น10.อาวุธที่ถือ.พลังโจมตีกายภาพ) # ได้ 3
print(ผู้เล่น10.เสื้อผ้าที่ใส่.ชื่อ) # ได้ ชุดเก่าๆ



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



อ้างอิง



<< บทที่แล้ว      บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python

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

目次

日本による名言集
モジュール
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
機械学習
-- ニューラル
     ネットワーク
ภาษา javascript
ภาษา mongol
言語学
maya
確率論
日本での日記
中国での日記
-- 北京での日記
-- 香港での日記
-- 澳門での日記
台灣での日記
北欧での日記
他の国での日記
qiita
その他の記事

記事の類別



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  記事を検索

  おすすめの記事

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文