Python การใช้ฟังก์ชัน

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

การสร้างฟังก์ชัน

การสร้างฟังก์ชันในภาษา Python สามารถทำได้โดยการกำหนดคีย์เวิร์ด def ตามด้วยชื่อฟังก์ชัน ดังตัวอย่าง

def my_function():
    print("สวัสดีชาวโลก")
  • บรรทัดที่ 1 สร้างฟังก์ชันชื่อว่า my_function
  • บรรทัดที่ 2 โค้ดคำสั่งที่จะให้ทำงานภายในฟังก์ชัน

การเรียกใช้ฟังก์ชัน

การเรียกใช้ฟังก์ชัน ทำได้โดยการพิมพ์ชื่อฟังก์ชันตามด้วยวงเล็บ ดังตัวอย่าง

def my_function():
    print("สวัสดีชาวโลก")

my_function()
  • บรรทัดที่ 4 เรียกใช้ฟังก์ชันที่เราได้สร้างไว้

การใช้งาน Arguments

กรณีที่เราต้องการให้ฟังก์ชันที่สร้างขั้นมาสามารถรับข้อมูลจากภายนอกเข้าไปประมวลผลได้ด้วย เราสามารถส่งข้อมูลเข้าไปในฟังก์ชันโดยผ่านอากิวเมนต์ Arguments ซึ่งจะถูกกำหนดไว้ภายในวงเว็บหลังชื่อฟังก์ชัน และแต่ละฟังก์ชันสามารถอากิวเมนต์ได้หลายตัวเท่าที่เราต้องการ แต่ต้องคั่นอากิวเมนต์แต่ละตัวด้วยเครื่องหมายคอมม่า โดยมีรูปแบบ functionname(arg1, arg2, arg3) ดังตัวอย่าง

def fullname(firstname, lastname):
    print(firstname + " " + lastname)
  • บรรทัดที่ 1 สร้างฟังก์ชันชื่อว่า fullname โดยมีอากิวเมนต์ 2 ตัว คือ firstname และ lastname
  • บรรทัดที่ 2 สั่งให้ให้แสดงผลออกมาทางหน้าจอ

การเรียกใช้ฟังก์ชันที่มี Arguments

เมื่อต้องการเรียกใช้ฟังก์ชันที่มี Arguments เราต้องส่งข้อมูลเข้าไปตามจำนวนอากิวเมนต์ที่ฟังก์ชันนั้น ๆ ต้องการ เรียกข้อมูลที่ส่งเข้าไปว่า พารามิเตอร์ Parameters ดังตัวอย่าง

def fullname(firstname, lastname):
    print(firstname + " " + lastname)

fullname("Rhaenyra", "Targaryen")
  • บรรทัดที่ 4 เรียกใช้ฟังก์ชัน fullname โดยส่งพารามิเตอร์เข้าไป 2 ตัว คือ “Rhaenyra” และ “Targaryen” ตามจำนวนอากิวเมนต์ของฟังก์ชัน

ผลลัพธ์ที่ได้คือ

ตามคำสั่งการทำงานที่กำหนดไว้ภายในฟังก์ชันนั่นเอง

ข้อแตกต่างระหว่าง Arguments และ Parameters

ทั้งอากิวเมนต์ Arguments และ พารามิเตอร์ Parameters ใช้สำหรับสิ่งเดียวกัน แต่เรียกชื่อต่างกันตามจุดประสงค์ของการใช้งาน

  • อากิวเมนต์ Arguments เป็นชื่อเรียกตัวแปรที่สร้างไว้รองรับข้อมูลที่ส่งเข้ามาเมื่อเรียกใช้ฟังก์ชัน
  • พารามิเตอร์ Parameters คือข้อมูลที่ถูกส่งเข้าไปกำหนดค่าให้กับอากิวเมนต์เมื่อเรียกใช้ฟังก์ชัน

Arbitrary Arguments, *args

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

แต่ในกรณีที่เราไม่รู้ว่าฟังก์ชันต้องรับอากิวเมนต์กี่ตัว หรือจำนวนอากิวเมนต์ไม่แน่นอน สามารถแก้ปัญหาโดยการเติมเครื่องหมายดอกจันทน์ * ไว้หน้าชื่ออากิวเมนต์

