Tutorial

Instance Creation บน Python ไส้มันเป็นยังไง

By Arnon Puitrakul - 19 พฤศจิกายน 2021

Instance Creation บน Python ไส้มันเป็นยังไง

หลังจากเราเอาเรื่องของ Dunder Method มาเล่ากันเมื่อไม่นานมานี้ ทำให้มีคนถามเราเข้ามาในเรื่องของ new และ init ที่เป็นหนึ่งใน Dunder Method ที่เราใช้ในในการสร้าง Instnace หรือ Object บน Python นั่นเอง วันนี้เลยจะมาอธิบายเพิ่มเติมกันว่า ทั้ง 2 Methods นี้มันต่างกันอย่างไร การสร้าง Instance ใน Python มันทำยังไง และเราจะใช้งานมันได้อย่างไรบ้าง

__init__()

เริ่มจากตัวที่เราใช้งานกันบ่อย ๆ ก่อนอย่าง 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 ออกมา แล้วใครเป็นใครสร้างละ

__new__()

นั่นทำให้เราจะต้องมาทำความรู้จัก 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 ตามที่เราต้องการนั่นเอง

Read Next...

ฮาวทูย้าย Synology Add-on Package ไปอีก Volume

ฮาวทูย้าย Synology Add-on Package ไปอีก Volume

เรื่องราวเกิดจากการที่เราต้องย้าย Add-on Package ใน DSM และคิดว่าหลาย ๆ คนน่าจะต้องประสบเรื่องราวคล้าย ๆ กัน วันนี้เราจะมาเล่าวิธีการว่า เราทำยังไง เจอปัญหาอะไร และ แก้ปัญหาอย่างไรให้ได้อ่านกัน...

จัดการ Docker Container ง่าย ๆ ด้วย Portainer

จัดการ Docker Container ง่าย ๆ ด้วย Portainer

การใช้ Docker CLI ในการจัดการ Container เป็นท่าที่เราใช้งานกันทั่วไป มันมีความยุ่งยาก และผิดพลาดได้ง่ายยังไม่นับว่ามี Instance หลายตัว ทำให้เราต้องค่อย ๆ SSH เข้าไปทำทีละตัว มันจะดีกว่ามั้ย หากเรามี Centralised Container Managment ที่มี Web GUI ให้เราด้วย วันนี้เราจะพาไปทำความรู้จักกับ Portainer กัน...

Host Website จากบ้านด้วย Cloudflare Tunnel ใน 10 นาที

Host Website จากบ้านด้วย Cloudflare Tunnel ใน 10 นาที

ปกติหากเราต้องการจะเปิดเว็บสักเว็บ เราจำเป็นต้องมี Web Server ตั้งอยู่ที่ไหนสักที่หนึ่ง ต้องใช้ค่าใช้จ่าย พร้อมกับต้องจัดการเรื่องความปลอดภัยอีก วันนี้เราจะมาแนะนำวิธีการที่ง่ายแสนง่าย ปลอดภัย และฟรี กับ Cloudflare Tunnel ให้อ่านกัน...

จัดการข้อมูลบน Pandas ยังไงให้เร็ว 1000x ด้วย Vectorisation

จัดการข้อมูลบน Pandas ยังไงให้เร็ว 1000x ด้วย Vectorisation

เวลาเราทำงานกับข้อมูลอย่าง Pandas DataFrame หนึ่งในงานที่เราเขียนลงไปให้มันทำคือ การ Apply Function เข้าไป ถ้าข้อมูลมีขนาดเล็ก มันไม่มีปัญหาเท่าไหร่ แต่ถ้าข้อมูลของเราใหญ่ มันอีกเรื่องเลย ถ้าเราจะเขียนให้เร็วที่สุด เราจะทำได้โดยวิธีใดบ้าง วันนี้เรามาดูกัน...