from typing import Union
def circle_area (r : Union[int,float]) -> float :
return 3.14 * (r **2)
ก่อนหน้านี้ ถ้าเราได้ทำการกำหนด DataType ให้กับ Function ของเรา เราจะทราบกันว่า ถ้าเราต้องการ Hint ให้มันได้มากกว่า 1 DataType เราจะต้องไป Import Union มาจาก typing แล้ว ก็ใช้ในการรวม DataType เข้าด้วยกัน จากตัวอย่างด้านบน เราจะเห็นว่า เราทำการสร้าง Function สำหรับการคำนวณพื้นที่วงกลมขึ้นมา ซึ่ง เส้นรอบวงหรือ R ที่เราใส่เข้าไป เราจะเป็น Integer หรือ Float ก็ได้ ทำให้เวลาเราทำ Typing เราเลย จำเป็นที่จะต้องบอกว่า เส้นรอบวงที่ใส่เข้าไปเป็น Integer หรือ Float ก็ได้ ทำให้เราต้องใช้ Union เข้ามาช่วย
เราจะเห็นได้ว่า เราต้องมานั่ง Import Union เข้ามาอีก ก็ทำให้มันเสียเวลาเข้าไปอีก นอกจากนั้น Code มันดูรกไปหมด ยิ่งถ้าเราบอกว่า เรามี Argument เยอะ ๆ เขียนแบบนี้เข้าไปรับรองปวดหัวแน่นอน ทำให้ใน Python 3.10 มีการลดรูป Union ได้เลย
def circle_area (r : int | float) -> float :
return 3.14 * (r**2)
แทนที่เราจะใช้คำสั่ง Union เราก็สามารถลดรูปเหลือเครื่องหมาย Vertical Bar (|) ได้เลย พอเขียนออกมา เราจะเห็นได้ว่า มันสะอาดตาเยอะขึ้นมาก และ ไม่ต้องมานั่ง Import Union เพิ่มขึ้นมาอีกบรรทัดอีกด้วย ทำให้ทั้งสวย และ ประหยัดเข้าไปอีก ดีงามมาก ๆ
FilePath = str FileName = str def get_file_name (file_path : FilePath) -> FileName : return file_path.split('/')[-1]
อีกหนึ่งปัญหาของการทำ Typing คือ เรื่องของ User Defined Type หรือก็คือ Type ที่เรากำหนดขึ้นมาเอง เพื่อความง่าย จากตัวอย่างด้านบน เราพยายามที่จะสร้าง Function สำหรับการ Parse ชื่อไฟล์จาก File Path ออกมา ตอนที่ทำ Typing แทนที่เราจะกำหนดให้มันเป็น str ไปเลย เวลาเรากลับมาอ่าน Code เยอะ ๆ หน่อย เราก็อาจจะ งง ได้ วิธีคือ เราทำการกำหนด Type ไว้ในส่วนเดียวกันให้หมด เวลาเราอ่าน ๆ ไป เราเจอ เราก็จะได้รู้ว่า เราควรจะไปหาว่ามันเป็น Type อะไรจริง ๆ ในที่เดียวเลย
แต่เราจะเห็นว่า การทำแบบด้านบน บางทีมันแอบอ่านยากไปหน่อย มองผ่าน ๆ เราไม่รู้เลยนะว่า มันเป็นการสร้าง Type ดู ๆ มันก็แค่เป็นการ Assign ค่าให้ตัวแปรธรรมดาไม่มีอะไร เราไม่รู้เลยว่ามันจะเอามาใช้เป็น Type ทำให้ Python 3.10 ออกเรื่องของ TypeAlias ออกมาช่วยเพื่อให้เรามองแล้วรู้ว่าบรรทัดนี้มันเป็นการสร้าง Type ขึ้นมา
from typing_extensions import TypeAlias
FilePath: TypeAlias = str
FileName: TypeAlias = str
เริ่มจากการไป Import TypeAlias เข้ามาจากด้านบน และ เวลาที่เราจะสร้าง Type ขึ้นมาใหม่ เราก็บอก Python ว่า ตัวแปรที่เราสร้างเป็น Type Alias ด้วยการไปเรียก TypeAlias ที่เราพึ่ง Import เข้ามาได้เลย ทำให้เวลาเรากลับมาอ่าน เราก็จะเห็นแล้วว่า FilePath และ FileName เป็นการสร้าง Type นั่นเอง
ตัวอย่างที่เรายกขึ้นมา มันอาจจะไม่ค่อยเห็นภาพเท่าไหร่ เพราะ Type ที่เราหยิบมาเป็น Primitive Type แต่ลองนึกสภาพ ถ้าเราต้องกำหนดเป็น Class ต่าง ๆ ที่เราจะต้องเอาชื่อ Class มา Cast ให้เป็น String งานนี้แหละ เราไม่รู้เลยว่า อันไหนมันเป็น Type ที่เราสร้างกันแน่ งง กันไปหมดแน่นอน
อันนี้แหละที่รู้สึกว่า สักที !!! มาเป็น Default สักที ก่อนจะไปเล่าถึงการเปลี่ยนแปลง เราต้องเล่าก่อนว่า ปกติแล้ว Python มันจะทำ Type Annotation ตอนที่มันสร้าง Function ขึ้นมา ซึ่งแน่นอนว่า มันก็ต้องไล่อ่านจากบนลงล่าง เพื่อที่จะสร้าง Annotation ขึ้นมา ดูเหมือนจะดี แต่จริง ๆ แล้วมันมีปัญหาอยู่ 2 เรื่องใหญ่ ๆ ด้วยกัน
class Foo:
def bar(self) -> Bar:
return Bar()
class Bar:
def foo(self) -> Foo:
return Foo()
เรื่องแรกคือ Forward Reference อันนี้จะเกิดในกรณีที่เราไป Hint เป็น Type ที่ Python มันยังอ่านไม่ถึง หรือก็คือ เราอาจจะเรียกในบรรทัดก่อนที่ตัวที่เราเรียกจะถึงเช่นด้านบน เราสร้าง Class ขึ้นมา 2 ตัวคือ Foo และ Bar เพื่อให้เห็นภาพชัด อยากให้ไปดูที่ Class Foo เราจะเห็นว่า ใน Method bar(self) มันมีการไปกำหนด Type เป็น Bar ซึ่งในมุมมองของ Python มันจะไม่รู้จัก เพราะอย่างที่บอกว่า Python มันจะอ่านจากบนลงล่าง ทำให้ในขณะที่กำลัง Evaluate Class Foo ใน Method ชื่อ bar Python มันยังไม่รู้จัก Class ชื่อ Bar เลยก็เลยทำให้เกิด Exception เกิดขึ้นได้ ซึ่งเราก็สามารถที่จะแก้ไขได้ด้วยการกำหนดเป็น String ไปเลย
นอกจากนั้น มันยังทำให้การ Import Module มันช้าลงอีกด้วย เพราะตอนที่เรา Import เข้ามา Python มันก็ต้องเข้าไปอ่านส่วนของ Code ที่มีการ Import เข้ามา บางทีเราอาจจะ Import มาแค่ไม่กี่ Function ก็ดูจะไม่มีปัญหา แต่ถ้าเราบอกว่าเรา Import หลาย ๆ Library เลยละ นั่นแหละ ก็หายนะอยู่เหมือนกัน
ทำให้ใน PEP563 ได้มีการคิดในส่วนของ Postponed Evaluation of Annotations ขึ้นมา เพื่อให้ Python มันยังไม่ทำการ Annotate Type ตอนที่ Import จากปัญหาทั้ง 2 ที่กล่าวไป แต่ไป Annotate ตอนที่เราใช้งานเลย ก็จะทำให้ เราลดเวลาในการ Initialise ลงไปได้เยอะพอสมควรเลย
from __future__ import annotation
ซึ่งก่อนหน้านี้ใน Python 3.7 ขึ้นไป PEP563 ก็ได้ถูกนำมาใช้แล้วละ แต่ยังไม่ได้เป็นค่าเริ่มต้น ทำให้ถ้าใครที่ใช้ Python 3.7 ขึ้นไปสามารถเรียก Code ด้านบนได้เลย ก็จะสามารถเรียกใช้ไอเดียของ PEP563 ได้แล้ว ส่วนใน Python 3.10 ก็จะเลือกให้เป็นค่าเริ่มต้นแล้ว เราก็ไม่ต้องไป Import มาแล้ว และก็ไม่ต้องกลัวทั้ง 2 ปัญหาที่เล่าไปด้วย
>> 10.bit_count() 2
ใน Python 3.10 เราสามารถเรียกคำสั่งเพื่อทำการนับจำนวนของเลข 1 ในเลขฐาน 2 ได้แล้ว หรือที่เราเรียกกันในภาษาอังกฤษว่า Population Count เราจะเห็นว่า เราสามารถเอาตัวเลขจำนวนเต็ม (Integer) มาเรียกได้ตรง ๆ เลย ง่ายกว่าเมื่อก่อนมาก
bin(10).count('1')
ด้านบนนี้จะเป็น Code ใน Python Version ก่อนหน้าถ้าเราต้องการ Population Count เราจะเห็นว่า เราจะต้องใช้ bin() เพื่อแปลงให้มันเป็นเลขฐาน 2 ก่อน ซึ่งมันจะให้มาเป็น String และหลังจากนั้น เราจึงสามารถเรียกคำสั่ง Count เพื่อให้มันนับจำนวนเลข 1 ที่เกิดขึ้น แต่อย่างที่บอกว่า bin() มันให้เป็น String ดังนั้น เราก็ต้องนับ 1 ที่เป็น String ด้วยเช่นกัน ทำให้เราต้องใส่เครื่องหมายคำพูดค่อมไว้นั่นเอง
การมี Function สำเร็จสำหรับการทำ Population Count มันทำให้ Code ดูดีขึ้นมากจากเดิมที่ต้องมานั่งเขียน ๆ ค่อม ๆ 2 ชั้น อันนี้เราสามารถเรียกตรง ๆ ได้เลย
อันนี้เจอครั้งแรกก็คือ เผ็ดช์ร้อน สำหรับหลาย ๆ คนที่เขียนภาษาอื่นก่อนจะมาเขียน Python เราน่าจะเคยได้เจอกับ Syntax พวก Switch-Case กันมาก่อนแน่ ๆ แล้วพอมาเขียน Python กลับพบว่า มัน ไม่ มี แต่วันนี้ เราจะได้ใช้ของที่เหมือนกันแล้ว แต่เปลี่ยนชื่อเป็น Match-Case
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the Internet"
จริง ๆ การทำงานจะเหมือนกับการที่เราใช้ Switch-Case ไม่มีผิดเลย แต่แค่มาอยู่ใน Python เท่านั้นแหละ แต่ก็เป็นเรื่องที่ดี ที่ในที่สุด Python ก็เอาเข้ามาสักที มันทำให้ Code ของเราดูเรียบร้อยมากขึ้นเยอะกว่าการใช้ Nested IF ซ้อน ๆ กันบ้างแหละ หรืออาจจะใช้ Dictionary มา Map มันก็จะแปลก ๆ นิดนึงเหมือนกัน
Python 3.10 มี Feature ใหม่ ๆ มาให้เราลองเล่นเยอะมาก ๆ โดยเฉพาะเรื่องของการทำ Typing ที่มันเข้ามาทำให้เราทำงานได้ง่ายขึ้น Code อ่านง่ายขึ้นเยอะมาก ๆ กับอีกตัวที่ชอบคือ Population Count ที่เมื่อก่อนก็ทำได้แหละ แต่การที่มันมีคำสั่งสำเร็จมาให้เลย มันก็ทำให้ Code ของเรามันไป Focus ที่ Result ของเรามากขึ้น เข้าใจได้ง่ายขึ้นนั้นเอง สำหรับคนที่อยากลอง ก็สามารถไป Update ใน Stable Release ได้เลยผ่าน Package Manager ที่ทุกคนใช้อยู่ แต่ย้ำ สำหรับคนที่ใช้พวก Tensorflow ว่า ณ วันที่เขียน ตอนนี้ Tensorflow 2.4.1 จะรองรับถึงแค่ Python 3.8 เท่านั้น ทำให้เราไม่สามารถเอามาใช้กับ Python 3.10 ได้นะ
Reference
[ docs.python.org ] - What's new in Python 3.10
กิจกรรมที่กำลังจะมาถึง
ไม่พลาดกิจกรรมเด็ด ๆ ที่น่าสนใจ
Event นี้จะเริ่มขึ้นใน April 25, 2023
รายละเอียดเพิ่มเติม/สมัครเข้าร่วมคอร์สเรียนไพธอนออนไลน์ที่เราได้รวบรวมและได้ย่อยจากประสบการณ์จริงและเพื่อย่นระยะเวลาในการเรียนรู้ ลองผิด ลองถูกด้วยตัวเองมาให้แล้ว เพราะเวลามีค่าเป็นอย่างยิ่ง พร้อมด้วยการซัพพอร์ตอย่างดี