By Arnon Puitrakul - 19 พฤศจิกายน 2021
หลังจากเราเอาเรื่องของ Dunder Method มาเล่ากันเมื่อไม่นานมานี้ ทำให้มีคนถามเราเข้ามาในเรื่องของ new และ init ที่เป็นหนึ่งใน Dunder Method ที่เราใช้ในในการสร้าง Instnace หรือ Object บน Python นั่นเอง วันนี้เลยจะมาอธิบายเพิ่มเติมกันว่า ทั้ง 2 Methods นี้มันต่างกันอย่างไร การสร้าง Instance ใน Python มันทำยังไง และเราจะใช้งานมันได้อย่างไรบ้าง
เริ่มจากตัวที่เราใช้งานกันบ่อย ๆ ก่อนอย่าง Init หรือบางครั้งเราก็จะเรียกมันว่า Constructor ก็ได้เหมือนกัน โดยที่ Method นี้มันจะถูกเรียกเป็น Method แรกหลังจากที่ Instance ของเราถูกสร้างเรียบร้อยแล้ว โดยที่เราอาจจะมีการเรียกคำสั่งสำหรับการ Init ค่าอะไรบางอย่างก็ได้ เช่นการเพิ่ม Attribute ต่าง ๆ ลงไป
class Customer :
def __init__ (self, name, surname) :
self.name = name
self.surname = surname
print(self.__dict__)
ตัวอย่างด้านบน เราทำการสร้าง Class Customer ขึ้นมา โดยที่เราเรียก init ให้ทำการรับค่าเข้ามาเป็น name และ surname เข้ามา แล้วให้มันเอาไปใส่ใน Attribute ของมัน จากนั้น เพื่อเป็นการเช็คว่า มันเข้าไปอยู่ใน Attribute จริง ๆ เราก็เลยเรียกคำคำสั่ง Dict ออกมา
{'name': 'My name', 'surname' : 'My Surname'}
คำสั่ง Dict นี้ก็เป็น Dunder Method ตัวหนึ่งเหมือนกันที่โดยค่าเริ่มต้น มันจะพ่น Attribute ออกมาเป็น Dictionary ออกมาให้เราได้เลย จากตัวอย่าง เราทดลองสร้าง Object ออกมา เราก็จะเห็นได้เลยว่า มันก็จะพ่น Dictionary ออกมาให้เรา แปลว่า Constructor มันทำงานแน่นอน
แต่เราอยากให้สังเกตตรงที่ว่า init สิ่งที่มันทำจริง ๆ คือ มันจะทำการ Mutate ตัวเอง โดยที่ไม่ได้มีการคืนค่าอะไรกลับไปเลย เราไม่เคยเห็น init ที่คืนค่ากลับไปแน่ ๆ ทำให้เกิดคำถามว่า เอ๊ะ แล้วถ้า Init มันไม่ได้ร้าง Object ออกมา แล้วใครเป็นใครสร้างละ
นั่นทำให้เราจะต้องมาทำความรู้จัก Dunder Method อีกหนึ่งตัวคือ new นั่นเอง โดยที่มันมี Signature คือ มันจะต้อง Return Object ของ Class นั้น ๆ กลับไป พร้อมกับมีการรับ Argument เข้ามาด้วย
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls)
return obj
จากด้านบน เป็นตัวอย่างที่ง่ายที่สุดสำหรับการ Override new Method ด้วย Default Method นี่แหละง่าย ๆ เลย สั้น ๆ คือ เรารับเข้ามาว่า Class ที่เราจะสร้างมันคืออะไร ส่วน Argument อื่น ๆ เราเขียนเข้ามาเฉย ๆ เป็น Pattern ยังไม่ต้องไปสนใจนะ เดี๋ยวจะ งง จากนั้น เราก็จะเข้าไปสร้าง Instance ขึ้นมาจริง ๆ ละ ผ่าน new Method ของ object อีกที หรือจริง ๆ แล้ว เพื่อให้ อ๋อ เข้าไปอีก เราสามารถแทนที่มันได้ด้วย super().__new__(cls) (เราจะบอกว่าเขียนแบบนี้ดีกว่านะ เพราะว่า ไม่ว่ายังไง แม่มันก็คือ object แน่นอน แต่ถ้าเราเขียนแบบในตัวอย่าง วันนึงคน Maintain Python บอกว่า ไม่เอาละ ไม่อยากให้ชื่อ object แล้วอยากเปลี่ยนเป็นชื่ออื่น เขียนแบบตัวอย่างแตกได้ ใช้แบบที่เราบอกเมื่อครู่รอดง่ายกว่าเยอะ) ได้ เอ๊ะอะไรมั้ยฮ่ะ ใช่แล้ว จริง ๆ แล้ว Object ทั้งหมดที่เราเห็นใน Python มันมีแม่คนเดียวกัน คือ Class ที่ชื่อว่า object
>>> object
<class 'object'>
ถ้าเราลองเช็คว่า object มันเป็นอะไร มันก็จะบอกว่า อ่อ ชั้นเป็น Class แหละ ซึ่งจริง ๆ เราเรียกมันว่า แม่ทุกสถาบันได้เลย เพราะมันเป็นต้นกำเนินแห่ง Object ทั้งปวงใน Python ซึ่งคำสั่ง New ของมัน ก็จะรับ Class เข้าไป แล้วก็จะพ่น Object ของ Class ที่เราใส่เข้าไปกลับมาให้เรานั่นเอง ทำให้เราสามารถเอาตัวแปร obj ไปรับในตัวอย่างก่อนหน้า และ Return กลับไปได้เลยนั่นเอง
กับเราจะบอกว่า จริง ๆ แล้วลองไปเช็คดูได้ new ของ Object กับ new ใน Object ที่เราสร้างมันเป็นตัวเดียวกันเลย
อ่านมาแล้ว เออ มันดูเหมือนจะเป็นคนละเรื่องกันเลยนะ อื้ม... ก็ใช่แหละ เพราะมันทำหน้าที่คนละอย่างกัน self เขาทำหน้าที่ในการ Init Object ของเราให้พร้อมใช้งานต่าง ๆ อะไรก็ว่ากันไป แต่ตัวที่สร้าง Object จริง ๆ มันคือ new แต่ถึงแม้ว่า มันจะทำงานกันคนละหน้าที่ แต่มันก็มีความสัมพันธ์กันอยู่อย่างแนบเนียน
เวลาเราเขียน Method ต่าง ๆ สงสัยมั้ยว่า self มันมาจากไหน ??? ทำไม เราไม่เขียนเราจะเรียก Method แล้วพัง มันจะบอกว่า Function นี้รับ Argument 2 ตัว เราใส่เข้ามาตัวเดียว ตอนเราใส่เราก็ใส่เข้ามาตัวเดียวเหมือนใน Signature ที่เราเขียนเลยนะ เอ๊ะอะไรของมัน !!!!
จริง ๆ แล้ว Self ก็ตามชื่อเลย มันก็คือตัว Object เอง ที่พ่นออกมาจาก new Method นี่ละ ตัว Python มันจะต้อง Inject self เข้าไปในทุก ๆ ที่ เพื่อให้เรายังคงเรียกตัวเองได้ ซึ่งมันก็คือตัวเดียวกับ Object ที่เราเรียกจากข้างนอกเลย ตัวอย่างง่าย ๆ
class Employee :
def __init__ (self, name:str) :
self.name = name
def get_me (self) :
return self
emp_a = Employee("Alice")
print(id(emp_a), id(emp_a.get_me()))
เราสร้าง Class ง่าย ๆ ขึ้นมาตัวนึง โดยที่เราสร้าง Method ตัวนึงคือ get_me สิ่งที่มันทำคือ เราขอ Self ออกมาเลย แล้วเราก็เอา Class นี้แหละไปสร้างเป็น Object ออกมา เราขอ ID ของ emp_a ซึ่งก็คือ Object ที่เราสร้างจาก Class Employee และ self ที่อยู่ใน Object ของ emp_a ผ่าน Method get_me ที่เราสร้างไว้ก่อนหน้านี้
4337806688 4337806688
ผลที่ได้ เราจะเห็นว่า มันเป็นตัวเลขชุดเดียวกันเลย นั่นแปลว่าจริง ๆ แล้ว Object ที่เราเรียกอยู่ข้างนอกนั่นทั้งหมด มันคือตัวเดียวกับ Self ที่อยู่ใน Class ที่เราอ้างถึง 100% เลย แน่นอน เพราะมันคือตัวเดียวกันยังไงละ นั่นแปลว่าอะไร ?
นั่นแปลว่า จริง ๆ แล้ว มันนำไปสู่การตอบคำถามเรื่อง ไก่กับไข่อะไรเกิดก่อนกัน ไม่ใช่ละ แต่ก็ใส่เคียง มันทำให้เราเห็นได้เลยว่าจริง ๆ แล้วในการทำงานของการสร้าง Instance จริง ๆ มันจะเริ่มไปเรียก new Method ที่อยู่ใน Class นั่นแหละ แล้วก็ไปเรียก new ที่อยู่ใน Class object เพื่อที่จะพ่น self หรือก็คือตัว Object จริง ๆ ออกมา จากนั้น Constructor อย่าง init ถึงจะทำงานเป็นไม้ต่อ เพื่อทำการประกาศค่าเริ่มต้นของ Object นั้น ๆ ตามที่เรากำหนดไว้นั่นเอง
ดังนั้น ในวันนี้เราสามารถแยกความแตกต่างระหว่าง การสร้าง และ การ Init Object ได้แล้วผ่าน Dunder Method อย่าง new และ init ที่เราคุ้นเคยกันเป็นอย่างดี โดยที่ new จะทำหน้าที่ในการสร้าง instance ขึ้นมาจริง ๆ ผ่านการไปเรียก new จาก object หรือ super() ที่เป็น Class แม่ของมัน จากนั้นค่อยพ่น Class กลับมา แล้วค่อยไปเรียก init อีกรอบเพื่อทำการ Initiate ตามที่เราต้องการนั่นเอง
เราเป็นคนที่อ่านกับซื้อหนังสือเยอะมาก ปัญหานึงที่ประสบมาหลายรอบและน่าหงุดหงิดมาก ๆ คือ ซื้อหนังสือซ้ำเจ้าค่ะ ทำให้เราจะต้องมีระบบง่าย ๆ สักตัวในการจัดการ วันนี้เลยจะมาเล่าวิธีการที่เราใช้ Obsidian ในการจัดการหนังสือที่เรามีกัน...
หากเราเรียนลงลึกไปในภาษาใหม่ ๆ อย่าง Python และ Java โดยเฉพาะในเรื่องของการจัดการ Memory ว่าเขาใช้ Garbage Collection นะ ว่าแต่มันทำงานยังไง วันนี้เราจะมาเล่าให้อ่านกันว่า จริง ๆ แล้วมันทำงานอย่างไร และมันมีเคสใดที่อาจจะหลุดจนเราต้องเข้ามาจัดการเองบ้าง...
ก่อนหน้านี้เราเปลี่ยนมาใช้ Zigbee Dongle กับ Home Assistant พบว่าเสถียรขึ้นเยอะมาก อุปกรณ์แทบไม่หลุดออกจากระบบเลย แต่การติดตั้งมันเข้ากับ Synology DSM นั้นมีรายละเอียดมากกว่าอันอื่นนิดหน่อย วันนี้เราจะมาเล่าวิธีการเพื่อใครเอาไปทำกัน...
เมื่อหลายวันก่อนมีพี่ที่รู้จักกันมาถามว่า เราจะโหลด CSV ยังไงให้เร็วที่สุด เป็นคำถามที่ดูเหมือนง่ายนะ แต่พอมานั่งคิด ๆ ต่อ เห้ย มันมีอะไรสนุก ๆ ในนั้นเยอะเลยนี่หว่า วันนี้เราจะมาเล่าให้อ่านกันว่า มันมีวิธีการอย่างไรบ้าง และวิธีไหนเร็วที่สุด เหมาะกับงานแบบไหน...