By Arnon Puitrakul - 12 พฤศจิกายน 2021
เมื่อไม่กี่วันก่อนนั่งคิดขำ ๆ กับเพื่อน เรามาลองแกล้ง Python กันมั้ย เราสงสัยกันเรื่องของ None เป็นของที่เราใช้กันบ่อยมาก ๆ แต่เรามักจะมองว่า เออ มันก็เอาไว้แค่เป็นค่าส่งกลับเวลามันไม่มีของที่เราตามหาอะไรแบบนั้น ทำให้เราก็อาจจะเอามาใช้เป็น Flag ในการเช็คอีกทอดก็มี ทำให้มันเกิดเป็นบทความในวันนี้ เพราะเราจะบอกว่า 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
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 ในการเช็คโอกาสรอดจะเยอะกว่า
Obsidian เป็นโปรแกรมสำหรับการจด Note ที่เรียกว่า สารพัดประโยชน์มาก ๆ เราสามารถเอามาทำอะไรได้เยอะมาก ๆ หนึ่งในสิ่งที่เราเอามาทำคือ นำมาใช้เป็นระบบสำหรับการจัดการ Todo List ในแต่ละวันของเรา ทำอะไรบ้าง วันนี้เราจะมาเล่าให้อ่านกันว่า เราจัดการะบบอย่างไร...
อะ อะจ๊ะเอ๋ตัวเอง เป็นยังไงบ้างละ เมื่อหลายเดือนก่อน เราไปเล่าเรื่องกันขำ ๆ ว่า ๆ จริง ๆ แล้วพวก Loop ที่เราใช้เขียนโปรแกรมกันอยู่ มันไม่มีอยู่จริง สิ่งที่เราใช้งานกันมันพยายาม Abstract บางอย่างออกไป วันนี้เราจะมาถอดการทำงานของ Loop จริง ๆ กันว่า มันทำงานอย่างไรกันแน่ ผ่านภาษา Assembly...
นอกจากการทำให้ Application รันได้แล้ว อีกเรื่องที่สำคัญไม่แพ้กันคือการวางระบบ Monitoring ที่ดี วันนี้เราจะมาแนะนำวิธีการ Monitor การทำงานของ MySQL ผ่านการสร้าง Dashboard บน Grafana กัน...
จากตอนที่แล้ว เราเล่าในเรื่องของการ Harden Security ของ SSH Service ของเราด้วยการปรับการตั้งค่าบางอย่างเพื่อลด Attack Surface ที่อาจจะเกิดขึ้นได้ หากใครยังไม่ได้อ่านก็ย้อนกลับไปอ่านกันก่อนเด้อ วันนี้เรามาเล่าวิธีการที่มัน Advance มากขึ้น อย่างการใช้ fail2ban...