ทำความเข้าใจกับ Python Decorators

Python decorators เป็น feature หนึ่งของภาษา Python ซึ่ง feature นี้ไม่ใช่ Decorator pattern นะครับ เพียงแต่ว่าสามารถนำไปใช้ทำ pattern แบบนั้นได้

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

สมมุติว่าเรามีฟังก์ชั่น fizzbuzz ตามนี้

def fizzbuzz(number):
    if number % 3 == 0 and number % 5 == 0:
        return 'fizzbuzz'
    elif number % 3 == 0:
        return 'fizz'
    elif number % 5 == 0:
        return 'buzz'
    else:
        return number

แล้วเราอยากจะให้แสดงผลออกมาแบบมีข้อความขึ้นสักหน่อยเช่น Your input is 3, so you get fizz. ถ้าเราสะดวกเราก็เข้าไปแก้ฟังก์ชั่นของเราเลยใช่ไหมครับ เราก็จะได้ฟังก์ชั่นหน้าตาแบบนี้

def fizzbuzz(number):
    result = 'Your input is ' + str(number) + ', so you get '
    if number % 3 == 0 and number % 5 == 0:
        result += 'fizzbuzz'
    elif number % 3 == 0:
        result += 'fizz'
    elif number % 5 == 0:
        result += 'buzz'
    else:
        result += str(number)
    return result + '.'

ดูแล้วไม่ค่อยน่าทำเลยใช่ไหมครับ? เรามาลองใช้ decorator กันดีกว่า ให้เราสร้าง nested function หรือ inner function  ขึ้นมาตามนี้ (ที่ต้องทำตามนี้เพราะว่านี่เป็น syntax ของ Python decorator ครับ)

def have_nice_message(func):
    def wrap(number):
        result = 'Your input is ' + str(number) + ', so you get '
        result += func(number)
        result += '.'
        return result
    return wrap

วิธีใช้งานเราก็แค่เอาไปแปะไว้บนหัวของฟังก์ชั่นที่เราต้องการตามนี้

@have_nice_message
def fizzbuzz(number):
    if number % 3 == 0 and number % 5 == 0:
        return 'fizzbuzz'
    elif number % 3 == 0:
        return 'fizz'
    elif number % 5 == 0:
        return 'buzz'
    else:
        return number

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

เป้าหมายพัฒนาตัวเองในปี 2015 และสรุป..

ดูจากสรุปปีที่แล้วทำไว้ไม่ค่อยดีเท่าไหร่ รู้สึกเกินตัวไปหน่อย ปณิธานปีนี้เอาแค่ 3 ข้อพอ! สู้ๆ

  1. อย่างน้อยต้องเอา Verified Certificate ของ  Intro to Machine Learning ส่วนคอร์สที่เกี่ยวข้องอื่นๆ ถือเป็นโบนัส 🙂
  2. อ่าน Data Science for Business ให้จบ
  3. ปั่นจักรยานให้ครบ 3,000 km

วันนี้วันที่ 1 ม.ค. ปี 2016.. สรุปเป้าหมายของปีที่แล้วได้ว่า.. ไม่ผ่านสักอย่าง 🙂

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

สรุปผลการพัฒนาตัวเองในปี 2014

จากที่ตั้งไว้ 5 ข้อ..

  1. Learn to be Data Scientist.
    เรียนได้ทุกคอร์สจริง แต่ว่าไม่จบสักคอร์สตามที่หวังไว้ แล้วก็ไม่ค่อยได้ตั้งใจเท่าไหร่ ไม่ผ่าน
  2. Learn Ruby more.
    ที่ว่าจะลองพัฒนา Web app ขึ้นมาก็ไม่ได้ทำ ไม่ผ่าน
  3. Stay healthy.
    ปั่นจักรยานให้ครบ 5,000 km? ปั่นไปได้แค่ 1,651.94 km วิ่งไปแค่ 35.53 km เศร้าจริง ไม่ผ่าน
  4. Read at least two text books.
    ได้อ่านหลายเล่ม แต่ก็ไม่จบสักเล่ม ไม่ผ่าน
  5. Give at least 5 public talks.
    ข้อนี้ควบคุมยากหน่อยเพราะมีปัจจัยภายนอกมาเอี่ยว แต่ก็ได้พูด 3 เรื่อง ถือว่า ผ่าน เป็นกำลังใจเนอะ 🙂
    1) Scrum at Pronto Marketing ที่ ม.เกษตร ท่าน อ.​มะนาวเชิญไป
    2) Pronto R&D Presentation ที่ ม.กรุงเทพ
    3) Achieving "Zero Downtime Deployment" with Automated Testing ที่งาน BugDay Bangkok 2014 จัดที่ Software Park แจ้งวัฒนะ

