Tutorial

pipe Python Package ที่ทำให้ Code น่ารักขึ้นเยอะ

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

pipe Python Package ที่ทำให้ Code น่ารักขึ้นเยอะ

เมื่อไม่กี่วันก่อนระหว่างเปิดหาอะไรในเน็ตไปเรื่อยก็ไปเจอกับของเล่นใหม่เข้าโดยบังเอิญ แล้วพอเอามาลองใช้จริง ๆ เออ มันก็แปลก ๆ ดีนะ เวลาเขียนแล้วมัน งง ๆ ว่าเอ๊ะอิหยังวะอยู่พอสมควรเลย นั่นคือ Package ที่ชื่อว่า pipe ใช่แล้ว มันคือ pipe เดียวกับที่เราใช้ใน Command Line สำหรับเอาผลของ Expression ก่อนหน้าไปใส่เป็น Input ของตัวต่อไปนั่นเองเหมือนกันเลย โดยที่ใน Package นี้ตัวอย่างที่เราว่าใช้งานได้โหด ๆ เลยคือการทำงานกับพวก List ทั้งหลาย ยิ่งถ้าข้อมูลเราเยอะ ๆ ต้องทำ Operation หลาย ๆ ตัวเราว่าโคตรเหมาะเลย ทำให้ง่ายกว่าเดิมเยอะมาก

ติดตั้ง pipe

pip install pipe

การติดตั้ง pipe ทำได้ไม่ยากเลย เราสามารถใช้ pip ได้เลย สำหรับคนที่ใช้ conda เราลองแล้วมันหา Package ไม่เจอ เราก็ทำการ Install ผ่าน pip ได้เช่นกันจากคำสั่งด้านบน

from pipe import where

เพื่อเป็นการเช็คว่าเราติดตั้งผ่านแล้วจริง ๆ เราอาจจะลอง Import มันเข้ามาก็ได้ ถ้าไม่มีปัญหาอะไรก็คือ การติดตั้งเรียบร้อยดีไม่มีปัญหา

Your First Pipe

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 ทั้งหมดออกมาพร้อมใช้งานต่อได้เลย สะดวกมาก ๆ เลยชั่ยมั้ยล้าาาา

Handling Fu_king Large Data with 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 มาก ๆ ในการทำงาน

Groupping Data

อีกเคสที่เราว่าน่าสนใจคือ การ 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 ต่าง ๆ มันเป็นอะไรที่น่าสนใจมาก ๆ

Read Next...

การสร้าง 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...

Synology NAS และ SSD Cache จำเป็นจริง ๆ เหรอ เหมาะกับระบบแบบใด

Synology NAS และ SSD Cache จำเป็นจริง ๆ เหรอ เหมาะกับระบบแบบใด

ใน Synology NAS มีความสามารถนึงที่น่าสนใจคือ การใช้ SSD เป็น Cache สำหรับระบบ ที่ทำให้ Performance ในการอ่านเขียน เร็วขึ้นกว่าเดิมมาก ๆ แน่นอนว่า เราลองละ วันนี้เราจะมาเล่าให้อ่านกันว่า หากใครคิดที่จะทำ มันเหมาะ หรือ ไม่เหมาะกับการใช้งานของเรา...

ฮาวทูย้าย Synology Add-on Package ไปอีก Volume

ฮาวทูย้าย Synology Add-on Package ไปอีก Volume

เรื่องราวเกิดจากการที่เราต้องย้าย Add-on Package ใน DSM และคิดว่าหลาย ๆ คนน่าจะต้องประสบเรื่องราวคล้าย ๆ กัน วันนี้เราจะมาเล่าวิธีการว่า เราทำยังไง เจอปัญหาอะไร และ แก้ปัญหาอย่างไรให้ได้อ่านกัน...