Tutorial

เขียน Python Script อย่างไรให้ไม่โดนเพื่อนร่วมงานโยกหน้าให้

By Arnon Puitrakul - 21 กุมภาพันธ์ 2022 - 1 min read min(s)

เขียน Python Script อย่างไรให้ไม่โดนเพื่อนร่วมงานโยกหน้าให้

การเขียน Code เรามองว่ามันคล้าย ๆ กับการเขียนหนังสือ เราเขียนด้วยลายมือชุ้ย ๆ ถามว่า ข้อมูลมันก็อยู่ในสมุดที่เราเขียนแหละ แต่ถามว่า ตอนเรากลับมาอ่านมัน เราก็ต้องใช้เวลาแกะแคะเกลาสารพัดเรื่องไปหมด เหมือนที่หลาย ๆ คนชอบพูดว่า ถ้าให้เรากลับไปอ่าน Code ที่เราเขียนเมื่อสัก 10 ปีก่อน ก็น่าจะ งง ไม่น้อย เทียบกับคนที่เขียนลายมือดี ๆ มันก็จะอ่านง่าย กลับมาอ่าน ก็ไม่ต้องใช้เวลาเยอะ เราก็อ่านได้ตรง ๆ เลย การเขียนโปแกรมมันไม่ต่างกัน

ในวันนี้เราจะมาแนะนำวิธีการเขียนเบื้องต้น เขียนอย่างไรไม่ให้เพื่อนร่วมงานที่มาอ่าน Code ของเราแล้วไม่เอาหมัดมาโยกหน้าเรา เพราะเขียนเ_ย อะไรของ _ง เนี่ย ไ_สั_ !!

Python มี Code Standard อยู่แล้ว

จริง ๆ หลาย ๆ คนอาจจะยังไม่ทราบว่า Python เองเขามี Code Standard อยู่แล้วนะว่า เราควรจะเขียน Code และ จัดระเบียบ Code อย่างไร ลองไปอ่านได้ใน PEP 8 อาจจะยาวไปหน่อย แต่เราแนะนำให้ลองอ่านดู เพราะมันจะช่วยเวลาเราทำงานกับคนอื่นได้เยอะมาก ๆ ถ้าทั้งโลกเขาตามนี้หมด เราไม่ตาม เราเขียนแล้วเรียงแปลก ๆ เพื่อนร่วมงานน่าจะมีความอยากโยกหน้าเราไม่มากก็น้อยแหละ

โดยเฉพาะในภาษา Python เองที่มีความยืดหยุ่นสูงมาก ๆ ในแง่ของการเขียนโปรแกรมหลาย ๆ Paradigm มาก ๆ ถ้าเราไม่ควบคุม หรือเราไม่รู้ ส่วนใหญ่เราจะเจออะไรแปลก ๆ มาก ๆ อ่านแล้วจะร้อง อิหยังว้าาาา ทันที เราว่าต้องเจอกันมาบ้างแหละ หรือไม่ก็ Code ตัวเองนี่แหละ

Indentation

if a.test() :
    do_process()

เรื่องแรกสำคัญมาก ๆ คือ Indentation หรือการเว้นนั่นเอง เพราะ Python เป็นภาษาที่ผูกชีวิตกับ Indentation สูงมาก ๆ เช่น ถ้าเราต้องการจะเขียน เงื่อนไข, Loop และ Function ต่าง ๆ เราก็จำเป็นที่จะต้องใช้ Code Block ซึ่งมันใช้ Indentation ในการคั่นหมดเลย

ซึ่งถ้าเราเรียน Python มาตามปกติเลย เราจะรู้กันว่า การ Indent จริง ๆ คือเราจะใช้ 1 Tab แล้วถ้ามันเป็น Nested เราก็ทำลึกลงไปเรื่อย ๆ ทีละ 1 Tab แต่หารู้หรือไม่ว่า จริง ๆ แล้ว 1 Tab ในบาง OS มันขนาดไม่เท่ากัน บ้างก็เท่ากับ 3 Space บ้างก็ 4 Space หรือถามยันกระทั่งว่า แล้วถ้าเราไม่อยากด Tab เรากด Space Bar แทนได้มั้ย

คำตอบจริง ๆ คือได้ โดยมาตรฐานแล้ว บน Python เราจะกำหนดให้ 1 Tab = 4 Space ดังนั้น ถ้าเราไม่อยากกด Tab เราสามารถกด 4 Space แทนได้ เช่น ถ้าเราอยู่ใน Nested Block ชั้นที่ 2 เราก็กดไป 4 คูณด้วย 2 = 8 Space นั่นเอง

if a.test() :
    do_process()
else :
   b.test()

และ อยากให้ระวัง เพราะว่า ถ้าเราใช้ 1 Tab ไม่เท่ากัน ดังตัวอย่างด้านบน บน Block แรกของ IF เลย เราใช้ 4 Space แต่ Block ด้านล่าง เราใช้ 3 Space ถ้าเราทำแบบนี้ไปโปรแกรมจะ Error เลยเพราะมันจะ งง ว่า เอ๊ะ มัน Block ไหนอะไรกันแน่นั่นเอง