ในกรณีนี้ฟังก์ชันจะรับอากิวเมนต์เป็นแบบ tuple และสามารถเข้าถึงข้อมูลภายในอากิวเมนต์นั้น ๆ ด้วยวิธีการเข้าถึงข้อมูลแบบ tuple ดังตัวอย่าง

def my_dragons(*dragons):
    print("My Dragons:")
    for dragon in dragons:
        print(dragon)

my_dragons("Syrax", "Caraxes", "Vermax", "Seasmoke", "Vhagar", "Dreamfyre", "Sunfyre", "Meleys")
  • บรรทัดที่ 1 สร้างฟังก์ชันชื่อว่า my_dragons โดยมีอากิวเมนต์เพียง 1 ตัว แต่เติมเครื่องหมาย * ไว้ข้างหน้าอากิวเมนต์ เพื่อให้อากิวเมนต์ดังกล่าวรับค่าเข้ามาในแบบ tuple
  • บรรทัดที่ 3-4 ใช้ลูป for เข้าถึงสมาชิกในตัวแปร dragons ทีละตัวแล้วแสดงผลออกมา
  • บรรทัดที่ 6 เรียกใช้ฟังก์ชัน my_dragons โดยส่งพารามิเตอร์เข้าไป 8 ตัว (กี่ตัวก็ได้ เพราะอากิวเมนต์เป็นแบบ Arbitrary Arguments)

ผลลัพธ์

อากิวเมนต์แบบ Keyword

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

def my_dragons(dragon2, dragon1, dragon4, dragon3):
    print("My second dragon is " + dragon2)

my_dragons(dragon1 = "Syrax", dragon2 = "Caraxes", dragon3 = "Vermax", dragon4 ="Seasmoke")
  • บรรทัดที่ 4 เรียกใช้ฟังก์ชัน my_dragons โดยส่งอากิวเมนต์เป็นแบบ key = value

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

การใช้งาน Arbitrary Keyword Arguments, **kwargs

ในกรณีที่ต้องการใช้งานอากิวเมนต์แบบ key = value แต่ไม่รู้ว่ามีกี่ตัว หรือไม่สามารถกำหนดล่วงหน้าได้ว่ามีกี่ตัว สามารถแก้ปัญหาได้โดยการใช้อากิวเมนต์แบบ Arbitrary Keyword Arguments โดยการเติมเครื่องหมายดอกจันทน์จำนวน 2 ตัว ** หน้าอากิวเมนต์

ในกรณีนี้ฟังก์ชันจะรับอากิวเมนต์เป็นแบบ Dictionary ดังตัวอย่าง

def person(**var):
    print(var["firstname"] + " " + var["lastname"])

person(firstname = "Rhaenyra", lastname= "Targaryen")
  • บรรทัดที่ 1 สร้างฟังก์ชันชื่อว่า person โดยกำหนดอากิวเมนต์แบบ Arbitrary Keyword Arguments **var
  • บรรทัดที่ 2 เข้าถึงดิกชันนารี var โดยระบุคีย์เวิร์ดเป็น firstname และ lastname เพื่อให้แสดงผลออกมา
  • บรรทัดที่ 4 เรียกใช้ฟังก์ชัน person โดยระบุอากิวเมนต์แบบ key = value

ผลลัพธ์

Default Parameter Value

ในการสร้างฟังก์ชัน เราสามารถกำหนดค่าพารามิเตอร์ไว้ล่วงหน้าก็ได้ หลังจากนั้นเมื่อเราเรียกใช้ฟังก์ชัน เราจะกำหนดอากิวเมนต์หรือไม่ก็ได้ โดยเมื่อเรียกใช้ฟังก์ชัน

  • ถ้าเรากำหนดค่าให้อากิวเมนต์ ค่าที่เราจะกำหนดจะถูกนำไปใช้เป็นค่าพารามิเตอร์
  • ถ้าไม่กำหนด ค่าดีฟอลต์ที่กำหนดไว้จะถูกนำมาใช้แทน

ดังตัวอย่าง

def person(name = "Rhaenyra"):
    print("My name is " + name)

person()

