Tutorial

Comparison Operators กับ List และ Iterable อื่น ๆ บน Python

By Arnon Puitrakul - 17 มกราคม 2022

Comparison Operators กับ List และ Iterable อื่น ๆ บน Python

เรื่องนึงที่เราว่ามันเป็น Hidden หรือไม่ก็ Unseen สำหรับเรา เวลาเราใช้งาน Python เลย ตอนรู้จักครั้งแรกคือ ห่ะ แบบนี้ก็ได้เหรอ คือ การใช้พวก Comparison Operators อย่าง เท่ากับ มากกว่า น้อยกว่า อะไรพวกนั้น กับ Iterable ต่าง ๆ อย่างพวก List และ Set อะไรพวกนั้นได้ด้วย วันนี้เราลองมาดูกัน

Set

x = {1, 2, 3}
y = {2, 3, 4, 5}

z = x.issubset(y) 

เราขอเริ่มจากตัวที่เข้าใจง่ายที่สุดก่อน อย่าง Set ถ้าใครที่เคยใช้ Set ใน Python น่าจะคุ้นเคยกันดี ซึ่งหนึ่งในสิ่งที่เราจะเช็ค เราอาจจะเช็คพวก Subset และ Superset อะไรพวกนั้นกับ Set อีกตัว ซึ่งใน Python เอง เราก็สามารถที่จะเช็คได้ผ่านคำสั่ง issubset() ได้เลยอะไรแบบนั้น แต่เราจะเห็นว่า เออ มันก็ดูรก ๆ หน่อย มันน่าจะดีมาก ๆ ถ้ามี Shorthand ให้เราสามารถใช้ได้ พูดมาขนาดนี้ ใช่แล้วละ เราสามารถใช้ Comparison Operators ได้

>>> {1} == {1}
True

>>> {1} < {1,2}
True

>>> {1,2} < {1}
False

>>> {1,3} < {1,2}
False

ดูผ่าน ๆ อาจจะ งง นิด ๆ ทริกง่าย ๆ คือ เท่ากันหมายความว่า Set ทั้ง 2 มี Member เหมือนกันทุกตัว 100% ทำให้มันเท่ากัน ส่วนเครื่องหมาย มากกว่า น้อยกว่า มองง่าย ๆ ว่า ด้านที่น้อยกว่าเป็น Subset ของอีกด้านหรือไม่ ทำให้ในตัวอย่างที่ 2 เป็นจริง เพราะ 1 ของด้านซ้าย อยู่ใน Set ด้านขวาที่เอามาเปรียบเทียบ แต่ในตัวอย่างที่ 3 มองกลับกัน 2 ของด้านซ้าย ไม่ได้อยู่ใน Set ด้านขวา ทำให้ Set ด้านขวา ไม่ได้เป็น Superset ของ Set ด้านซ้ายทำให้ได้ False ไป และตัวอย่างสุดท้าย คือตัวอย่างที่ 4 อันนี้ไม่เป็นจริงใน 2 Member คือ 3 ในฝั่งซ้ายไม่ได้อยู่ในฝั่งขวา และ 2 ในฝั่งขวา ไม่ได้อยู่ในด้านซ้าย ทำให้ไม่เป็นจริงได้ False ออกมา

และถ้าเราเพิ่มเท่ากับ เป็น มากกว่าเท่ากับ และ น้อยกว่าเท่ากับ เราก็จะได้เป็นเงื่อนไขที่เป็น หรือ เช่น มากกว่าเท่ากับ ก็จะเป็น Superset หรือ เป็น Set เดียวกันอะไรแบบนั้นเหมือนเราใช้เครื่องหมายพวกนี้ตามปกติทุกประการเลยแค่นั้น

List และ Iterator อื่น ๆ

สาเหตุที่เราไม่เอา List ที่ดูจะเป็น Data Structure ที่ง่ายที่สุดขึ้นก่อน เพราะ มันเป็นตัวที่ปวดหัวมาก ๆ เพราะสิ่งที่มันจะเช็คคือ ตัวไหนมาก่อน หรือก็คือเป็น Lexicographic order บางคนก็เรียก Dictionary Order ก็ว่ากันไป เอาง่าย ๆ อย่าง String กันก่อน

>>> "brand" < "black"
False

