เรียน Python: พลังลับของ Tuple ที่ทำให้ *args และ **kwargs โคตรยืดหยุ่น
หลายคนเริ่มเรียน Python แล้วเจอสัญลักษณ์แปลก ๆ
*args
**kwargs
แล้วจำได้แค่ว่า
"อ๋อ...เอาไว้รับ parameter ไม่จำกัด"
แต่จริง ๆ แล้วเบื้องหลังของมันคือแนวคิดเดียวกับที่ทำให้ Python สามารถ
- Swap ตัวแปรได้
- Return หลายค่าได้
- Unpack List ได้
- ส่ง Parameter แบบ Dynamic ได้
ทั้งหมดนี้เกิดจาก Tuple Packing และ Unpacking
Python Return ได้หลายค่า จริงเหรอ?
หลายภาษาทำไม่ได้
def get_user():
return "John", 25
เรียกใช้
name, age = get_user()
print(name)
print(age)
ผลลัพธ์
John
25
ดูเหมือน Return สองค่า
แต่จริง ๆ แล้ว Python Return Tuple
("John", 25)
นั่นเอง
Tuple Packing
Python สามารถสร้าง Tuple ได้โดยไม่ต้องใส่วงเล็บ
data = 1, 2, 3
print(type(data))
ผลลัพธ์
<class 'tuple'>
จริง ๆ แล้ว Python มองเป็น
data = (1, 2, 3)
Tuple Unpacking
ดึงค่าออกมาพร้อมกัน
x, y, z = (10, 20, 30)
หรือ
x, y, z = [10, 20, 30]
ก็ได้เช่นกัน
*args คือ Tuple อัตโนมัติ
สมมติอยากสร้างฟังก์ชันบวกเลขได้ไม่จำกัดจำนวน
def add(*args):
print(args)
เรียกใช้
add(1, 2, 3, 4)
ผลลัพธ์
(1, 2, 3, 4)
สังเกตไหมว่า
args
เป็น
tuple
ไม่ใช่ list
ใช้งานจริง
def add(*args):
return sum(args)
print(add(1, 2))
print(add(1, 2, 3))
print(add(1, 2, 3, 4))
ผลลัพธ์
3
6
10
ฟังก์ชันเดียวรองรับจำนวน parameter ไม่จำกัด
ส่ง Tuple เข้าไปแตกเป็น Parameter
numbers = (10, 20)
print(*numbers)
ผลลัพธ์
10 20
หรือ
def add(a, b):
return a + b
numbers = (10, 20)
print(add(*numbers))
ผลลัพธ์
30
Python จะทำการ Unpack Tuple ให้อัตโนมัติ
**kwargs คือ Dictionary อัตโนมัติ
def show(**kwargs):
print(kwargs)
เรียกใช้
show(
name="John",
age=25
)
ผลลัพธ์
{
'name': 'John',
'age': 25
}
kwargs จะกลายเป็น Dictionary
ใช้งานจริง
def create_user(**kwargs):
for k, v in kwargs.items():
print(k, v)
create_user(
name="John",
age=25,
city="Bangkok"
)
ผลลัพธ์
name John
age 25
city Bangkok
ส่ง Dictionary เข้า Method
def create_user(name, age):
print(name, age)
data = {
"name": "John",
"age": 25
}
create_user(**data)
ผลลัพธ์
John 25
Python จับคู่ Key กับชื่อ Parameter ให้อัตโนมัติ
ความเจ๋งเมื่อใช้กับ Method
ตัวอย่างคลาส
class User:
def __init__(self, name, age):
self.name = name
self.age = age
u = User("John", 25)
สร้าง Object จาก Dictionary ได้เลย
data = {
"name": "John",
"age": 25
}
u = User(**data)
Python Framework ดัง ๆ ใช้เทคนิคนี้เต็มไปหมด
เช่น
- FastAPI
- Django
- SQLAlchemy
- Pydantic
Wrapper Method ขั้นเทพ
สมมติอยากสร้าง Logger
def logger(func):
def wrapper(*args, **kwargs):
print("calling...")
return func(*args, **kwargs)
return wrapper
ใช้งาน
@logger
def add(a, b):
return a + b
print(add(10, 20))
ผลลัพธ์
calling...
30
ทำไม Decorator ส่วนใหญ่ใช้ *args และ **kwargs
เพราะไม่รู้ล่วงหน้าว่าฟังก์ชันที่จะถูกห่อมี parameter อะไรบ้าง
def wrapper(*args, **kwargs):
สามารถรองรับได้ทุกฟังก์ชัน
f()
f(1)
f(1, 2)
f(name="john")
f(1, 2, city="bangkok")
โดยไม่ต้องแก้โค้ด
เทคนิคสุดว้าว: จับค่าแรกและที่เหลือ
first, *others = [1, 2, 3, 4, 5]
ผลลัพธ์
first = 1
others = [2, 3, 4, 5]
สังเกตว่า
others
กลายเป็น List
ไม่ใช่ Tuple
จับหัว จับท้าย พร้อมกัน
first, *middle, last = [1, 2, 3, 4, 5]
ผลลัพธ์
first = 1
middle = [2, 3, 4]
last = 5
อ่านง่ายมากเมื่อเทียบกับหลายภาษา
แล้วทำไม Python Framework ถึงชอบใช้กันนัก?
เพราะมันช่วยให้เขียน API ที่ยืดหยุ่นได้มาก
ตัวอย่างแนวคิด
class EventBus:
def emit(self, event, *args, **kwargs):
...
ไม่ว่าข้อมูลจะมากน้อยแค่ไหน
emit("login", user)
emit("buy", product, qty=5)
emit("error", code=500, message="fail")
รองรับได้หมด
สรุป
ฟีเจอร์ *args และ **kwargs ไม่ได้เป็นเพียง "รับ parameter ไม่จำกัด"
แต่เป็นส่วนหนึ่งของแนวคิด Tuple Packing / Unpacking ที่ฝังอยู่ทั่วทั้งภาษา Python ตั้งแต่
- Swap ตัวแปร
- Return หลายค่า
- Function Arguments
- Constructor
- Decorator
- Framework ต่าง ๆ
เมื่อเข้าใจแนวคิดนี้แล้ว คุณจะเริ่มอ่านโค้ด Python ระดับกลางถึงระดับสูงได้คล่องขึ้นมาก และเริ่มเห็นว่าทำไมหลายคนที่มาเรียน Python แล้วรู้สึกว่า "ภาษานี้ออกแบบมาเพื่อให้คนเขียนโค้ดได้เร็วจริง ๆ"
ความคิดเห็น
แสดงความคิดเห็น