# My name is Rhaenyra
  • บรรทัดที่ 1 สร้างฟังก์ชันโดยมีอากิวเมนต์ 1 ตัว กำหนดค่าเริ่มต้นไว้เรียบร้อย
  • บรรทัดที่ 4 เรียกใช้ฟังก์ชันโดยไม่ระบุพารามิเตอร์ใด ๆ

ชนิดข้อมูลของอากิวเมนต์

เราสามารถกำหนดอากิวเมนต์เป็นข้อมูลชนิดใดก็ได้ โดยอากิวเมนต์ในฟังก์ชันจะกลายเป็นชนิดข้อมูลตามที่เรากำหนด เช่น ถ้าเราส่งข้อมูลประเภท list เข้าไป อากิวเมนต์ในฟังก์ชันก็จะเป็นข้อมูลประเภท list ด้วยเช่นกัน

def my_dragons(dragons):
    for dragon in dragons:
        print(dragon)

dragons = ["Syrax", "Caraxes", "Vermax", "Seasmoke"]
my_dragons(dragons)

การคืนค่าจากฟังก์ชัน

ถ้าต้องการให้ฟังก์ชันคืนค่าใด ๆ กลับออกมาหลังจากถูกเรียกใช้ สามารถใช้คีย์เวิร์ด return เพื่อบอกให้ฟังก์ชันส่งค่าที่ต้องการกลับออกมา ดังตัวอย่าง

def greet(name):
    return "Hello " + name

print(greet("Dreamfyre"))

# Hello Dreamfyre
  • บรรทัดที่ 2 ใช้คีย์เวิร์ด return เพื่อส่งค่ากลับออกมาจากฟังก์ชัน
  • บรรทัดที่ 4 ให้แสดงผลค่าที่ส่งกลับออกมาจากฟังก์ชัน

pass Statement

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

def greet(name):
    pass

Positional-Only Arguments

Positional-Only Arguments คืออาร์กิวเมนต์ที่ต้องระบุในตำแหน่งที่กำหนดไว้เมื่อเรียกใช้ฟังก์ชัน ไม่สามารถระบุชื่อ (keyword) เมื่อเรียกใช้ได้

รูปแบบการใช้ positional-only arguments คือการใช้เครื่องหมาย / ในการกำหนดในส่วนการประกาศฟังก์ชัน ดังนี้

def my_function(a, b, /, c, d):
    print(a, b, c, d)

จากตัวอย่าง a และ b เป็น positional-only arguments ซึ่งหมายความว่าต้องถูกกำหนดตามลำดับในขณะที่เรียกใช้ฟังก์ชัน ส่วน c และ d สามารถกำหนดได้ทั้งในรูปแบบตำแหน่งและ keyword

ตัวอย่างการเรียกใช้

def my_function(a, b, /, c, d):
    print(a, b, c, d)

my_function(1, 2, 3, 4)    # ถูกต้อง
my_function(1, 2, c=3, d=4)  # ถูกต้อง
# my_function(a=1, b=2, c=3, d=4)  # ผิดพลาด จะเกิด TypeError

การใช้ positional-only arguments ช่วยเพิ่มความชัดเจนและป้องกันความสับสนเมื่อใช้งานฟังก์ชัน ทำให้โค้ดอ่านง่ายขึ้นและลดความเสี่ยงในการเกิดข้อผิดพลาดจากการระบุ keyword ที่ผิด

Keyword-Only Arguments

Keyword-Only Arguments คืออาร์กิวเมนต์ที่ต้องระบุชื่อ (keyword) เมื่อเรียกใช้ฟังก์ชัน ไม่สามารถกำหนดตามตำแหน่งได้ การกำหนด keyword-only arguments ใช้เครื่องหมาย * ในการแบ่งส่วนระหว่าง positional arguments กับ keyword-only arguments

ตัวอย่างการใช้งาน keyword-only arguments

def my_function(a, b, *, c, d):
    print(a, b, c, d)

จากตัวอย่าง a และ b เป็น positional arguments ซึ่งสามารถกำหนดได้ตามตำแหน่งหรือชื่อ แต่ c และ d เป็น keyword-only arguments ซึ่งต้องระบุชื่อเมื่อเรียกใช้ฟังก์ชัน