เราเทียบง่าย ๆ เลย คือคำว่า Brand กับ Black ถ้าเราเอาน้อยกว่ามาใส่ มันจะเป็นจริงได้ก็ต่อเมื่อ Brand มาก่อน Black ใน Dictionary เทียบง่าย ๆ คือ ตัวแรก มันเท่ากัน เพราะคือตัว B เหมือนกัน แต่ตัวที่ 2 นี่แหละ ตัดเชือกละ สาเหตุที่มันเป็น False เพราะ L ที่อยู่ใน Black มันมาก่อน ตัว R ใน Brand ทำให้ Brand มันเลยมากกว่านั่นเอง แค่นี้เลย

>>> ["b", "r"] < ["b", "l"]
False

>>> [1,2] < [2,2]
True

ถ้าเราจำกันได้ จริง ๆ แล้ว String มันก็เป็นเหมือน List ที่มีแค่ Character และถ้าเราเห็นพฤติกรรมจากตัวอย่างก่อนหน้าแล้ว เราก็จะเดาของ List ได้ไม่ยากเลย เราเอาตัวอย่างเดิมมาเลย แต่เราใช้แค่ 2 ตัวแรก ก็รู้เรื่องแล้ว เราก็จะได้ผลลัพธ์เหมือนกันเป๊ะ ๆ เลย

งั้นเอาใหม่ เราลองมาเป็นอะไรที่เข้าใจง่ายกว่านั้นหน่อย เราเอา คู่ของ 1,2 และ 2,2 มา โดยเทียบกันผ่านเครื่องหมายน้อยกว่า มันก็จะเป็นจริง เพราะ 2 ในตัวแรกของฝั่งขวา มันมาทีหลัง 1 ที่เป็นตัวแรกของฝั่งซ้ายนั่นเอง

>>> [1] < [2,2]
True

ในตัวอย่างด้านบน เราทำให้ดูในกรณีที่จำนวนสมาชิกไม่เท่ากัน โดยเราจะเห็นว่า จริง ๆ แล้วมันไม่ได้แคร์จำนวนของสมาชิก หรือความยาวของ String ใด ๆ ทั้งสิ้น เพราะจริง ๆ แล้วมันจะค่อย ๆ เทียบทีละตัวไปเรื่อย ๆ จนได้ผลลัพธ์ออกมาว่า เป็นมากกว่าหรือน้อยกว่า หรือฝั่งใดฝั่งหนึ่งหมดก่อนมันจะออกมาเป็นเท่ากับ อย่างในกรณีนี้ เราจะเห็นว่า 1 ที่อยู่ใน List ด้านซ้ายมันมาก่อน 2 ที่เป็นตัวแรกของฝั่งขวา เลยทำให้ มันเป็นจริงนั่นเอง และแน่นอนว่า การเปรียบเทียบแบบนี้ เราสามารถใช้ได้กับ Tuple ก็ได้ด้วยเช่นกัน

>>> [1, 'a'] < [1, 'b']
True

อย่างที่เราบอกว่า มันจะเปรียบเทียบทีละตัวไปเรื่อย ๆ เราลองมาดูเคสที่ซับซ้อนมากกว่านี้กัน เราบอกว่า ใน List เรามี Member ที่เป็นคนละ DataType กัน ตัวนึงเป็น Integer และอีกตัวเป็น String โดยมันจะเริ่มเทียบจากตัวแรก ก็คือ 1 ของทั้ง 2 ด้าน มันเท่ากัน เลยผ่านไป ที่ตัวต่อไป คือ a และ b ซึ่งแน่นอนว่า b มาทีหลัง ทำให้ใน List นี้ฝั่งขวาเลยมากกว่าฝั่งซ้าย ทำให้มันเป็นจริงนั่นเอง

>>> [1, 3] < [1, 'b']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'

ไหน ๆ เราก็พูดถึงการเปรียบเทียบกับ List ที่มีหลาย ๆ DataType หนึ่งในอีกเคสที่เราเจอได้คือ เมื่อ DataType มันไม่เหมือนกัน อย่างที่บอกว่า 1 ของทั้ง 2 ฝั่ง ไม่ใช่ปัญหาเพราะมันเท่า แต่ปัญหามันจะไปอยู่ที่ 3 ของฝั่งซ้าย และ b ของฝั่งขวา เมื่อมันเปรียบเทียบกัน ตัว Python มันเทียบไม่ได้ ทำให้เจอ Error อย่างที่เห็นด้านบน

