Tutorial

Python กับ None ร่างจริงที่ไม่ใช่ร่างทรง (ซะที่ไหน !)

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

Python กับ None ร่างจริงที่ไม่ใช่ร่างทรง (ซะที่ไหน !)

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

จริง ๆ None มีตัวตน

>>> type(None)
<class 'NoneType'>

หลาย ๆ คนอาจจะคิดว่า None มันเป็นแค่ Type พิเศษที่ไม่ได้มีตัวตนอะไร แต่เราจะบอกว่า จริง ๆ แล้ว None เนี่ย มันมีตัวตนจริง ๆ นะ เป็น Object ด้วย ถ้าไม่เชื่อลองรัน Code ด้านบนดู มันก็จะได้ออกมาบอกว่า None มันเป็น Object จาก NoneType Class ทำให้จริง ๆ แล้ว None มีตัวตนจริง ๆ เป็น Object ปกติที่เราสร้างนี่แหละ

>>> hex(id(None))
'0x104d18398'

หนักกว่านั้นอีก ถ้าเราลองดู Memory Address ของ None ที่เหมือนจะไม่มี มันมีอยู่ใน Memory จริง ๆ เพื่อการสังเกตที่ลึกเข้าไปอีก เราลองทำแบบด้านล่างดู

>>> a = type(None)()
>>> hex(id(a))
'0x104d18398'

เราลองสร้าง Object  a ให้เป็น NoneType ดู และเราลองเอา Memory Address ของ a ออกมาดู เราจะเห็นได้เลยว่า มันตรงกับ None เฉย ๆ เลย ซึ่งถ้าเราเข้าไปอ่านลึก ๆ เราจะทราบว่าจริง ๆ แล้ว None เป็น Singleton เลยนะ ทำให้เราสามารถใช้ None ในการเปรียบเทียบกับ None ได้ทั้งโปรแกรมเลย โดยที่มั่นใจได้เลยว่า None จะเท่ากับ None จริง ๆ ในทุก ๆ เคส เพราะเราเอาของชิ้นเดียวกันเป๊ะ มาเทียบกันเลย ไม่ว่า มันจะมี Alias เป็นอะไรก็ตาม ลองเข้าไปอ่านเพิ่มเติมได้ที่ Python Document

Check None ยังไงให้รอด

def checker (a : int) -> Union[None, int] :
    if a > 0 :
        return a

โดยทั่วไปแล้ว เมื่อเราสร้าง Function ขึ้นมา และ เราไม่ได้ทำการเรียก keyword return หรือ อาจจะเดินไปไม่ถึง ตัว Python มันก็จะใช้ Default เป็น None เลย ทำให้เราสามารถทำมันเป็น Flag ในการเช็คได้ว่าถ้ามันมีอะไรผิดพลาด แทนที่มันจะ return อะไรกลับมา ถ้ามันกลับมาเป็น None แปลว่าแตกก็ได้เหมือนกัน

>> checker(0)
None

จากตัวอย่าง เราเขียน Function ไว้เช็คแค่ว่าถ้า a มันมากกว่า 0 ให้มันเอา a กลับมาเลย แต่ถ้าไม่ใช่ละ เราไม่ได้ให้มันทำอะไรต่อแล้ว ถ้าเราคิดแบบนี้ มันไม่น่าจะเอาอะไรกลับมาเลย แต่ถ้าเราลองเอาตัวแปรมารับ หรือลองใน Interactive Shell เลย เราจะเห็นว่า จริง ๆ แล้วมันไม่ได้เป็นอย่างที่เราคิด มันได้กลับมาเป็น None เฉยเลย ทำให้สามารถทำอย่างที่เราบอกได้ว่า อาจจะใช้เป็น Flag ในการเช็คได้

class MyClass :
    def __eq__ (self, other) :
        return True

test_obj = MyClass()

print (test_obj == None)
print (test_obj is None)

ทีนี้ ปัญหามันจะเริ่มเกิดขึ้นเมื่อเราพยายามที่จะเปรียบเทียบ None เพราะคนทั่ว ๆ ไปคิดว่า None มันเป็นแค่ Keyword แต่จากที่เราคุยกันมา มันไม่ได้เป็นแบบนั้นเลย เราลองสร้าง Class และ Object ง่าย ๆ เลย โดยที่เรา Override Dunder Function eq ให้มันเอา True กลับไปเสมอ ดังนั้น ใน Print ด้านล่างควรจะเกิดอะไรขึ้น

True
False

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

สรุป

วันนี้เรามาเล่าเรื่องของ Object ตัวนึงที่เราเจอมันบ่อย ๆ แต่เราก็ไม่ได้คิดว่ามันเป็น Object ด้วยซ้ำ นั่นก็คือ None Object ที่ถูกสร้างมาจาก Class ที่ชื่อว่า NoneType นั่นเอง โดยมันมรสมบัติหลาย ๆ อย่างที่ช่วยทำให้เราเอามาใช้งานเป็นพวก Flag ในการเช็ค รวมไปถึงเป็น Default Value ในหลาย ๆ เวลา เช่น Return Function Value ถ้าเราไม่มี Return มันก็จะเอากลับไปเป็น None ให้เราเองเลย แต่เวลาใช้งานจริง ๆ แนะนำให้ระวังเรื่องของวิธีการเช็คให้ดี ๆ เพราะอาจจะเจอเคสแปลก ๆ ที่ทำให้หลุดได้ แนะนำให้ใช้ Identity Operator ในการเช็คโอกาสรอดจะเยอะกว่า

Read Next...

โหลด CSV วิธีไหนเร็วที่สุด ?

โหลด CSV วิธีไหนเร็วที่สุด ?

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

ฮาวทู Sync Obsidian Note ด้วย LiveSync Plugin

ฮาวทู Sync Obsidian Note ด้วย LiveSync Plugin

ตั้งแต่ใช้ Obsidian เป็น App จด Note หลัก ชอบวิธีการจัดการมาก ๆ เมื่อใช้งานร่วมกับ Plugin ต่าง ๆ ยิ่งทำให้การใช้งานยืดหยุ่นมากกว่าเดิมอีกเยอะมาก ๆ ติดอยู่เรื่องเดียวคือ การใช้งานในหลาย ๆ อุปกรณ์ แต่เราเจอ Plugin ตัวนึงที่น่าสนใจมาก ๆ คือ LiveSync วันนี้เราจะมาเล่าวิธีการติดตั้ง และใช้งานมันกัน...

การสร้าง SSD Storage Pool บน Synology DSM

การสร้าง SSD Storage Pool บน Synology DSM

สำหรับคนที่ใช้ Synology NAS บางรุ่นจะมีช่อง M.2 สำหรับเสียบ NVMe SSD โดยพื้นฐาน Synology บอกว่ามันสำหรับการทำ Cache แต่ถ้าเราต้องการเอามันมาทำเป็น Storage ละ มันจะทำได้มั้ย วันนี้เราจะมาเล่าวิธีการทำกัน...

Multiprogramming, Multiprocessing และ Multithreading

Multiprogramming, Multiprocessing และ Multithreading

หลังจากที่เรามาเล่าเรื่อง malloc() มีคนอยากให้มาเล่าเรื่อง pthread เพื่อให้สามารถยัด Content ที่ละเอียด และเข้าใจง่ายในเวลาที่ไม่นานเกินไป เลยจะมาเล่าพื้นฐานที่สำคัญของคำ 3 คำคือ Multiprogramming, Multitasking, Multiprocessing และ Multithreading...