Pattern หนึ่งในการทำ Puppet Deployment

ช่วงนี้มีโอกาสได้ลองเริ่มใช้ Puppet กับงานที่บริษัทแบบจริงๆ จังๆ แล้วก็กำลังคิดถึงวิธีที่จะเอาสคริป Puppet ของเราไปวางไว้ที่ตัว Master สเต็ปที่คิดว่าจะทำคือ

  1. เขียนโค้ด รันเทสบนเครื่องให้เสร็จ
  2. Push เข้า Github
  3. ให้ CI รันเทสให้อีกที
  4. เอาสคริปสำหรับการ Deploy เข้า Puppet Master ไปวางไว้ใน CI แล้วให้ CI เป็นคนรัน

ไม่ค่อยแน่ใจว่าชาวบ้านทำกันแบบนี้หรือเปล่า เลยลองกูเกิ้ลเรื่อยๆ อยู่หลายวันก็ไม่ค่อยเจอคนบอกรายละเอียดสักเท่าไหร่ ส่วนใหญ่จะเจอแต่คำว่า Deploy แต่ไม่ค่อยเจอว่าเค้าทำกันอย่างไร วันนี้ว่างจัดเลยนั่งเคลียร์อีเมลที่ดองไว้ แล้วก็เจอเมลของ Sysadmin Casts ข้างในเนื้อหามีหัวเรื่องว่า Git to Puppet Deployment workflow ลองเข้าไปดูแล้วก็มั่นใจว่าที่เราคิดไว้ก็ถือว่าเป็น Pattern หนึ่งเหมือนกัน 🙂 ลองดูรูปข้างล่างนี้

Episode #33 - Git to Puppet Deployment Workflow
Git to Puppet Deployment Workflow (Credit: https://sysadmincasts.com/)

Pattern ที่เค้าทำ (เจ้าตัวก็บอกว่าไม่รู้ผิดหรือเปล่าเหมือนกัน) ก็คล้ายๆ กับที่คิดไว้ แต่ของเค้าจะใช้ Hook แทนเป็นตัวคอยรันเทสแล้วก็เอาโค้ดเข้า Puppet Master

เป้าหมายพัฒนาตัวเองในปี 2014

ปณิธานของปีนี้ เอา 5 ข้อพอ (น้อยหน่อยเพราะตั้งใจจะทำให้ครบจริงๆ)

  1. Learn to be Data Scientist.
    จะเรียน Data Science & Big Data track ให้ครบทุกคอร์ส 🙂
  2. Learn Ruby more.
    จะลงมือทำ ลองพัฒนา Web app ขึ้นมาจริงๆ จังๆ เพราะตอนนี้รู้สึกว่ายังเขียนได้ไม่คล่องเท่ากับ PHP และ Python
  3. Stay healthy.
    จะปั่นจักรยานให้ครบ 5,000 km
  4. Read at least two text books.
    ต้องอ่าน Doing Data Science กับ Agile Data Science ให้จบให้ได้
  5. Give at least 5 public talks.
    ความรู้อยู่กับตัวเองคนเดียวไม่มีประโยชน์ ต้องเผยแพร่ให้คนอื่นได้รับรู้ ในปณิธานข้อนี้จะเป็นการฝึกพูดไปด้วยในตัว ขอยึดบทความของ Hilary Mason เป็นแรงบันดาลใจในการฝึกฝน Speaking: Spend at least 1/3 of the time practicing the talk