ดังนั้น ถ้าเราต้องการที่จะเปรียบเทียบ List หรือ Tuple ที่สมาชิกสามารถมีได้มากกว่าหนึ่ง DataType เราอาจจะต้องมีการระวังในเรื่องนี้ด้วย ไม่งั้น เราจะมีโอกาสเจอ Error แบบด้านบนได้ แนะนำว่าให้แปลงให้เป็นสัก DataType นึงที่เหมาะกับข้อมูลของเรามากที่สุดจะได้ไม่แตก

สรุป

การใช้ Comparison Operator กับพวก Iterable ต่าง ๆ ก็ถือว่าเป็น Shorthand ที่ทำให้เวลาเราเขียน Script ออกมา มันสั้นลงได้ ก็ตามหน้าที่ของ Shorthard แหละ นอกจากนั้นมันยังลดความซับซ้อนในการเขียน โดยเฉพาะใน Python เองที่เราสามารถทำ Comparison Operation Chaining หรือก็คือ การเทียบไปเรื่อย ๆ เช่น 1 > 2 > 3 ได้ อย่างใน Set เองถ้าเราเจอพวก Chaining แล้วหาพวก subset เราก็ต้องพ่วง issubset() ไปเรื่อย ๆ มันก็ไม่น่าอ่านเท่าไหร่ อ่าน ๆ ไป งง อ้าว อันไหนเริ่มก่อนหลังอะไรยังไง ดูไม่น่ารักเลย พวกนี้การใช้ Comparison Operator มันช่วยมาก ๆ แต่สำหรับในกรณีทั่ว ๆ ไป เรามองว่า ใช้เป็นพวก issubset() อะไรพวกนั้นเหมือนเดิม เข้าใจง่ายกว่าเยอะ แต่สำหรับ List และ Tuple เอง การใช้พวกนี้ เราว่ามันทำให้เราทำงานง่ายขึ้นมากจริง ๆ ก็ลองเอาไปใช้กันได้ ถือว่าเป็นทริกเล็ก ๆ น้อยใน Python

Read Next...

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

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

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

ปั่นความเร็ว Python Script เกือบ 700 เท่าด้วย JIT บน Numba

ปั่นความเร็ว Python Script เกือบ 700 เท่าด้วย JIT บน Numba

Python เป็นภาษาที่เราใช้งานกันเยอะมาก ๆ เพราะความยืดหยุ่นของมัน แต่ปัญหาของมันก็เกิดจากข้อดีของมันนี่แหละ ทำให้เมื่อเราต้องการ Performance แต่ถ้าเราจะบอกว่า เราสามารถทำได้ดีทั้งคู่เลยละ จะเป็นยังไง เราขอแนะนำ Numba ที่ใช้งาน JIT บอกเลยว่า เร็วขึ้นแบบ 700 เท่าตอนที่ทดลองกันเลย...

Humanise the Number in Python with "Humanize"

Humanise the Number in Python with "Humanize"

หลายวันก่อน เราทำงานแล้วเราต้องการทำงานกับตัวเลขเพื่อให้มันอ่านได้ง่ายขึ้น จะมานั่งเขียนเองก็เสียเวลา เลยไปนั่งหา Library มาใช้ จนไปเจอ Humanize วันนี้เลยจะเอามาเล่าให้อ่านกันว่า มันทำอะไรได้ แล้วมันล่นเวลาการทำงานของเราได้ยังไง...

ทำไม 0.3 + 0.6 ถึงได้ 0.8999999 กับปัญหา Floating Point Approximation

ทำไม 0.3 + 0.6 ถึงได้ 0.8999999 กับปัญหา Floating Point Approximation

การทำงานกับตัวเลขทศนิยมบนคอมพิวเตอร์มันมีความลับซ่อนอยู่ เราอาจจะเคยเจอเคสที่ เอา 0.3 + 0.6 แล้วมันได้ 0.899 ซ้ำไปเรื่อย ๆ ไม่ได้ 0.9 เพราะคอมพิวเตอร์ไม่ได้มองระบบทศนิยมเหมือนกับคนนั่นเอง บางตัวมันไม่สามารถเก็บได้ เลยจำเป็นจะต้องประมาณเอา เราเลยเรียกว่า Floating Point Approximation...