By Arnon Puitrakul - 05 พฤศจิกายน 2021
เมื่อไม่กี่วันก่อนระหว่างเปิดหาอะไรในเน็ตไปเรื่อยก็ไปเจอกับของเล่นใหม่เข้าโดยบังเอิญ แล้วพอเอามาลองใช้จริง ๆ เออ มันก็แปลก ๆ ดีนะ เวลาเขียนแล้วมัน งง ๆ ว่าเอ๊ะอิหยังวะอยู่พอสมควรเลย นั่นคือ Package ที่ชื่อว่า pipe ใช่แล้ว มันคือ pipe เดียวกับที่เราใช้ใน Command Line สำหรับเอาผลของ Expression ก่อนหน้าไปใส่เป็น Input ของตัวต่อไปนั่นเองเหมือนกันเลย โดยที่ใน Package นี้ตัวอย่างที่เราว่าใช้งานได้โหด ๆ เลยคือการทำงานกับพวก List ทั้งหลาย ยิ่งถ้าข้อมูลเราเยอะ ๆ ต้องทำ Operation หลาย ๆ ตัวเราว่าโคตรเหมาะเลย ทำให้ง่ายกว่าเดิมเยอะมาก
pip install pipe
การติดตั้ง pipe ทำได้ไม่ยากเลย เราสามารถใช้ pip ได้เลย สำหรับคนที่ใช้ conda เราลองแล้วมันหา Package ไม่เจอ เราก็ทำการ Install ผ่าน pip ได้เช่นกันจากคำสั่งด้านบน
from pipe import where
เพื่อเป็นการเช็คว่าเราติดตั้งผ่านแล้วจริง ๆ เราอาจจะลอง Import มันเข้ามาก็ได้ ถ้าไม่มีปัญหาอะไรก็คือ การติดตั้งเรียบร้อยดีไม่มีปัญหา
from random import randint
from pipe import where
people_age = [randint(x, x+20) for x in range(80)]
adult_people = people_age | where (lambda x : x>18)
หลังจากเราติดตั้งแล้ว เราลองมาใช้งาน Pipe กันดีกว่า เราเริ่มจากคำสั่งที่ง่าย ๆ ก่อนละกันเป็นการเลือกข้อมูล โดยที่เราสามารถกำหนดเงื่อนไขได้ เหมือนกับเราเลือกข้อมูลใน SQL เลยอะไรแบบนั้น ในตัวอย่างด้านบน เราทำการสร้าง List ปลอม ๆ ขึ้นมาเหมือนกับเป็นอายุของคนละกัน จำนวน 80 คนด้วยกัน จากนั้น เราต้องการที่จะ Filter เพื่อเอาคนที่อายุ มากกว่า 18 ออกมา เราเลย Pipe แล้วให้มันวิ่งไปที่ where ในนั้น สิ่งที่เราใส่ลงไปก็จะเป็น Function สำหรับการเลือก ซึ่งในที่นี้คือ เราต้องการ Item ที่มากกว่า 18 โดยการใช้ Anonymous Function
adult_people = []
for person_age in people_age :
if person_age > 18 :
adult_people.append(person_age)
ถ้าเราไม่ใช้ Pipe และเขียนเป็น Loop ปกติ ก็น่าจะอารมณ์แบบตัวอย่างด้านบนเลย จะเห็นได้ว่าเขียนเยอะมาก ๆ ไม่ได้เป็น Functional มากสักเท่าไหร่ ไม่น่ารักเลย หรืออีกวิธี ถ้าเราไม่อยากเขียนยาว ๆ แบบนี้เราก็สามารถใช้ Map Function กับ Anonuymous Function ได้เหมือนกัน ถ้าอยากลองก็ไปลองกันได้เด้อ เราไม่ได้เขียนให้ดู
>>> adult_people
<generator object where.<locals>.<genexpr> at 0x100ae6c10>
กลับมาที่ Pipe ของเราต่อ เมื่อครู่ เราได้ Pipe เพื่อเลือกอายุที่มากกว่า 18 ออกมา แต่เมื่อเราเรียกเอ๋ ทำไมมันไม่เป็นเหมือนที่เราคิดละ ทำไมเราไม่ได้ List ของข้อมูลออกมาละ กลายเป็น Generator ซะงั้น ใช่แล้วฮ่ะ มันได้ออกมาเป็น Generator การใช้งานก็จะเหมือนการใช้ Generator ทั่ว ๆ ไปเลย มันก็มีข้อดีอยู่นะเอาจริง ๆ โดยเฉพาะเมื่อเราทำงานกับข้อมูลขนาดใหญ่ จะเก็บผลทั้งหมดลง Memory เลย ก็น่าจะไม่ไหวนะ เปลืองทั้งเงินซื้อ RAM และ เสียเวลามาก ๆ
>>> list(adult_people)
[20, 24, 26, 19, 19, 29, 24, 30, 20, 22, 29, 24, 35, 33, 27, 31, 32, 29, 30, 38, 35, 35, 34, 46, 34, 45, 35, 40, 36, 43, 53, 38, 54, 54, 49, 43, 54, 54, 58, 63, 54, 65, 67, 65, 54, 66, 57, 54, 61, 60, 71, 62, 68, 68, 64, 76, 83, 65, 77, 80, 80, 78, 73, 74, 76, 88, 78, 75, 85, 86, 89, 95, 89]
แต่ถ้าเรารู้อยู่แล้วว่าข้อมูลเราไม่ได้ใหญ่อะไรเลย เช่นในตัวอย่างของเราที่มีอย่างมากแค่ 80 ตัวเท่านั้น การเอามันออกมาทั้งหมดเลยไม่ใช่ปัญหาแน่นอน เราก็สามารถจับมัน Cast เป็น List แล้วเราก็จะได้ค่าใน Generator ทั้งหมดออกมาพร้อมใช้งานต่อได้เลย สะดวกมาก ๆ เลยชั่ยมั้ยล้าาาา
from random import randint
def age_generator (sizing) :
for _ in range(sizing) :
yield randint(10,70)
แน่นอนว่า เวลาเราทำงานบางอย่างที่ Data ขนาดมันไม่น่ารักเท่าไหร่ โหลดเข้ามาคือแตกแน่นอน เกิน RAM ไปไกล ดังนั้น เราจะต้องใช้เทคนิคอื่นในการจัดการ ซึ่งแน่นอนว่าถ้าใครเคยทำงานกับ Data ขนาดใหญ่มาก่อนวิธีนึงที่เราทำได้คือ การใช้ Generator สิ่งที่เราทำก็เหมือนเดิมเลย คือ เราทำการ Random อายุของคนขึ้นมา ผ่านการเขียน Generator ที่เราสามารถกำหนดจำนวนของคนที่เราต้องการลงไปได้ แล้วเราก็ For-Loop แล้วค่อย ๆ Yield อายุออกไปทีละรอบเลย
adult_people = age_generator(200) | where (lambda x : x>18)
จากนั้น แทนที่เราจะยัด List เข้าไปตรง ๆ เราก็ยัด Generator เข้าไปตรง ๆ ได้เลย เอาจริง ๆ มันต้องทำได้อยู่แล้วนะ เพราะความเป็นจริงแล้ว List และ Generator เป็นพวก Iterable อยู่แล้ว หมายความว่า มันเป็น Object ที่สามารถ Loop ใส่มันได้อะไรประมาณนั้น การใช้ท่า Generator ก็จะทำให้การทำงานกับ Data ขนาดใหญ่สบายขึ้นไปอีก
แต่ตัว Pipe ไม่ได้มี Built-in Function สำหรับการทำ Multiprocessing มาให้เราเลยนะ ถ้าอยากได้ เราก็สามารถเขียนเองได้เหมือนกัน เราว่าถ้าทำออกมาจริง ๆ มันจะ Powerful มาก ๆ ในการทำงาน
อีกเคสที่เราว่าน่าสนใจคือ การ Group Data จากเงื่อนไขต่าง ๆ เช่นถ้าเราทำงานกับ Data ที่ไม่ได้ใหญ่มากเท่าไหร่ อยากทำงานทดลองคิดไว ๆ การใช้ Pipe ก็ทำให้เราทดลองไอเดียได้เร็วมาก ๆ เหมือนกัน
filtered_data = age_generator(200) | groupby(lambda x: "Even" if x %2 == 0 else "Odd") | select(lambda x: {x[0]: list(x[1])})
print(list(filtered_data))
จากตัวอย่างด้านบน เราขอใช้ Generator เดิมเลยละกัน เป็น age_generator แต่หลัก ๆ ก็คือมัน Random ตัวเลขทั่ว ๆ ไปไม่มีอะไร เป้าหมายของเราคือ เราต้องการแยก เลขคู่ ออกจากเลขคี่ ซึ่งใน Pipe ก็จะมีคำสั่งชื่อ groupby มาให้เราใช้งานกัน ในนั้นเราก็เหมือนเดิมเลย ใช้ Anonymous Function ง่าย ๆ เช็คแค่ว่า Modulo กับ 2 แล้วมีเศษมั้ย แล้วก็แยกไปตามฟากได้เลย แต่ถ้าเราทำแค่นี้ เราจะได้ Dictionary ที่ Data เป็น Generator โดยเราสามารถเข้าไป Loop และแปลงเป็น List เองก็ได้ หรือเราก็ใช้ Pipe ให้เป็นประโยชน์ ก็แปลงมันไปเลยดิ แล้วค่อยแปลงอีกทีตอนเราจะใช้มันก็จะเรียกทั้งหมดเอง ง่ายมาก ๆ เลย
Pipe เป็น Package ที่มันเจ๋งอยู่นะ มันไม่ได้มาแทนที่สิ่งที่ Python ไม่เคยมี จริง ๆ แล้วทั้งหมดที่เราเล่ามาเราเขียน Python เปล่า ๆ เลยไม่ได้ลง Package อะไรเลยมันก็ทำได้ แต่แค่มันเสียเวลา กับมันดูรกเท่านั้นเอง ทำให้ประโยชน์ของ Pipe ที่เราว่ามันเจ๋งจริง ๆ คือ เรื่องของเวลา และ ความสะอาด เข้าใจง่ายของ Code ดูเป็น Functional มากขึ้นเท่านั้นเอง ซึ่งเราแนะนำเลย โดยเฉพาะถ้าเราต้องทำงานกับพวก Pipeline ต่าง ๆ มันเป็นอะไรที่น่าสนใจมาก ๆ
Obsidian เป็นโปรแกรมสำหรับการจด Note ที่เรียกว่า สารพัดประโยชน์มาก ๆ เราสามารถเอามาทำอะไรได้เยอะมาก ๆ หนึ่งในสิ่งที่เราเอามาทำคือ นำมาใช้เป็นระบบสำหรับการจัดการ Todo List ในแต่ละวันของเรา ทำอะไรบ้าง วันนี้เราจะมาเล่าให้อ่านกันว่า เราจัดการะบบอย่างไร...
อะ อะจ๊ะเอ๋ตัวเอง เป็นยังไงบ้างละ เมื่อหลายเดือนก่อน เราไปเล่าเรื่องกันขำ ๆ ว่า ๆ จริง ๆ แล้วพวก Loop ที่เราใช้เขียนโปรแกรมกันอยู่ มันไม่มีอยู่จริง สิ่งที่เราใช้งานกันมันพยายาม Abstract บางอย่างออกไป วันนี้เราจะมาถอดการทำงานของ Loop จริง ๆ กันว่า มันทำงานอย่างไรกันแน่ ผ่านภาษา Assembly...
นอกจากการทำให้ Application รันได้แล้ว อีกเรื่องที่สำคัญไม่แพ้กันคือการวางระบบ Monitoring ที่ดี วันนี้เราจะมาแนะนำวิธีการ Monitor การทำงานของ MySQL ผ่านการสร้าง Dashboard บน Grafana กัน...
จากตอนที่แล้ว เราเล่าในเรื่องของการ Harden Security ของ SSH Service ของเราด้วยการปรับการตั้งค่าบางอย่างเพื่อลด Attack Surface ที่อาจจะเกิดขึ้นได้ หากใครยังไม่ได้อ่านก็ย้อนกลับไปอ่านกันก่อนเด้อ วันนี้เรามาเล่าวิธีการที่มัน Advance มากขึ้น อย่างการใช้ fail2ban...