ตัวอย่างการเรียกใช้

def my_function(a, b, *, c, d):
    print(a, b, c, d)

my_function(1, 2, c=3, d=4)  # ถูกต้อง
my_function(a=1, b=2, c=3, d=4)  # ถูกต้อง
#my_function(1, 2, 3, 4)      # ผิดพลาด จะเกิด TypeError
  • บรรทัดที่ 4 my_function(1, 2, c=3, d=4) เป็นการเรียกใช้ฟังก์ชันที่ถูกต้อง โดยระบุค่าให้ c และ d เป็น keyword arguments
  • บรรทัดที่ 5 my_function(a=1, b=2, c=3, d=4) เป็นการเรียกใช้ฟังก์ชันที่ถูกต้อง โดยระบุค่าทั้งหมดเป็น keyword arguments
  • บรรทัดที่ 6 my_function(1, 2, 3, 4) เป็นการเรียกใช้ฟังก์ชันที่ผิดพลาด เนื่องจาก c และ d ต้องระบุเป็น keyword arguments เท่านั้น

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

การใช้งาน Positional-Only และ Keyword-Only ร่วมกัน

การใช้งาน Positional-Only และ Keyword-Only ร่วมกันในฟังก์ชันเดียว สามารถทำได้เพื่อควบคุมวิธีการระบุค่าให้กับอาร์กิวเมนต์ในฟังก์ชัน โดยกำหนดให้บางอาร์กิวเมนต์ต้องระบุในตำแหน่งที่กำหนด (Positional-Only) และบางอาร์กิวเมนต์ต้องระบุเป็น keyword เท่านั้น (Keyword-Only)

โดยการใช้เครื่องหมาย / สำหรับกำหนด positional-only arguments และเครื่องหมาย * สำหรับกำหนด keyword-only arguments ในการประกาศฟังก์ชัน

ตัวอย่างการใช้งาน

def my_function(pos1, pos2, /, pos_or_kw, *, kw1, kw2):
    print(f"pos1: {pos1}, pos2: {pos2}, pos_or_kw: {pos_or_kw}, kw1: {kw1}, kw2: {kw2}")
  • pos1 และ pos2 เป็น Positional-Only Arguments
  • pos_or_kw เป็น Positional-Or-Keyword Argument (สามารถระบุได้ทั้งแบบตำแหน่งหรือ keyword)
  • kw1 และ kw2 เป็น Keyword-Only Arguments

ตัวอย่างการเรียกใช้

def my_function(pos1, pos2, /, pos_or_kw, *, kw1, kw2):
    print(f"pos1: {pos1}, pos2: {pos2}, pos_or_kw: {pos_or_kw}, kw1: {kw1}, kw2: {kw2}")

my_function(1, 2, 3, kw1=4, kw2=5)  # ถูกต้อง
my_function(1, 2, pos_or_kw=3, kw1=4, kw2=5)  # ถูกต้อง
# my_function(1, 2, 3, 4, 5)  # ผิดพลาด จะเกิด TypeError เพราะ kw1 และ kw2 ต้องเป็น keyword-only
# my_function(pos1=1, pos2=2, pos_or_kw=3, kw1=4, kw2=5)  # ผิดพลาด จะเกิด TypeError เพราะ pos1 และ pos2 เป็น positional-only
  • my_function(1, 2, 3, kw1=4, kw2=5) เป็นการเรียกใช้ฟังก์ชันที่ถูกต้อง โดยกำหนดค่าให้กับ kw1 และ kw2 เป็น keyword arguments
  • my_function(1, 2, pos_or_kw=3, kw1=4, kw2=5) เป็นการเรียกใช้ฟังก์ชันที่ถูกต้อง โดยกำหนดค่า pos_or_kw เป็น keyword argument
  • my_function(1, 2, 3, 4, 5) เป็นการเรียกใช้ฟังก์ชันที่ผิดพลาด เนื่องจาก kw1 และ kw2 ต้องระบุเป็น keyword arguments เท่านั้น
  • my_function(pos1=1, pos2=2, pos_or_kw=3, kw1=4, kw2=5) เป็นการเรียกใช้ฟังก์ชันที่ผิดพลาด เนื่องจาก pos1 และ pos2 ต้องระบุในตำแหน่งที่กำหนดเท่านั้น

