Notes & software courses · Free to learn
Aph's Blog
On this page

*args, **kwargs & การ Unpack

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

เขียนฟังก์ชันที่รับ argument ได้ยืดหยุ่น และกระจายข้อมูลเข้า/ออกด้วย * และ **

บางฟังก์ชันต้องรับ argument จำนวนไม่แน่นอน เช่น print() รับกี่ตัวก็ได้ บทย่อยนี้สอนวิธีเขียนฟังก์ชันแบบนั้นด้วย *args และ **kwargs พร้อมเทคนิค unpacking ที่ใช้บ่อยมากในโค้ดจริง

positional vs keyword argument

argument ส่งได้ 2 แบบ: ตามตำแหน่ง (positional) หรือระบุชื่อ (keyword) และตั้งค่า default ได้

python
def greet(name, greeting="สวัสดี"):   # greeting มี default
    return f"{greeting} {name}"

print(greet("Aph"))                    # สวัสดี Aph  (ใช้ default)
print(greet("Aph", "หวัดดี"))          # หวัดดี Aph  (positional)
print(greet("Aph", greeting="ยินดี"))  # ยินดี Aph   (keyword)

*args — รับ positional ไม่จำกัดจำนวน

ใส่ * หน้าชื่อพารามิเตอร์ Python จะรวบ argument ที่เหลือทั้งหมดมาเป็น tuple ชื่อ args นิยมตั้งชื่อ args แต่จริง ๆ ชื่ออะไรก็ได้ ที่สำคัญคือ *

python
def total(*nums):       # nums เป็น tuple ของทุกตัวที่ส่งมา
    print(nums)
    return sum(nums)

print(total(1, 2, 3))        # (1, 2, 3) แล้วคืน 6
print(total(10, 20))         # (10, 20) แล้วคืน 30
print(total())               # () แล้วคืน 0

**kwargs — รับ keyword ไม่จำกัดจำนวน

ใส่ ** หน้าชื่อ Python จะรวบ keyword argument ที่เหลือมาเป็น dict นิยมตั้งชื่อ kwargs (keyword arguments)

python
def make_user(**info):       # info เป็น dict
    print(info)
    return info

make_user(name="Aph", age=25, city="Bangkok")
# {'name': 'Aph', 'age': 25, 'city': 'Bangkok'}

# รวมทุกแบบเข้าด้วยกัน (ลำดับสำคัญ: ปกติ, *args, **kwargs)
def log(level, *messages, **meta):
    print(level, messages, meta)

log("INFO", "started", "ok", user="aph", code=200)
# INFO ('started', 'ok') {'user': 'aph', 'code': 200}

unpacking: กระจาย list/dict ตอนเรียก

* และ ** ใช้ "ขาออก" ได้ด้วย — กระจาย list เป็น positional args หรือ dict เป็น keyword args ตอนเรียกฟังก์ชัน

python
def add(a, b, c):
    return a + b + c

nums = [1, 2, 3]
print(add(*nums))        # 6  — กระจาย list เป็น a=1, b=2, c=3

opts = {"a": 1, "b": 2, "c": 3}
print(add(**opts))       # 6  — กระจาย dict เป็น keyword

# ใช้บ่อยกับ print
words = ["a", "b", "c"]
print(*words)            # a b c  (แทน print(words[0], words[1]...))

keyword-only argument (บังคับระบุชื่อ)

ใส่ * เปล่า ๆ คั่น เพื่อบังคับให้ argument หลังจากนั้นต้องส่งแบบระบุชื่อเสมอ ป้องกันการส่งผิดตำแหน่ง

python
def connect(host, *, port, timeout=30):
    print(host, port, timeout)

connect("localhost", port=8080)        # ✅ ต้องระบุ port=
# connect("localhost", 8080)           # ❌ TypeError
กับดักคลาสสิก: mutable default argument

อย่าใช้ list/dict เป็นค่า default เช่น def f(items=[]) เพราะ default ถูกสร้างครั้งเดียวตอนนิยามฟังก์ชัน แล้วถูกใช้ร่วมกันทุกครั้งที่เรียก! ค่าจะค้างข้ามการเรียก วิธีแก้: ใช้ None แล้วสร้างใหม่ข้างใน (เราจะเจาะลึกเรื่องนี้ในหัวข้อ Mutability)

python
# ❌ ผิด: list ถูกแชร์ข้ามการเรียก
def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item("a"))   # ['a']
print(add_item("b"))   # ['a', 'b']  ← ค้าง! ไม่ใช่ ['b']

# ✅ ถูก: ใช้ None แล้วสร้างใหม่
def add_item_ok(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

สรุปหัวข้อนี้

  • *args รวบ positional เป็น tuple, **kwargs รวบ keyword เป็น dict
  • ลำดับพารามิเตอร์: ปกติ → *args → **kwargs
  • ตอนเรียก *list กระจายเป็น positional, **dict กระจายเป็น keyword
  • * เปล่า ๆ บังคับ keyword-only; ห้ามใช้ list/dict เป็น default
แบบฝึกหัด

1) เขียนฟังก์ชัน multiply(*nums) คูณทุกตัวเข้าด้วยกัน 2) เขียนฟังก์ชันรับ **kwargs แล้ว print ทุก key=value 3) มี list [3, 5, 7] ใช้ unpacking ส่งเข้าฟังก์ชัน add(a,b,c) 4) แก้ฟังก์ชันที่ใช้ items=[] เป็นแบบ items=None ให้ถูกต้อง