หรือบางครั้ง เราไม่ได้พิมพ์ Space เลยนะ เรากด Tab อย่างเดียวเลย มันก็มีโอกาสเกิดขึ้นเหมือนกันนะ เพราะอย่างที่บอกว่า Tab บาง OS ไม่เท่ากับชาวบ้าน ดังนั้น เราแนะนำว่า ถ้าเราไปทำงานในเครื่อง หรือ OS ที่ไม่แน่ใจ ให้เราลองกด Space 4 ครั้ง เทียบกับ 1 Tab ก่อนว่า มันเท่ากันมั้ย ถ้าเท่าก็ใช้ Tab ได้เลยเพื่อความเร็ว แต่ถ้าไม่ เราก็ต้องเข้าไปเซ็ตใน Editor ของเรา ไม่ก็ยอมกด Space ไปก็จะชัวร์กว่าเยอะ

Line Length

อันนี้เป็นเรื่องที่หลาย ๆ คนอาจจะยังไม่ทราบ คือเรื่องของความกว้างบนบรรทัดก็สำคัญเช่นกัน บางครั้งเราจะเจอเขียน Code ได้ยาวมาก ๆ อันที่เจอบ่อย ๆ เลยน่าจะเป็นเวลาเราเขียนเงื่อนไข แล้วเงื่อนไขของเรามันต่อกันเยอะมาก ๆ เช่น and กันเป็น 4-6 อันเลย ก็คือยาวถึงโลกหน้าเลย

ปัญหาของความยาวแบบนี้คือ แมร่ง อ่าน ยาก ! จบนะ มันอ่านยากจริง ๆ ถ้าเราใช้งาน Editor ที่มันสามารถเลื่อนซ้ายขวาได้ การจะอ่านให้จบ การจะแก้ เราก็ต้องเลื่อนไปอ่าน หรือถ้าเราเจอกับ Editor ที่ไม่ให้เลื่อนซ้ายขวา มันจะ New Line ลงมาให้อัตโนมัติเลย ทำให้เราอาจจะเอ๊ะ ว่า เด่วนะ เราไป New Line เมื่อไหร่ฟร๊ะ ดังนั้น Line Length สำคัญมาก ๆ

โดยปกติแล้วเราจะใช้กันอยู่ไม่เกิน 79 Character ต่อบรรทัด อันนี้รวมพวก Space แล้วนะ คือทั้งบรรทัดรวมทุก ๆ อย่างจริง ๆ ไม่เกิน 79 อักษรด้วยกัน ถ้ามันเกิน เราจะใช้วิธีการ New Line มันลงมา

def calculate_position (observation_a_x,
                        observation_a_y,
                        observation_b_x,
                        observation_b_y,
                        observation_c_x,
                        observation_c_y)
                        
    // do some calculation here
    return cal_position_x, cal_position_y

จากตัวอย่างด้านบน เราจะเห็นว่า ถ้าเรา Define Function ด้วย 1 บรรทัดคือ กำหมัดแน่นอน เพราะตัวแปรเยอะมาก ๆ และ แต่ละตัวชื่อยาวมาก ๆ เกิน 79 Character แน่นอน ดังนั้นสิ่งที่เราทำคือ เราจะ New Line มันลงมาเลย แต่ ๆ เพื่อความอ่านง่ายมากขึ้น เราจะใช้ Indentation ในการ Align ตัวแปรให้ตรงกัน เวลาเรามาอ่าน เราจะเห็นว่ามันอ่านง่ายกว่าเยอะเลยใช่มั้ยฮ่ะ

Specificity Naming

def a (b) :
    r = 0
    for i in range(b + 1) :
        r += i
    return r

เคสนี้ เราเจอเยอะมาก ๆ โดยเฉพาะมือใหม่ที่พึ่งมาเขียนโปรแกรมครั้งแรก ๆ หรือคนที่เขียนมาเยอะแหละ แต่ตรงที่มันทำงานง่าย ๆ โง่ ๆ เวลาตั้งชื่อตัวแปร จะตั้งชื่อที่ไม่มีความหมายอะไรเลย เช่น Code ด้านบน ถ้าเราอ่านผ่าน ๆ เลย เราจะไม่รู้เลยว่ามันทำอะไร

def sum (length):
    result = 0
    for i in range(length + 1) :
        result += i
    return result

เทียบกับ Code ด้านล่าง มันเป็น Code ที่ทำเหมือนกันทุกอย่างอย่างเลย แต่เราจะเห็นว่า ถ้าเราอ่านผ่าน ๆ เลย เราจะรู้ทันทีว่า Function นี้มันทำอะไร ดังนั้นการตั้งชื่อ ให้มัน Specific เป็นเรื่องสำคัญมาก ๆ และหลาย ๆ คนละเลย ลดโอกาสการโดนโยกหน้าไปอีกหนึ่ง

Letter Case is IMPORTANT!

