Courses
ทักษะการเขียนโค้ด Python เป็นที่ต้องการมากขึ้นในช่วงไม่กี่ปีที่ผ่านมา เพื่อช่วยพัฒนาทักษะการเขียนโปรแกรม Python เราได้คัดสรร 30 ทริก Python สุดเจ๋งที่คุณสามารถใช้เพื่อปรับปรุงโค้ดของคุณ ลองเรียนรู้วันละหนึ่งข้อเป็นเวลา 30 วัน และอย่าลืมดูโพสต์เกี่ยวกับแนวปฏิบัติที่ดีที่สุดของ Python เพื่อให้แน่ใจว่าโค้ดของคุณอยู่ในระดับยอดเยี่ยม
หากทักษะ Python ยังไม่แน่นพอ สามารถฝึกให้คมขึ้นได้ด้วย Python Skill Track ของเรา
ทริกเกี่ยวกับลำดับข้อมูลและโครงสร้างข้อมูล
#1 Slicing
a = "Hello World!"
print(a[::-1])
"""
!dlroW olleH
"""
Slicing เป็นคุณสมบัติใน Python ที่อาศัยการทำงานกับ index เพื่อให้เข้าถึงส่วนย่อยของลำดับข้อมูลได้ Index คือ ตำแหน่งของสมาชิกในลำดับข้อมูล หากชนิดลำดับข้อมูลเป็นแบบเปลี่ยนค่าได้ (mutable) สามารถใช้ slicing เพื่อดึงและแก้ไขข้อมูลได้
หมายเหตุ: เรายังสามารถใช้ slicing กับลำดับข้อมูลที่เปลี่ยนค่าไม่ได้ (immutable) ได้ แต่หากพยายามแก้ไข slice จะเกิด TypeError
รูปแบบของการทำ slice คือ: sequence[start:stop:step] หากไม่ได้ระบุค่าในพารามิเตอร์ start, stop และ step จะใช้ค่าเริ่มต้น โดยค่าเริ่มต้นคือ:
- "start" ค่าเริ่มต้นเป็น 0
- "stop" ค่าเริ่มต้นเป็นความยาวของลำดับข้อมูล
- "step" ค่าเริ่มต้นเป็น 1 หากไม่ระบุ
เมื่อใช้รูปแบบ sequence[start:stop] องค์ประกอบที่คืนค่าจะเป็นตั้งแต่ index เริ่มต้นจนถึง stop - 1 (ไม่รวม index stop)
เรายังสามารถส่ง index ติดลบได้ ซึ่งใช้เพื่อย้อนลำดับข้อมูลได้ ตัวอย่างเช่น ในลิสต์ที่มี 4 องค์ประกอบ index 0 ก็เท่ากับ index -4 และ index สุดท้ายก็เท่ากับ -1 ในโค้ดตัวอย่างด้านบน เรานำความรู้นี้ไปใช้กับพารามิเตอร์ step ของลำดับข้อมูล ทำให้สตริงถูกพิมพ์ย้อนจากท้ายลำดับมายัง index 0
#2 การสลับค่าแบบ inplace / การกำหนดค่าพร้อมกัน
a = 10
b = 5
print(f"First: {a, b}")
"""
First: (10, 5)
"""
a, b = b, a + 2
print(f"Second: {a, b}")
"""
Second: (5, 12)
"""
ถ้าความรู้สึกแรกของคุณคือ b จะมีค่า 7 แทนที่จะเป็น 12 แสดงว่าคุณติดกับดักของการสลับค่าแบบ in-place
ใน Python เราสามารถคลายแพ็ก iterable ไปยังตัวแปรหลายตัวในคำสั่งกำหนดค่าเดียวได้ด้วย automatic unpacking ตัวอย่างเช่น:
a, b, c = [1, 2, 3]
print(a)
print(b)
print(c)
"""
1
2
3
"""
เรายังสามารถรวบค่าหลายค่าเข้าสู่ตัวแปรตัวเดียวด้วย * – ทริกนี้เรียกว่า packing ดูตัวอย่างด้านล่าง
a, *b = 1, 2, 3
print(a, b)
"""
1 [2, 3]
"""
เมื่อผสมผสาน packing และ unpacking อัตโนมัติ จะเกิดเทคนิคที่เรียกว่า simultaneous assignment ซึ่งใช้กำหนดค่าชุดของค่าให้กับชุดของตัวแปรได้
#3 List กับ tuple
import sys
a = [1, 2, 3, 4, 5]
b = (1, 2, 3, 4, 5)
print(f"List size: {sys.getsizeof(a)} bytes")
print(f"Tuple size: {sys.getsizeof(b)} bytes")
"""
List size: 52 bytes
Tuple size: 40 bytes
"""
โปรแกรมเมอร์ Python ส่วนใหญ่คุ้นเคยกับโครงสร้างข้อมูลแบบ list แต่กับtuple อาจไม่มากนัก ทั้งคู่เป็น iterable เข้าถึงด้วย index ได้ และเก็บข้อมูลต่างชนิดร่วมกันได้ แต่มีสถานการณ์ที่ควรใช้ tuple แทน list
ก่อนอื่น list เป็นแบบเปลี่ยนค่าได้ หมายความว่าเราสามารถแก้ไขได้ตามต้องการ:
a = [1,2,3,4,5]
a[2] = 8
print(a)
"""
[1,2,8,4,5]
"""
ในทางกลับกัน tuple เปลี่ยนค่าไม่ได้ ซึ่งหมายความว่าหากพยายามแก้ไขจะเกิด TypeError
ด้วยเหตุนี้ tuple จึงใช้หน่วยความจำน้อยกว่า เพราะ Python สามารถจัดสรรบล็อกหน่วยความจำที่พอดีกับข้อมูลได้ ตรงกันข้าม list ต้องเผื่อหน่วยความจำไว้ในกรณีที่มีการขยายขนาด – ซึ่งเรียกว่า dynamic memory allocation
สรุปสั้น ๆ: ในกรณีที่ไม่ต้องการให้ข้อมูลถูกเปลี่ยน การใช้ tuple ควรได้รับการพิจารณามากกว่า list ด้วยเหตุผลด้านหน่วยความจำ อีกทั้ง tuple ยังเร็วกว่า list ด้วย
เรียนรู้เพิ่มเติมเกี่ยวกับ โครงสร้างข้อมูลใน Python ในบทเรียนนี้
#4 Generators
a = [x * 2 for x in range(10)]
b = (x * 2 for x in range(10))
print(a)
print(b)
"""
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x7f61f8808b50>
"""
List comprehension คือวิธีแบบไพธอนนิกในการสร้างลิสต์จาก iterable อื่น ๆ – เร็วกว่าการใช้ for loop มาก แต่ถ้าบังเอิญเปลี่ยนวงเล็บจาก [] เป็น () จะเกิดอะไรขึ้น? คุณจะได้ generator object
ใน Python วงเล็บกลมที่มีตรรกะแบบ list comprehension จะสร้างสิ่งที่เรียกว่า generator object Generator เป็น iterable ประเภทพิเศษ แตกต่างจากลิสต์ตรงที่ไม่เก็บรายการไว้ แต่เก็บคำสั่งสำหรับสร้างแต่ละองค์ประกอบตามลำดับและสถานะปัจจุบันของการวนซ้ำ
แต่ละองค์ประกอบจะถูกสร้างขึ้นเมื่อมีการร้องขอ โดยใช้เทคนิคที่เรียกว่า lazy evaluation ประโยชน์หลักของทริกนี้คือใช้หน่วยความจำต่ำกว่า เพราะไม่ต้องสร้างลำดับทั้งหมดในคราวเดียว
#5 Aliasing
a = [1, 2, 3, 4 ,5]
b = a
# Change the 4th index in b
b[4] = 7
print(id(a))
print(id(b))
print(a) # Remember we did not explicitly make changes to a.
"""
15136008
15136008
[1, 2, 3, 4, 7]
"""
Python เป็นภาษาเชิงวัตถุ – ทุกอย่างเป็นอ็อบเจ็กต์ ดังนั้น การกำหนดอ็อบเจ็กต์ให้กับตัวระบุจึงเป็นการสร้างอ้างอิงไปยังอ็อบเจ็กต์นั้น
เมื่อกำหนดตัวระบุหนึ่งไปยังอีกตัวระบุหนึ่ง เราจะได้ตัวระบุสองตัวที่อ้างถึงอ็อบเจ็กต์เดียวกัน นี่คือแนวคิดที่เรียกว่า aliasing การเปลี่ยนแปลงใน alias หนึ่งจะมีผลกับอีกอัน บางครั้งพฤติกรรมนี้ก็ตั้งใจ แต่ก็มักทำให้เผลอพลาดได้
วิธีแก้หนึ่งคือหลีกเลี่ยง aliasing เมื่อต้องใช้อ็อบเจ็กต์ที่เปลี่ยนค่าได้ อีกวิธีคือสร้างโคลนของอ็อบเจ็กต์ต้นฉบับแทนที่จะอ้างอิง
วิธีที่ง่ายที่สุดในการโคลนคือใช้ slicing:
b = a[:]
วิธีนี้จะสร้างการอ้างอิงใหม่ไปยังอ็อบเจ็กต์ลิสต์ในตัวระบุ b
คุณยังสามารถใช้วิธีอื่น ๆ ได้ เช่น เรียก list(a) ตอนกำหนดค่าให้ตัวระบุอื่น หรือใช้เมธอด copy()
#6 ตัวดำเนินการ ‘not’
a = []
print(not a)
"""
True
"""
ทริกถัดไปคือวิธีที่ง่ายที่สุดในการตรวจสอบว่าโครงสร้างข้อมูลว่างหรือไม่ ด้วยตัวดำเนินการ not ในตัวของ Python not เป็นตัวดำเนินการเชิงตรรกะที่คืนค่า True หากนิพจน์ไม่เป็นจริง มิฉะนั้นจะคืนค่า False – มันกลับค่าความจริงของนิพจน์บูลีนและอ็อบเจ็กต์
อีกวิธีการใช้ที่พบบ่อยคือใน if statement:
if not a:
# do something...
เมื่อ a เป็น True ตัวดำเนินการ not จะคืนค่า False และในทางกลับกัน
อาจจะงง ๆ หน่อย ลองฝึกใช้ดู
ทริกเกี่ยวกับสตริงและเอาต์พุต
#7 F-strings
first_name = "John"
age = 19
print(f"Hi, I'm {first_name} and I'm {age} years old!")
"""
Hi, I'm John and I'm 19 years old!
"""
บางครั้งเราอาจต้องจัดรูปแบบสตริง Python 3.6 แนะนำคุณสมบัติที่เรียกว่า f-strings เพื่อทำให้กระบวนการนี้ง่ายขึ้น จะเข้าใจวิธีใหม่นี้ได้ดีขึ้นหากรู้ว่าก่อนหน้าจัดรูปแบบสตริงกันอย่างไร
ก่อนหน้านี้จัดรูปแบบสตริงกันแบบนี้:
first_name = "John"
age = 19
print("Hi, I'm {} and I'm {} years old!".format(first_name, age))
"""
Hi, I'm John and I'm 19 years old!
"""
โดยรวม วิธีใหม่เร็วกว่า อ่านง่ายกว่า กระชับกว่า และพลาดได้ยากกว่า
การใช้ f-strings อีกแบบคือพิมพ์ชื่อตัวแปรพร้อมค่าของมัน ฟีเจอร์นี้เพิ่มเข้ามาใน Python 3.8
x = 10
y = 20
print(f"{x = }, {y = }")
"""
x = 10, y = 20
"""
ดูบทเรียนนี้เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ การจัดรูปแบบ F-string ใน Python
#8 พารามิเตอร์ ‘end’ ของฟังก์ชัน print()
languages = ["english", "french", "spanish", "german", "twi"]
print(' '.join(languages))
"""
english french spanish german twi
"""
มักพบว่าใช้คำสั่ง print โดยไม่กำหนดพารามิเตอร์เสริมใด ๆ ทำให้นักพัฒนา Python จำนวนไม่น้อยไม่รู้ว่าเราสามารถควบคุมเอาต์พุตได้ระดับหนึ่ง
พารามิเตอร์เสริมตัวหนึ่งที่เราปรับได้คือ end พารามิเตอร์ end ระบุว่าควรแสดงอะไรท้ายการเรียก print
ค่าเริ่มต้นของ end คือ "\n" ซึ่งบอก Python ให้ขึ้นบรรทัดใหม่ ในโค้ดด้านบนเราเปลี่ยนเป็นช่องว่าง จึงได้เอาต์พุตที่พิมพ์สมาชิกทั้งหมดของลิสต์ในบรรทัดเดียวกัน
#9 เพิ่มค่าลงใน tuple
a = (1, 2, [1, 2, 3])
a[2].append(4)
print(a)
"""
(1, 2, [1, 2, 3, 4])
"""
เราทราบแล้วว่า tuple เปลี่ยนค่าไม่ได้ – ดูทริก Python #3 List vs. Tuples การพยายามเปลี่ยนสถานะของ tuple จะเกิด TypeError แต่หากมอง tuple เป็นลำดับของชื่อที่ผูกกับอ็อบเจ็กต์ซึ่งเปลี่ยนการผูกไม่ได้ มุมมองจะต่างออกไป
สององค์ประกอบแรกของ tuple เป็นจำนวนเต็ม – เปลี่ยนค่าไม่ได้ ส่วนองค์ประกอบสุดท้ายเป็นลิสต์ ซึ่งเป็นอ็อบเจ็กต์แบบเปลี่ยนค่าได้ใน Python
ถ้ามองว่าลิสต์เป็นเพียงอีกชื่อหนึ่งในลำดับที่ผูกกับอ็อบเจ็กต์ซึ่งเปลี่ยนการผูกไม่ได้ เราจะเห็นว่ายังสามารถแก้ไขลิสต์จากภายใน tuple ได้
แนะนำให้ทำแบบนี้จริงไหม? อาจไม่ แต่เป็นความรู้น่ารู้!
#10 รวมพจนานุกรม
a = {"a": 1, "b": 2}
b = {"c": 3, "d": 4}
a_and_b = a | b
print(a_and_b)
"""
{"a": 1, "b": 2, "c": 3, "d": 4}
"""
ใน Python 3.9 ขึ้นไป สามารถรวมพจนานุกรมด้วย | (bitewise OR) ได้ ทริกนี้ไม่มีอะไรมาก นอกจากว่าอ่านง่ายขึ้นมาก!
ทริกด้านสไตล์โค้ดและไวยากรณ์
#11 ตัวดำเนินการแบบ ternary / นิพจน์เงื่อนไข
condition = True
name = "John" if condition else "Doe"
print(name)
"""
John
"""
ในโค้ดด้านบนคือสิ่งที่เรียกว่าตัวดำเนินการแบบ ternary หรือที่รู้จักกันว่า conditional expression ใช้ประเมินค่าโดยอ้างอิงจากเงื่อนไขว่าเป็น True หรือ False
อีกวิธีที่เขียนได้คือ:
condition = True
if condition:
name = "John"
else:
name = "Doe"
print(name)
"""
John
"""
แม้ทั้งสองแบบให้ผลลัพธ์เหมือนกัน แต่สังเกตว่าแบบ ternary ทำให้โค้ดสั้นและชัดเจนกว่า เป็นวิธีที่ Pythonic กว่า
#12 ลบค่าซ้ำในลิสต์
a = [1, 1, 2, 3, 4, 5, 5, 5, 6, 7, 2, 2]
print(list(set(a)))
"""
[1, 2, 3, 4, 5, 6, 7]
"""
วิธีที่ง่ายที่สุดในการลบค่าซ้ำจากลิสต์คือแปลงลิสต์เป็นเซ็ต (แล้วค่อยแปลงกลับเป็นลิสต์ถ้าต้องการ)
ตามคุณสมบัติการเปลี่ยนค่า เซ็ตและลิสต์ค่อนข้างคล้ายกันใน Python เราสามารถเพิ่มและลบองค์ประกอบจากทั้งสองโครงสร้างได้ตามต้องการ แต่ก็ยังต่างกันมาก
ลิสต์มีลำดับ มี index เริ่มที่ศูนย์ และเปลี่ยนค่าได้ เซ็ตไม่มีลำดับและไม่มี index องค์ประกอบในเซ็ตต้องเป็นชนิดที่เปลี่ยนค่าไม่ได้ แม้ตัวเซ็ตเองจะเปลี่ยนค่าได้ก็ตาม – การเรียกองค์ประกอบด้วย index หรือพยายามแก้ไของค์ประกอบจะทำให้เกิดข้อผิดพลาด
ความแตกต่างสำคัญอีกข้อคือ เซ็ตไม่สามารถมีค่าซ้ำได้ นี่แหละที่ช่วยให้เราลบค่าซ้ำจากลิสต์ได้
#13 ขีดล่างเดี่ยวแบบโดดเดี่ยว
>>> print(_)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
>>> 1 + 2
3
>>> print(_)
3
ขีดล่าง (_) เป็นตัวระบุที่ถูกต้องใน Python ดังนั้นจึงสามารถใช้เพื่ออ้างอิงอ็อบเจ็กต์ได้ แต่ขีดล่างยังมีหน้าที่อีกอย่าง: เก็บผลลัพธ์ของการประเมินค่าครั้งล่าสุด
เอกสารกำหนดไว้ว่า "ตัวแปลแบบโต้ตอบจะทำให้ผลลัพธ์ของการประเมินค่าครั้งล่าสุดพร้อมใช้งานในตัวแปร _ (เก็บอยู่ในโมดูล builtins เช่นเดียวกับฟังก์ชันในตัวอย่าง print)"
เนื่องจากเราไม่ได้กำหนดค่าให้ขีดล่างก่อนเรียกในบรรทัดแรก จึงเกิดข้อผิดพลาด อย่างไรก็ตามเมื่อคำนวณ 1 + 2 แล้ว ตัวแปลแบบโต้ตอบจะเก็บผลลัพธ์ไว้ในตัวระบุ _ ให้เรา
#14 ใช้ขีดล่างเพื่อไม่สนใจค่า
for _ in range(100):
print("The index doesn't matter")
"""
The index doesn't matter
The index doesn't matter
...
"""
ในทริก #13 เราพบว่าตัวแปลแบบโต้ตอบจะทำให้ผลลัพธ์ครั้งล่าสุดพร้อมในตัวระบุ (_) แต่นั่นไม่ใช่กรณีใช้งานเพียงอย่างเดียว
เรายังใช้มันแทนอ็อบเจ็กต์ที่เราไม่สนใจหรือจะไม่ใช้ต่อในโปรแกรมได้ สิ่งนี้สำคัญเพราะการใช้ตัวระบุทั่วไปแทนขีดล่าง (_) จะทำให้เกิดข้อผิดพลาด F841 เมื่อทำการ lint โปรแกรม F841 หมายถึงมีการกำหนดชื่อตัวแปรภายในไว้แต่ไม่ได้ใช้งาน ซึ่งเป็นแนวปฏิบัติที่ไม่ดี
#15 ขีดล่างปิดท้าย
list_ = [0, 1, 2, 3, 4]
global_ = "Hi there"
ต่อเนื่องจากสองทริกก่อนหน้า การใช้ขีดล่าง (_) ใน Python อีกวัตถุประสงค์หนึ่งคือหลีกเลี่ยงการชนกับคีย์เวิร์ดของภาษา
PEP 8 ระบุว่าขีดล่างปิดท้ายควร "ใช้โดยธรรมเนียมเพื่อหลีกเลี่ยงความขัดแย้งกับคีย์เวิร์ดของ Python" และยังกล่าวว่า "โดยทั่วไปควรเติมขีดล่างปิดท้ายเดี่ยว มากกว่าการใช้ตัวย่อหรือคำที่เพี้ยน ดังนั้น list_ จึงดีกว่า lst"
#16 ขีดล่างนำหน้า
class Example:
def __init__(self):
self._internal = 2
self.external = 20
มักพบว่าโปรแกรมเมอร์ Python ที่มีประสบการณ์มักเติมขีดล่างนำหน้าชื่ออัตลักษณ์หรือนามเมธอด – และมีเหตุผล
ขีดล่างที่นำหน้าตัวระบุหรือเมธอดมีนัยซ่อนอยู่: ตัวแปรหรือเมธอดนี้มีไว้ใช้ภายในเท่านั้น โดยสาระคือเป็นการประกาศให้โปรแกรมเมอร์คนอื่นทราบตามที่กำหนดไว้ใน PEP 8 แต่ Python ไม่ได้บังคับใช้ ดังนั้นขีดล่างนำหน้าจึงเป็นตัวบ่งชี้ที่อ่อน
ต่างจาก Java, Python ไม่มีการแยกที่แข็งแรงระหว่างตัวแปร private และ public กล่าวคือมันมีความหมายก็เพราะชุมชน Python ตกลงร่วมกัน การใส่ขีดล่างไม่ได้ส่งผลต่อพฤติกรรมของโปรแกรม
#17 ขีดล่างเพื่อความอ่านง่าย
นี่คือทริกสุดท้ายเกี่ยวกับขีดล่าง; ที่ผ่านมาเราครอบคลุมกรณีใช้งาน 3 แบบของขีดล่างแล้ว แต่สามารถดูบทเรียนของเราเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ บทบาทของขีดล่าง(_) ใน Python
number = 1_500_000
print(number)
"""
15000000
"""
อีกวิธีในการใช้ขีดล่างคือเป็นตัวคั่นเพื่อความอ่านง่ายสำหรับการจัดกลุ่มหลักตัวเลขในลิตภพจน์จำนวนเต็ม ทศนิยม และจำนวนเชิงซ้อน – เพิ่มเข้ามาใน Python 3.6
แนวคิดนี้เพื่อช่วยให้ค่าตัวเลขยาว ๆ อ่านง่ายขึ้น หรือค่าที่ควรแยกเป็นส่วน ๆ อย่างชัดเจน – อ่านเพิ่มเติมได้ใน PEP 515
ทริกด้านสไตล์โค้ดและไวยากรณ์
#18 __name__ == “__main__”
if __name__ == "__main__":
print("Read on to understand what is going on when you do this.")
"""
print("Read on to understand what is going on when you do this.")
"""
มีโอกาสสูงที่คุณจะเคยเห็นไวยากรณ์นี้ในโปรแกรม Python หลายตัว Python ใช้ชื่อตัวพิเศษ "__main__" และตั้งค่าให้กับตัวระบุ __name__ หากไฟล์ Python ที่รันเป็นโปรแกรมหลัก
หากนำโมดูลที่แสดงในภาพหน้าจอไป import ในโมดูลอื่น (ไฟล์ Python อื่น) แล้วรันไฟล์นั้น ความจริงของนิพจน์ในโค้ดของเราจะเป็นเท็จ เพราะเมื่อเรา import จากโมดูลอื่น ตัวระบุ __name__ จะถูกตั้งเป็นชื่อของโมดูล (ไฟล์ Python)
#19 เมธอด ‘setdefault’
import pprint
text = "It's the first of April. It's still cold in the UK. But I'm going to the museum so it should be a wonderful day"
counts = {}
for word in text.split():
counts.setdefault(word, 0)
counts[word] += 1
pprint.pprint(counts)
"""
{'April.': 1,
'But': 1,
"I'm": 1,
"It's": 2,
'UK.': 1,
'a': 1,
'be': 1,
'cold': 1,
'day': 1,
'first': 1,
'going': 1,
'in': 1,
'it': 1,
'museum': 1,
'of': 1,
'should': 1,
'so': 1,
'still': 1,
'the': 3,
'to': 1,
'wonderful': 1}
"""
บางครั้งเราอยากตั้งค่าค่าสำหรับหลายคีย์ในพจนานุกรม เช่น ตอนนับคำในคลังข้อความ วิธีทั่วไปทำได้ดังนี้:
- ตรวจว่าคีย์มีอยู่ในพจนานุกรมหรือไม่
- ถ้ามี ให้เพิ่มค่าไป 1
- ถ้าไม่มี ให้เพิ่มคีย์นั้นและตั้งค่าเป็น 1
หน้าตาโค้ดจะเป็นแบบนี้:
counts = {}
for word in text.split():
if word in counts:
counts[word] += 1
else:
counts[word] = 1
วิธีที่กระชับกว่าคือใช้เมธอด setdefault() กับอ็อบเจ็กต์พจนานุกรมของคุณ
อาร์กิวเมนต์ตัวแรกคือคีย์ที่ต้องการตรวจสอบ อาร์กิวเมนต์ตัวที่สองคือค่าที่จะตั้งให้คีย์ถ้าคีย์ยังไม่มีอยู่ในพจนานุกรม – หากคีย์มีอยู่แล้ว เมธอดจะคืนค่าของคีย์นั้น จึงไม่ถูกเปลี่ยน
ทริกเกี่ยวกับโครงสร้างโปรแกรม
#20 การจับคู่ regex
import re
number = re.compile(r"(0)?(\+44)?\d{10}")
num_1 = number.search("My number is +447999999999")
num_2 = number.search("My number is 07999999999")
print(num_1.group())
print(num_2.group())
"""
'+447999999999'
'07999999999'
"""
Regular expressions ช่วยให้ระบุรูปแบบของข้อความที่ต้องการค้นหาได้ คนส่วนใหญ่รู้ว่าเราค้นหาด้วย CTRL + F (Windows) ได้ แต่ถ้าไม่รู้แน่ชัดว่ากำลังหาสิ่งใด จะหาอย่างไร? คำตอบคือค้นหารูปแบบ
ตัวอย่างเช่น หมายเลขโทรศัพท์สหราชอาณาจักรมีรูปแบบคล้ายกัน: จะมีเลขศูนย์ข้างหน้าและตามด้วยสิบหลัก หรือ +44 แทนศูนย์แล้วตามด้วยสิบหลัก – แบบหลังคือรูปแบบสากล
Regex ช่วยประหยัดเวลาอย่างมาก หากต้องเขียนกฎเพื่อจับกรณีในภาพแทน regex อาจกินโค้ด 10+ บรรทัด
การเรียนรู้การทำงานของ regex สำคัญแม้ไม่เขียนโค้ดก็ตาม โปรแกรมแก้ไขข้อความสมัยใหม่จำนวนมากรองรับการใช้ regex กับฟังก์ชันค้นหาและแทนที่
#21 เครื่องหมายท่อ (pipe) ใน regex
import re
heros = re.compile(r"Super(man|woman|human)")
h1 = heros.search("This will find Superman")
h2 = heros.search("This will find Superwoman")
h3 = heros.search("This will find Superhuman")
print(h1.group())
print(h2.group())
print(h3.group())
"""
Superman
Superwoman
Superhuman
"""
Regex มีอักขระพิเศษที่เรียกว่า pipe (|) ที่ช่วยให้จับคู่หนึ่งในหลาย ๆ นิพจน์ได้ และใช้ได้ทุกที่ สะดวกมากเมื่อมีรูปแบบที่คล้ายกันหลายแบบ
เช่น 'Superman', 'Superwoman' และ 'Superhuman' มีคำนำหน้าเหมือนกัน จึงใช้ pipe เพื่อเก็บส่วนของรูปแบบที่ซ้ำ แล้วเปลี่ยนเฉพาะส่วนที่ต่างกัน ช่วยประหยัดเวลาอย่างยิ่ง
ข้อควรระวัง: หากนิพจน์ที่ต้องการจับคู่ทั้งหมดปรากฏในข้อความเดียวกัน การจับคู่ครั้งแรกที่พบจะถูกคืนค่า – เช่น "An example text containing Superwoman, Superman, Superhuman" จะคืนค่า Superwoman
#22 พารามิเตอร์ ‘sep’ ของฟังก์ชัน print()
day = "04"
month = "10"
year = "2022"
print(day, month, year)
print(day, month, year, sep = "")
print(day, month, year, sep = ".")
"""
04 10 2022
04/10/2022
04.10.2022
"""
จำนวนโปรแกรมเมอร์ Python ที่ไม่รู้ถึงความสามารถเต็มรูปแบบของ print() นั้นเยอะน่าตกใจ หาก “Hello World” คือโปรแกรมแรกของคุณ ฟังก์ชัน print() ก็คงเป็นหนึ่งในฟังก์ชันที่เรียนเป็นอย่างแรก เราใช้ print() เพื่อแสดงข้อความที่จัดรูปแบบบนหน้าจอ แต่ยังมีอีกมากที่ทำได้
ในโค้ดด้านบน เราแสดงวิธีต่าง ๆ ในการพิมพ์ข้อความที่จัดรูปแบบ พารามิเตอร์ sep เป็นอาร์กิวเมนต์เสริมใน print() ที่ให้กำหนดตัวคั่นระหว่างอ็อบเจ็กต์ได้เมื่อพิมพ์มากกว่าหนึ่งรายการ
ค่าเริ่มต้นคือเว้นวรรค แต่เราได้เปลี่ยนการทำงานนี้ในคำสั่ง print ของเรา – หนึ่งครั้งตั้ง sep เป็น "" และอีกครั้งตั้งเป็น "."
#23 Lambda functions
def square(num:int) -> int:
return num ** 2
print(f"Function call: {square(4)}")
"""
Function call: 16
"""
square_lambda = lambda x: x**2
print(f"Lambda function: {square_lambda(4)}")
"""
Lambda functional: 16
"""
Lambda functions พาคุณไปสู่สิ่งที่อยู่ในระดับกลางถึงขั้นสูงที่ทำได้ด้วย Python – เรียนรู้ Intermediate Python ด้วยคอร์สนี้ ตอนแรกอาจดูซับซ้อน แต่จริง ๆ แล้วเรียบง่าย
ในโค้ดตัวอย่างเราใช้เพียงอาร์กิวเมนต์เดียว แต่จริง ๆ ใช้ได้หลายตัวถ้าต้องการ:
square = lambda a, b: a ** b
print(f"Lambda function: {square(4, 2)}")
"""
16
"""
โดยสรุป คีย์เวิร์ด lambda อนุญาตให้สร้างฟังก์ชันขนาดเล็ก ข้อจำกัดสูง แบบไม่ระบุชื่อได้ในบรรทัดเดียว ทำงานเหมือนฟังก์ชันปกติที่ประกาศด้วย def ต่างกันที่ไม่มีชื่อ
#24 เมธอด ‘swapcase’
string = "SoMe RaNDoM sTriNg"
print(string.swapcase())
"""
sOmE rAndOm StRInG
"""
เมธอด swapcase() ใช้กับสตริงเพื่อสลับอักษรตัวใหญ่เป็นตัวเล็กและกลับกันในบรรทัดเดียว แม้จะมีเคสใช้งานไม่มาก แต่ก็น่ารู้ไว้
#25 เมธอด ‘isalnum’
password = "ABCabc123"
print(password.isalnum())
"""
True
"""
สมมติว่าเราสร้างโปรแกรมที่ให้ผู้ใช้ป้อนรหัสผ่าน และต้องเป็นการผสมระหว่างตัวเลขกับตัวอักษร เราทำได้ในบรรทัดเดียวด้วยการเรียก isalnum() บนอินสแตนซ์สตริง
เมธอดนี้ตรวจว่าทุกอักขระเป็นตัวอักษร (A-Za-z) หรือเลข (0-9) การมีช่องว่างหรือสัญลักษณ์ (!#%$&? เป็นต้น) จะคืนค่า False
#26 การจัดการข้อยกเว้น
def get_ration(x:int, y:int) -> int:
try:
ratio = x/y
except ZeroDivisionError:
y = y + 1
ratio = x/y
return ratio
print(get_ration(x=400, y=0))
"""
400.0
"""
โปรแกรม Python จะหยุดทำงานเมื่อพบข้อผิดพลาด
บางครั้งเราไม่ต้องการพฤติกรรมแบบนี้ เช่น เมื่อมีผู้ใช้ปลายทางโต้ตอบกับโค้ดของเรา จะเลวร้ายแค่ไหนหากโค้ดหยุดทำงานก่อนเวลาอันควร?
มีแนวคิดหลายแบบในการรับมือกับกรณีพิเศษ นักพัฒนา Python จำนวนมากมักยึดแนวคิดที่ว่า “ขออภัยทีหลังง่ายกว่าขออนุญาตก่อน” หมายถึงพวกเขาจะจับข้อผิดพลาดที่เกิดขึ้น โดยเขียนบริบทล้อมรอบที่สามารถจัดการข้อยกเว้นได้ แก่นความคิดคือไม่จำเป็นต้องเสียเวลาป้องกันทุกกรณียกเว้นที่เป็นไปได้
แต่นี่ใช้ได้ก็ต่อเมื่อมีวิธีรับมือหลังปัญหาเกิดขึ้นแล้ว
#27 หาองค์ประกอบที่ต่างกันระหว่างลิสต์
list_1 = [1, 3, 5, 7, 8]
list_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
solution_1 = list(set(list_2) - set(list_1))
solution_2 = list(set(list_1) ^ set(list_2))
solution_3 = list(set(list_1).symmetric_difference(set(list_2)))
print(f"Solution 1: {solution_1}")
print(f"Solution 2: {solution_2}")
print(f"Solution 3: {solution_3}")
"""
Solution 1: [9, 2, 4, 6]
Solution 2: [2, 4, 6, 9]
Solution 3: [2, 4, 6, 9]
"""
นี่คือสามวิธีในการเปรียบเทียบความแตกต่างระหว่างลิสต์สองรายการใน Python
หมายเหตุ: เว้นแต่ว่าคุณมั่นใจว่า list_1 เป็นสับเซ็ตของ list_2 มิฉะนั้น solution 1 จะไม่เหมือนกับอีกสองวิธี
#28 Args & kwargs
def some_function(*args, **kwargs):
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
some_function(1, 2, 3, a=4, b=5, c=6)
"""
Args: (1, 2, 3)
Kwargs: {'a': 4, 'b': 5, 'c': 6}
"""
เราใช้ *args และ **kwargs เป็นพารามิเตอร์ให้ฟังก์ชันเมื่อไม่ทราบจำนวนตัวแปรที่ฟังก์ชันควรรอรับ
พารามิเตอร์ *args อนุญาตให้ส่งพารามิเตอร์แบบจำนวนแปรผันเข้าไปได้เมื่อเป็นแบบไม่กำหนดชื่อ (non-keyworded) ส่วน **kwargs อนุญาตให้ส่งพารามิเตอร์แบบกำหนดชื่อได้ตามอำเภอใจ
ความจริงแล้ว คำว่า *args และ **kwargs ไม่ได้วิเศษอะไร: ความวิเศษอยู่ที่เครื่องหมายดอกจัน (*) ซึ่งหมายความว่าเราจะใช้คำใดก็ได้หลังเครื่องหมายดอกจัน แต่การใช้ args และ kwargs เป็นธรรมเนียม และได้รับการยึดถือในหมู่นักพัฒนา Python
#29 Ellipsis
print(...)
"""
Ellipsis
"""
def some_function():
...
# Alternative solution
def another_function():
pass
Ellipsis เป็นอ็อบเจ็กต์ใน Python ที่เรียกได้ด้วยการพิมพ์จุดสามจุด (...) หรือเรียกอ็อบเจ็กต์โดยตรง (Ellipsis)
การใช้งานที่โดดเด่นที่สุดคือการเข้าถึงและ slice อาเรย์หลายมิติใน NumPy ตัวอย่างเช่น:
import numpy as np
arr = np.array([[2,3], [1,2], [9,8]])
print(arr[...,0])
"""
[2 1 9]
"""
print(arr[...])
"""
[[2 3]
[1 2]
[9 8]]
"""
การใช้งานอีกอย่างของ Ellipsis คือเป็นที่คั่นในฟังก์ชันที่ยังไม่ได้ implement
หมายความว่าคุณจะใช้ Ellipsis, ... หรือ pass ก็ยังถูกต้องทั้งหมด
#30 List comprehension
even_numbers = [x for x in range(10) if x % 2 == 0 and x != 0]
print(even_numbers)
"""
[2, 4, 6, 8]
"""
ทริกสุดท้ายคือ list comprehension ซึ่งเป็นวิธีที่สง่างามในการสร้างลิสต์จากลำดับอื่น ช่วยให้คุณทำตรรกะและการกรองขั้นซับซ้อนได้ดังที่ทำในโค้ดด้านบน
มีวิธีอื่นที่ทำได้เหมือนกัน ตัวอย่างเช่น เราอาจใช้ lambda ดังนี้:
even_numbers = list(filter(lambda x: x % 2 ==0 and x != 0, range(10)))
print(even_numbers)
"""
[0, 2, 4, 6, 8]
"""
แต่ชาว Python จำนวนมากเห็นว่าวิธีนี้อ่านยากกว่า list comprehension มาก
FAQs
ทริก Python ใดที่มีประโยชน์ที่สุดสำหรับผู้เริ่มต้น?
F-strings น่าจะมีประโยชน์ทันทีที่สุด ช่วยให้การจัดรูปแบบสตริงเร็วขึ้น อ่านง่ายขึ้น และพลาดได้ยากกว่าแนวทางเก่าอย่าง .format().
จะสลับค่าตัวแปรสองตัวใน Python อย่างเร็วที่สุดได้อย่างไร?
ใช้การกำหนดค่าพร้อมกัน: a, b = b, a ไม่ต้องใช้ตัวแปรชั่วคราว — Python ประเมินค่าด้านขวาจนครบก่อนแล้วจึงกำหนดค่า
จะรวมพจนานุกรมสองตัวในบรรทัดเดียวได้อย่างไร?
ใน Python 3.9+ ใช้ตัวดำเนินการ |: merged = dict_a | dict_b สำหรับเวอร์ชันเก่า ใช้ {**dict_a, **dict_b}.
ความแตกต่างระหว่าง *args และ **kwargs คืออะไร?
*args รวบอาร์กิวเมนต์แบบตำแหน่งส่วนเกินเป็นทูเพิล ส่วน **kwargs รวบอาร์กิวเมนต์แบบกำหนดชื่อส่วนเกินเป็นพจนานุกรม ความพิเศษอยู่ที่ตัวดำเนินการ * และ ** ไม่ใช่ที่คำ — ตั้งชื่อเป็นอย่างอื่นก็ได้
จะตรวจสอบว่าลิสต์ว่างใน Python ได้อย่างไร?
ใช้ตัวดำเนินการ not: if not my_list: วิธีนี้ Pythonic กว่าการเช็กแบบ len(my_list) == 0.
ความแตกต่างระหว่าง list comprehension กับ generator คืออะไร?
List comprehension (วงเล็บเหลี่ยม) สร้างลิสต์ทั้งก้อนในหน่วยความจำทันที ส่วน generator (วงเล็บกลม) สร้างค่าทีละรายการเมื่อถูกเรียกใช้ จึงใช้หน่วยความจำต่ำกว่าสำหรับลำดับที่ยาวมาก
ควรใช้ตัวดำเนินการ ternary เมื่อไร?
ใช้กับเงื่อนไขสั้น ๆ ที่ผลลัพธ์ทั้งสองด้านสั้นและชัดเจน: name = "John" if condition else "Doe" ถ้าซับซ้อนกว่านั้น ใช้บล็อก if/else ปกติจะอ่านง่ายกว่า
วิธีที่ง่ายที่สุดในการลบค่าซ้ำจากลิสต์คืออะไร?
ห่อด้วย set() แล้วแปลงกลับ: list(set(my_list)) แต่โปรดทราบว่าเซ็ตไม่มีลำดับ ดังนั้นลำดับเดิมจะไม่ถูกเก็บไว้