ฟีเจอร์ Python ขั้นสูง
(blog.edward-li.com)- แนะนำ ฟีเจอร์ขั้นสูง 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 เข้าไปด้วย
สนุกกับการได้อ่านบทความนี้