อันนี้หลาย ๆ คนที่เขียนโปรแกรมน่าจะพอทราบอยู่แล้วว่า แต่ละภาษาเขาจะมีวิธีการตั้งชื่อไม่เหมือนกัน ในภาษา Python เองเราจะใช้ทั้ง Snake Case และ Pascal Case

def sum_number (length) :
    sum_result = 0
    for i in range(length + 1) :
        result += i 
    return sum_result

ตัวอย่างแรกคือการใช้ Snake Case จะเป็นการใช้ตัวพิมพ์เล็กทั้งหมดแล้วคั่นด้วย Underscore บน Python เราจะใช้กับ ชื่อของตัวแปร และ ชื่อของ Function เท่านั้น

class Person :
    def __init__ (self, name) :
        self.name = name

แต่สำหรับชื่อ Class เราตจะใช้เป็น Pascal Case หรือก็คือการใช้ตัวใหญ่ที่ตัวแรกของคำ เช่น People และ DataPoint เป็นต้น ถามว่า ทำไมเราต้องทำแบบนี้ เพราะเวลาเราเขียนจริง ๆ ในภาษา Python มันให้กรรมสิทธิ์ Function เป็นตัวแปรแบบนึงเลย (ลองไปหาอ่านได้ เราสามารถ Pass Function เป็น Argument บน Function เองได้ด้วยละ) เลยทำให้เราใช้เป็น Snake Case เหมือนกันนั่นเอง แต่สำหรับ Class มันไม่ได้เป็นแบบนั้น เพื่อให้เราสามารถแยกได้ง่าย ๆ เขาเลยให้เราเขียนเป็น Pascal Case เหมือนกับภาษาอื่น ๆ อย่าง Java นั่นเอง

Comment ให้เหมือนคู่มือ IKEA

แก ๆ IKEA ไม่ได้จ่ายนะ แต่ ๆ ถ้าเราเปรียบโปรแกรมของเราเหมือนเฟอร์นิเจอร์ตัวนึง ถ้าเราสักแต่เขียนโปรแกรมออกมา แล้วเราไม่ได้บอกอะไรเลย โอเคแหละ เราสามารถเดาจาก Logic ที่เขียนไว้ได้ แต่ ถามว่า มันเสียเวลามั้ยอะ ที่ต้องมาแกะ มันจะดีกว่ามั้ยว่า เราจะ Comment บอกไว้สิว่า มันทำอะไร ตรงนี้คืออะไร วันนึง ไม่ว่าจะเป็นเราเอง หรือ เพื่อนร่วมงานเรามาแก้ จะได้อ่าน Comment แล้ว อ่อ มันคือตรงนี้นะอะไรแบบนั้น ถ้าให้ไปงมเอง อาจจะไปผิดที่ผิดทางแล้วนำความชิบหายสู่เราที่เป็นคนเขียนได้

แต่การเขียน Comment ที่ดี ไม่ใช่แค่เออ บอกว่า Function นี้มันเอาออะไรออกมาเท่านั้น ถ้าเทียบกับคู่มือประกอบเฟอร์นิเจอร์มันก็ไม่ต่างจากการที่ มันมีแต่รูปของเฟอร์ที่ประกอบเสร็จแล้วเท่านั้นแหละ ถามว่า ถ้าเราเจอคู่มือแบบนี้ เราจะอยากโยกหน้าคนเขียนมั้ยอะ นั่นสิ ดังนั้น Comment จะต้องบอกว่ามันทำอะไรบ้าง จนไปถึงปลายทางไม่ใช่มันให้อะไรกลับมา พวกนั้นไปอ่าน Documentation สิโว้ยยย

def calculate_position (observation_a_x,
                        observation_a_y,
                        observation_b_x,
                        observation_b_y,
                        observation_c_x,
                        observation_c_y)
    
    """
    This function calculate position by using True-range multilateration through 3 co-ordinates
    """
    
    // do some calculation here
    return cal_position_x, cal_position_y

อย่างด้านบน เราก็เขียน Comment บอกไว้เลยว่า Function นี้มันไว้ทำอะไร แล้วมันทำยังไง และได้อะไรออกมา อ่านแล้วก็ อ่ออออ มันทำแบบไอ้นี่นะ ไม่ต้องมานั่งเดาอะไรเยอะแยะไปหมด

สรุป

สิ่งที่เรายกมาในวันนี้เป็นเพียงส่วนหนึ่งของ PEP 8 เท่านั้น เพื่อทำให้ Code ของเราอ่านง่าย และเป็น Standard มากขึ้น ทำให้เราทำงานกับคนอื่นที่ใช้ภาษา Python เหมือนกันได้ง่ายขึ้น ถามว่า ถ้าเราอินดี้ ไม่อยากทำตามได้มั้ย ก็ได้ มันรันได้ ทำงานได้เลยละ หรือบาง Project หรือบางทีม จะไม่ทำตามได้มั้ย ก็ได้เหมือนกัน เพราะบางครั้ง เราเข้าใจว่ามันจะมี Special Case บางอย่างที่การเปลี่ยนนิดหน่อยมันทำให้ทำงานเร็วขึ้นได้ ก็ขึ้นกับการคุยกันในทีมเลยละกันเนอะ