การผสมผสานการใช้งาน Positional-Only และ Keyword-Only Arguments ช่วยให้ฟังก์ชันมีความยืดหยุ่นในการรับค่าและช่วยให้โค้ดชัดเจนยิ่งขึ้น

Recursion

Recursion หรือการเรียกตัวเองของฟังก์ชัน เป็นแนวคิดในการเขียนโปรแกรมที่ฟังก์ชันหนึ่งจะเรียกใช้ตัวมันเองเพื่อแก้ปัญหาที่มีโครงสร้างซ้ำ ๆ กัน โดยปกติการใช้ recursion จะต้องมีเงื่อนไขหยุด (base case) เพื่อป้องกันการเรียกฟังก์ชันไม่สิ้นสุด

ตัวอย่างเช่นการคำนวณค่า factorial ของตัวเลข ค่า factorial ของ n (เขียนว่า n!) คือผลคูณของตัวเลขทุกตัวตั้งแต่ 1 ถึง n

สามารถเขียนเป็นฟังก์ชัน recursive ได้ดังนี้

def factorial(n):
    # Base case: เมื่อ n เท่ากับ 0 หรือ 1, factorial ของมันจะเป็น 1
    if n == 0 or n == 1:
        return 1
    # Recursive case: คำนวณ factorial ของ n โดยเรียกฟังก์ชันตัวเอง
    else:
        return n * factorial(n - 1)
  • บรรทัดที่ 3-4 ถ้า n เท่ากับ 0 หรือ 1 ฟังก์ชันจะคืนค่า 1 (base case)
  • บรรทัดที่ 6-7 มิฉะนั้น ฟังก์ชันจะคืนค่า n คูณกับการเรียกใช้ฟังก์ชัน factorial ของ n-1 (recursive case)

ตัวอย่างการเรียกใช้ฟังก์ชัน

def factorial(n):
    # Base case: เมื่อ n เท่ากับ 0 หรือ 1, factorial ของมันจะเป็น 1
    if n == 0 or n == 1:
        return 1
    # Recursive case: คำนวณ factorial ของ n โดยเรียกฟังก์ชันตัวเอง
    else:
        return n * factorial(n - 1)

print(factorial(5))  # Output: 120

ขั้นตอนการทำงาน

  • factorial(5) จะคืนค่า 5 * factorial(4)
  • factorial(4) จะคืนค่า 4 * factorial(3)
  • factorial(3) จะคืนค่า 3 * factorial(2)
  • factorial(2) จะคืนค่า 2 * factorial(1)
  • factorial(1) จะคืนค่า 1 (base case)

ดังนั้นค่า factorial(5) จะเป็น 5 * 4 * 3 * 2 * 1 = 120

การใช้ recursion ต้องระวังเรื่องการกำหนด base case ให้ถูกต้องและแน่ใจว่ามีเงื่อนไขหยุดที่ทำให้ฟังก์ชันไม่ทำงานไม่สิ้นสุดและเกิด stack overflow

Stack Overflow คืออะไร?

Stack Overflow เป็นข้อผิดพลาด (error) ที่เกิดขึ้นเมื่อหน่วยความจำที่จัดสรรให้กับ stack เต็มจนไม่สามารถจัดเก็บข้อมูลเพิ่มเติมได้ ซึ่งมักเกิดจากการเรียกใช้ฟังก์ชันซ้ำ ๆ กันในลักษณะของ recursion โดยไม่มีเงื่อนไขหยุดที่เหมาะสม ทำให้เกิดการเรียกใช้ฟังก์ชันอย่างไม่สิ้นสุด (infinite recursion)

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

ถ้าไม่มีการหยุดการเรียกใช้ฟังก์ชันอย่างเหมาะสม ฟังก์ชันจะถูกเรียกใช้ซ้ำ ๆ และข้อมูลจะถูกเพิ่มลงใน stack เรื่อย ๆ จน stack เต็ม และเกิดข้อผิดพลาด stack overflow ขึ้น

แชร์เรื่องนี้