The Power of Any / All

The Power of Any / All

วันที่ 1 ของ #PythonTricksEveryday นะครับ วันนี้เปิดด้วยหนึ่งในเทคนิคที่ พอรู้แล้วก็ใช้ได้ตลอดเลย มันคือ built-in function ที่ชื่อว่า all() และ any() ครับ ไปดูสถานการณ์เลยดีกว่า

สถานการณ์ที่เรามักจะเจอกันบ่อยๆ คือ เราต้องเขียน if ที่มี condition เยอะมากๆ อย่างในตัวอย่างนี้ก็ 5 conditions ซึ่ง บางทีรูปแบบที่มันอยู่อาจจะอยู่ยากกว่านี้ก็ได้ เช่น เป็น if แยกกันหลายๆ อันหรือ if ซ้อนใน if ให้เราลองพยายาม normalize ให้มันเหลือ if เดียวแล้วให้ความสัมพันธ์ของมันเหลือแค่ and / or

พอเรา normalize ความสัมพันธ์ของแต่ละ condition จนเหมือนภาพข้างบนแล้ว สิ่งที่เกิดขึ้นก็คือ Linter ด่าครับ ยาวเกิน 80 บรรทัดอะไรก็ว่าไป ซึ่งเราก็จะต้องตบมันเข้ามาให้ไม่เกิน 80 บรรทัด โค้ดข้างล่างนี้ผมใช้ Black ช่วย Format ให้นะครับหน้าตาก็จะประมาณนี้

โอเค อ่านง่ายกว่าตะกี้หน่อยๆ พอเราเห็นแบบนี้ปั๊ป เห้ยนี้มัน List นี่นา เราต้องทำอะไรบางอย่างเกี่ยวกับ List ได้สิ ถ้าเราเอา and / or ออกไปอะไรจะเกิดขึ้นน้า

สวัสดี All / Any

all() / any() น่าจะเป็น built-in function ที่อยู่คู่บุญ Python มานาน นานแค่ไหนผมก็ไม่รู้เหมือนกัน เพราะตั้งแต่ผมมาเขียนมันก็มีให้ใช้แล้ว แต่เพิ่งมาตระหนักว่ามันใช้อย่างนี้ได้ก็ปีที่แล้วนี่เอง ตัว function มันจะทำงานแบบนี้ครับ (method เอามาจาก Official document นะครับ)

ตัว Signature ของทั้งสอง function นั้นรับ Iterable object เหมือนกันครับ ซึ่งถ้าไม่ใช่ object ที่เรา implement เองแล้วมี __iter__ กับ next() method เราก็ใช้ List / Tuple ธรรมดาๆ ได้ครับ

คำอธิบายของ All คือ จะ return True ถ้าทุกค่าเป็น True หรือ iterable เราเนี่ยส่งมาเป็นค่าว่าง ส่วน Any จะสลับกันคือ จะ return True ถ้ามี element อย่างน้อยตัวนึงมีค่าความจริงเป็น True ถ้า iterable ว่างจะ return False

ทีนี้เราจะเอามันมาใช้กับ if ยาวๆ เราได้ไงครับ ไปดูตัวอย่างข้างล่างได้เลย

จะเห็นว่าแทนที่ผมจะเชื่อมแต่ละ condition ด้วย and / or ผมก็ยัดมันใส่ List / Tuple เอาไว้เลย แล้วตรง if ผมก็ใช้ all ครอบ List / Tuple ของ condition เอา แค่นี้ก็เรียบร้อยครับ

ข้อควรระวัง

เพราะว่าเราย้าย expression ไปไว้ใน predicates List / Tuple แล้วจะมีปัญหาใหม่ตามมาคือ ถ้า if ที่เราเขียนตอนแรกเราอิง lazy evaluation หมายถึง ถ้าพังตัวแรกแล้วดีดออกจาก if เลย ซึ่งส่วนใหญ่จะเป็นเคส and ถ้า condition ที่ตามมา depends กับ condition ก่อนหน้า โค้ดเราจะระเบิดทันทีครับ

จากตัวอย่างนะครับ สมมติเรามีสถานการณ์คล้ายๆ แบบนี้คือ is_customer_active() และ is_vip_customer() ต้องการ customer_id ไม่งั้นระเบิดทั้งคู่ เราสามารถ move lazy evaluate มาเช็คใน predicate ก่อนได้ครับอย่างเช่น customer_id and is_customer_active(customer_id) ถ้า customer_id ไม่มีค่าเนี่ย expression นี้ก็จะเป็น False ไปเลย

จบแล้วครับ ข้อควรระวังอีกข้อนึงที่อยากฝากไว้คือ เวลาเรารู้เทคนิคใหม่ๆ แล้ว พอเราพยายามเอาไป Apply กับปัญหาที่ตัวเองใช้ อย่าลืมย้อนกลับมาดูด้วยนะครับว่า ถ้าเทียบกันแล้ววิธีใหม่กับวิธีเก่า เราหรือทีมโอเคกับแบบไหนมากกว่ากัน มันไม่มีวิธีไหนที่แก้ปัญหาได้ทุกอย่างครับ เหมือนกับ any / all ก็ไม่เหมาะกับบาง Use case เพราะฉะนั้น ใช้ด้วยความระมัดระวังนะครับ


Original post at: https://yothinix.medium.com/the-power-of-any-all-ba219ef5c6cf