φυβλαςのβλογ
บล็อกของ 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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2019年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文