- แนะนำ ฟีเจอร์ขั้นสูง 14 อย่าง ของ Python ที่คนมักไม่ค่อยรู้จัก พร้อมตัวอย่างใช้งานจริง
- อธิบายเชิงลึกเกี่ยวกับ static typing และการออกแบบเชิงโครงสร้าง เช่น
typing, generics, protocols, context managers
- รวมถึง structural pattern matching ที่เพิ่งเพิ่มเข้ามาตั้งแต่ Python 3.10 ขึ้นไป และเทคนิคเพิ่มประสิทธิภาพอย่าง slots และ metaclass
- มี เคล็ดลับสำหรับการเขียนโค้ดให้สะอาดขึ้น เช่น
f-string, cache, future, proxy, for-else, walrus
- ในแต่ละฟีเจอร์มี ลิงก์และเอกสารอ้างอิงสำหรับศึกษาต่อ พร้อมการนำเสนอที่แม้นักพัฒนาระดับจูเนียร์ก็เข้าถึงได้ง่าย
สรุป 14 ฟีเจอร์ขั้นสูงของ Python
# การโอเวอร์โหลดไทป์
- ตัวตกแต่ง
@overload ช่วยให้ กำหนด type signature ได้หลายแบบในฟังก์ชันเดียว
- type checker สามารถ อนุมานชนิดค่าที่คืนกลับได้อย่างแม่นยำ ตามค่าของอาร์กิวเมนต์ที่ส่งมา
- ใช้
Literal เพื่อ จำกัดค่าของสตริง ได้ด้วย
- สามารถทำ signature ของฟังก์ชันที่บังคับให้รับ
id หรือ username อย่างใดอย่างหนึ่ง ได้
- ใช้
Literal เป็นทางเลือกแทน Enum แบบเบาสำหรับความปลอดภัยของชนิดข้อมูล
# อาร์กิวเมนต์แบบ keyword-only / positional-only
- ใช้
* เพื่อกำหนดให้เป็น อาร์กิวเมนต์แบบ keyword-only (ห้ามใช้เป็น positional argument)
- ใช้
/ เพื่อกำหนดให้เป็น อาร์กิวเมนต์แบบ positional-only (ห้ามใช้เป็น keyword argument)
- ช่วย บังคับรูปแบบการใช้อาร์กิวเมนต์ให้ชัดเจน ในการออกแบบ API
# Future Annotations (__future__)
- เดิมที type hint จะถูกประเมินทันทีใน runtime จึงเกิด ปัญหาเรื่องลำดับการประกาศ
- สามารถ เลื่อนเวลาการประเมิน ได้ด้วย
from __future__ import annotations
- แต่เนื่องจากใช้วิธีจัดการแบบสตริง จึงต้องระวังเมื่อใช้งาน type ใน runtime
PEP 649 เสนอการปรับปรุงโดยใช้การประเมินแบบหน่วงเวลาในแอตทริบิวต์ __annotations__
# ไวยากรณ์ Generic
- ตั้งแต่ Python 3.12 เป็นต้นไป รองรับ ไวยากรณ์ใหม่สำหรับการนิยาม generic type
- ใช้งานได้เข้าใจง่ายกว่าเดิมในรูปแบบ
class Foo[T, U: int] แทน TypeVar
- มี Variadic Generics เพิ่มเข้ามาเพื่อรองรับการจัดการชนิดข้อมูลที่หลากหลาย
- การประกาศ type alias ก็สั้นลง เช่น
type Vector = list[float]
# Protocols
- เป็นเวอร์ชันตรวจสอบชนิดของ Duck Typing ที่ทำให้ใช้ structural subtyping ได้
- หากคลาสมีเมธอดที่กำหนดไว้ ก็สามารถเข้ากันได้ทางชนิดข้อมูลแม้ไม่สืบทอด type โดยตรง
- สามารถขยายให้ตรวจด้วย
isinstance ได้ผ่าน @runtime_checkable
# Context Manager
- เป็นอ็อบเจ็กต์ที่มีเมธอด
__enter__, __exit__ และใช้งานในบล็อก with
- สามารถสร้างแบบฟังก์ชันอย่างง่ายได้ด้วยตัวตกแต่ง
contextlib.contextmanager
- ทำ งานตั้งค่าและงานเก็บกวาด ก่อนและหลัง
yield
# Structural Pattern Matching
- ใช้ไวยากรณ์
match-case เพื่อ แตกแขนงการทำงานกับโครงสร้างข้อมูลที่ซับซ้อนได้อย่างเข้าใจง่าย
- รองรับการแยกโครงสร้าง tuple/list, OR pattern, guard condition (
if) และ wildcard
- เนื่องจากแยกเงื่อนไขตามโครงสร้างของข้อมูล จึง ช่วยเพิ่มความอ่านง่ายและการดูแลรักษาโค้ด
# การปรับแต่งประสิทธิภาพด้วย __slots__
- ใช้สล็อตแบบคงที่แทน
__dict__ เพื่อปรับปรุงหน่วยความจำและความเร็ว
__slots__ ใช้ tuple ที่ระบุเฉพาะชื่อแอตทริบิวต์
- ป้องกันการเพิ่มแอตทริบิวต์ที่ไม่จำเป็นในคลาส
- แต่เป็นการปรับแต่งระดับไมโคร จึงควรใช้อย่างระมัดระวัง
# รวมเคล็ดลับสไตล์โค้ด Python
- คำสั่ง for-else: หากลูปจบโดยไม่มี
break จะทำงานส่วน else
- walrus operator (
:=): ประกาศตัวแปรและตรวจสอบได้พร้อมกัน
- or short-circuit evaluation: คืนค่าที่เป็นจริงตัวแรกจากหลายค่า
- การเชื่อม comparison operator: เขียนโค้ดให้กระชับได้ เช่น
0 < x < 10
# การฟอร์แมตขั้นสูงของ f-string
- ใช้รูปแบบ
f"{ตัวแปร=}" เพื่อ แสดงผลสำหรับดีบัก ได้
- มีตัวเลือกหลากหลาย เช่น ฟอร์แมตตัวเลข (
:.2f, :+.2f, :,) และฟอร์แมตวันที่ (%Y-%m-%d)
- ใช้ format mini-language สำหรับการจัดกึ่งกลาง การเติมช่องว่าง และการแสดงเปอร์เซ็นต์
# ตัวตกแต่งแคช
- ใช้
@lru_cache และ @cache เพื่อ เก็บผลลัพธ์ของฟังก์ชัน และเพิ่มความเร็ว
- มีประโยชน์กับฟังก์ชันเวียนเกิดหรือการคำนวณซ้ำจำนวนมาก
@cache ถูกเพิ่มเข้ามาตั้งแต่ Python 3.9 และมีแคชไม่จำกัดเป็นค่าเริ่มต้น
# Python Future
- เป็น อ็อบเจ็กต์สำหรับจัดการงานอะซิงก์ คล้าย Promise ของ JS
- จัดการผลลัพธ์แบบอะซิงก์ได้ด้วย
Future.set_result(), add_done_callback() เป็นต้น
asyncio.Future() สามารถใช้ร่วมกับ await ได้
- เมื่อใช้ร่วมกับ
ThreadPoolExecutor ก็สามารถทำ งานขนานเบื้องหลัง ได้
# Proxy Property
- ทำให้แอตทริบิวต์ของคลาสหนึ่งตัว ทำงานได้ทั้งเหมือนพร็อพเพอร์ตีและเหมือนฟังก์ชัน
- ใช้
__get__, __call__, __repr__ เพื่อให้มีความสามารถทั้งสองแบบ
- ช่วยให้การออกแบบ API จัดการทั้งค่าเริ่มต้นและการเรียกพร้อมพารามิเตอร์ในรูปแบบเดียวกัน ได้
- มีคุณค่าในเชิงตัวอย่างเชิงทดลองมากกว่าการใช้งานจริง
# Metaclass
- คือ คลาสของคลาส ที่ใช้สร้างตัวคลาสขึ้นมาเอง
- สามารถใส่เมตะลอจิก เช่น การปรับแต่งแอตทริบิวต์ของคลาสหรือการลงทะเบียนอัตโนมัติ
- ในทางปฏิบัติ ส่วนใหญ่ แทนที่ได้ด้วยตัวตกแต่ง
- ใน Django, SQLAlchemy, Pydantic เป็นต้น มีการใช้ metaclass ภายใน
5 ความคิดเห็น
จากมุมมองของฝั่งแบ็กเอนด์ ผมเคยมีประสบการณ์ว่า metaclass ทำให้การดีบักยากขึ้น
มีความเห็นว่า
for-elseไม่ได้ช่วยให้โค้ดอ่านง่ายหรือชัดเจนขึ้นมากนัก จึงมักถูกมองว่าเป็น anti-pattern และโปรดทราบว่าasyncio.Futureถือเป็นรายละเอียดการติดตั้งใช้งานภายในของasyncioขอบคุณครับ โดยเฉพาะข้อ 10 จะเอาไปใช้ทันที
เพิ่มกฎการเขียนโค้ดด้วย AI..
ขอบคุณสำหรับเคล็ดลับดี ๆ
ความคิดเห็นจาก Hacker News
สวัสดี! ผมเป็นผู้เขียนบล็อกต้นฉบับเอง! ตกใจมากที่เห็นบทความของตัวเองขึ้นหน้าแรกของ HN ตอนตี 4
ทุกครั้งที่ใช้ Python ผมจะกังวลว่าโค้ดจะดูเหมือนใช้ Python ผิดวิธีไหม
Python ก็ควรเป็น Python ต่อไป ส่วน golang, Rust, Typescript ก็ควรมีปรัชญาและการออกแบบของแต่ละภาษาเอง
ข้อดีที่ใหญ่ที่สุดของ Python คือมันให้ความรู้สึกเหมือน pseudocode ที่รันได้
ข้อท้วงติงเกี่ยวกับหัวข้อ 9.3 การประเมินนิพจน์: ถ้ามีสตริงว่าง การประเมินผลจะต่างออกไป
ในฐานะคนที่ย้ายจาก Javascript/Typescript มา Python นี่เป็นแหล่งข้อมูลที่มีประโยชน์
ฟีเจอร์ส่วนใหญ่ไม่ใช่ฟีเจอร์ขั้นสูง
สิ่งที่อยากเปลี่ยนในรายการคือการใส่คอนเทนเนอร์ของ collections.abc เข้าไปด้วย
สนุกกับการได้อ่านบทความนี้