ใน Python นั้นง่ายต่อการใช้สัญกรณ์ความเข้าใจรายการเมื่อสร้างรายการใหม่(List comprehensions
)
- 5. Data Structures — List Comprehensions — Python 3.10.0 Documentation
- 6. Expressions — Displays for lists, sets and dictionaries — Python 3.10.0 Documentation
ในบทความนี้ เราจะมาพูดถึงสิ่งต่อไปนี้ก่อน
- ประเภทพื้นฐานของสัญกรณ์ความเข้าใจรายการ
- แสดงรายการสัญกรณ์ความเข้าใจที่มีการแตกแขนงแบบมีเงื่อนไขโดย if
- ผสมผสานกับโอเปอเรเตอร์แบบไตรภาค (หากการประมวลผลคล้ายคลึงกัน)
zip()
,enumerate()
ผสมผสานกับสิ่งเหล่านี้- สัญกรณ์การรวมรายการที่ซ้อนกัน
ต่อไป เราจะอธิบายชุดของสัญลักษณ์แสดงความเข้าใจรายการพร้อมโค้ดตัวอย่าง
- ตั้งค่าสัญกรณ์รวม(
Set comprehensions
) - สัญกรณ์รวมพจนานุกรม(
Dict comprehensions
) - ประเภทเครื่องกำเนิดไฟฟ้า(
Generator expressions
)
- ประเภทพื้นฐานของสัญกรณ์ความเข้าใจรายการ
- แสดงรายการสัญกรณ์ความเข้าใจที่มีการแตกแขนงแบบมีเงื่อนไขโดย if
- ผสมผสานกับโอเปอเรเตอร์แบบไตรภาค (หากการประมวลผลคล้ายคลึงกัน)
- รวมกับ zip() และ enumerate()
- สัญกรณ์การรวมรายการที่ซ้อนกัน
- ตั้งค่าสัญกรณ์รวม(Set comprehensions)
- สัญกรณ์รวมพจนานุกรม(Dict comprehensions)
- ประเภทเครื่องกำเนิดไฟฟ้า(Generator expressions)
ประเภทพื้นฐานของสัญกรณ์ความเข้าใจรายการ
สัญกรณ์ความเข้าใจรายการเขียนดังนี้
[Expression for Any Variable Name in Iterable Object]
ใช้แต่ละองค์ประกอบของอ็อบเจ็กต์ที่ iterable เช่น list, tuple หรือ range ด้วยชื่อตัวแปรที่กำหนดเอง แล้วประเมินค่าด้วยนิพจน์ รายการใหม่ที่มีผลการประเมินเป็นองค์ประกอบจะถูกส่งกลับ
ตัวอย่างจะได้รับพร้อมกับคำสั่งที่เทียบเท่า
squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
squares.append(i**2)
print(squares)
# [0, 1, 4, 9, 16]
กระบวนการเดียวกันสามารถทำได้ด้วย map() แต่ควรใช้สัญลักษณ์แสดงความเข้าใจรายการเนื่องจากความเรียบง่ายและความชัดเจน
แสดงรายการสัญกรณ์ความเข้าใจที่มีการแตกแขนงแบบมีเงื่อนไขโดย if
การแตกแขนงแบบมีเงื่อนไขด้วย if เป็นไปได้เช่นกัน เขียน if ใน postfix ดังนี้
[Expression for Any Variable Name in Iterable Object if Conditional Expression]
เฉพาะอิลิเมนต์ของอ็อบเจ็กต์ iterable ที่มีนิพจน์เงื่อนไขเป็นจริงเท่านั้นที่จะถูกประเมินโดยนิพจน์ และรายการใหม่ที่มีองค์ประกอบที่เป็นผลลัพธ์จะถูกส่งกลับ
คุณสามารถใช้ชื่อตัวแปรใดก็ได้ในนิพจน์เงื่อนไข
ตัวอย่างจะได้รับพร้อมกับคำสั่งที่เทียบเท่า
odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
if i % 2 == 1:
odds.append(i)
print(odds)
# [1, 3, 5, 7, 9]
กระบวนการเดียวกันสามารถทำได้ด้วย filter() แต่ควรใช้สัญลักษณ์แสดงความเข้าใจรายการเนื่องจากความเรียบง่ายและชัดเจน
ผสมผสานกับโอเปอเรเตอร์แบบไตรภาค (หากการประมวลผลคล้ายคลึงกัน)
ในตัวอย่างข้างต้น จะมีการประมวลผลเฉพาะองค์ประกอบที่ตรงตามเกณฑ์เท่านั้น และองค์ประกอบที่ไม่ตรงตามเกณฑ์จะไม่รวมอยู่ในรายการใหม่
หากคุณต้องการเปลี่ยนกระบวนการโดยขึ้นอยู่กับเงื่อนไข หรือหากคุณต้องการประมวลผลองค์ประกอบที่ไม่เป็นไปตามเงื่อนไขอย่างแตกต่าง ให้ใช้ตัวดำเนินการ ternary
ใน Python ตัวดำเนินการ ternary สามารถเขียนได้ดังนี้
Value When True if Conditional Expression else Value When False
ใช้ในส่วนนิพจน์ของสัญกรณ์ความเข้าใจรายการดังที่แสดงด้านล่าง
[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]
ตัวอย่างจะได้รับพร้อมกับคำสั่งที่เทียบเท่า
odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
if i % 2 == 1:
odd_even.append('odd')
else:
odd_even.append('even')
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
นอกจากนี้ยังสามารถเขียนนิพจน์โดยใช้ชื่อตัวแปรที่กำหนดเองสำหรับค่าจริงและค่าเท็จ
หากเป็นไปตามเงื่อนไข การประมวลผลบางอย่างจะเสร็จสิ้น มิฉะนั้น ค่าของอ็อบเจ็กต์ที่ทำซ้ำได้เดิมจะไม่เปลี่ยนแปลง
odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]
รวมกับ zip() และ enumerate()
ฟังก์ชันที่มีประโยชน์ที่มักใช้ในคำสั่ง for ได้แก่ zip() ซึ่งรวม iterables หลายรายการ และ enumerate() ซึ่งจะคืนค่าพร้อมกับดัชนี
แน่นอน มันเป็นไปได้ที่จะใช้ zip() และ enumerate() ด้วยสัญลักษณ์รายการความเข้าใจ ไม่ใช่ไวยากรณ์พิเศษ และไม่ยากหากคุณพิจารณาการโต้ตอบกับคำสั่ง for
ตัวอย่างของ zip()
l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']
l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
l_zip.append((s1, s2))
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
ตัวอย่างของการแจกแจง ()
l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
l_enu.append((i, s))
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
แนวคิดจะเหมือนกับเมื่อก่อนเมื่อใช้ if
l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]
แต่ละองค์ประกอบสามารถใช้ในการคำนวณองค์ประกอบใหม่ได้
l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]
l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]
สัญกรณ์การรวมรายการที่ซ้อนกัน
เช่นเดียวกับการซ้อนลูป สัญกรณ์ความเข้าใจรายการสามารถซ้อนได้
[Expression for Variable Name 1 in Iterable Object 1
for Variable Name 2 in Iterable Object 2
for Variable Name 3 in Iterable Object 3 ... ]
เพื่อความสะดวก มีการเพิ่มตัวแบ่งบรรทัดและการเยื้อง แต่ไม่จำเป็นสำหรับไวยากรณ์ พวกเขาสามารถดำเนินการต่อในบรรทัดเดียว
ตัวอย่างจะได้รับพร้อมกับคำสั่งที่เทียบเท่า
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
for x in row:
flat.append(x)
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
นอกจากนี้ยังสามารถใช้หลายตัวแปรได้
cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
คุณยังสามารถทำการแตกแขนงตามเงื่อนไขได้อีกด้วย
cells = [(row, col) for row in range(3)
for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]
นอกจากนี้ยังสามารถแยกสาขาตามเงื่อนไขสำหรับแต่ละวัตถุที่ทำซ้ำได้
cells = [(row, col) for row in range(3) if row % 2 == 0
for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]
ตั้งค่าสัญกรณ์รวม(Set comprehensions)
การเปลี่ยนเครื่องหมายวงเล็บเหลี่ยม [] ในรายการแสดงความเข้าใจเป็นวงเล็บปีกกา {} สร้างชุด (วัตถุประเภทชุด)
{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}
print(s)
# {0, 1, 4, 9, 16}
สัญกรณ์รวมพจนานุกรม(Dict comprehensions)
พจนานุกรม (อ็อบเจ็กต์ประเภท dict) สามารถสร้างได้ด้วยสัญกรณ์ความเข้าใจ
{} และระบุคีย์และค่าในส่วนนิพจน์เป็นคีย์: ค่า
{Key: Value for Any Variable Name in Iterable Object}
สามารถระบุนิพจน์สำหรับคีย์และค่าได้
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
หากต้องการสร้างพจนานุกรมใหม่จากรายการคีย์และค่า ให้ใช้ฟังก์ชัน zip()
keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}
ประเภทเครื่องกำเนิดไฟฟ้า(Generator expressions)
หากวงเล็บเหลี่ยม [] ในรายการเข้าใจสัญกรณ์ถูกใช้เป็นวงเล็บเหลี่ยม () ตัวสร้างจะถูกส่งคืนแทนทูเปิล นี่เรียกว่านิพจน์ตัวสร้าง
ตัวอย่างสัญกรณ์ความเข้าใจรายการ
l = [i**2 for i in range(5)]
print(l)
# [0, 1, 4, 9, 16]
print(type(l))
# <class 'list'>
ตัวอย่างของนิพจน์ตัวสร้าง หากคุณพิมพ์ () ตัวสร้างตามที่เป็นอยู่ มันจะไม่พิมพ์เนื้อหาออกมา แต่ถ้าคุณเรียกใช้ด้วยคำสั่ง for คุณสามารถรับเนื้อหาได้
g = (i**2 for i in range(5))
print(g)
# <generator object <genexpr> at 0x10af944f8>
print(type(g))
# <class 'generator'>
for i in g:
print(i)
# 0
# 1
# 4
# 9
# 16
นิพจน์ตัวสร้างยังอนุญาตให้มีการแตกแขนงตามเงื่อนไขและการซ้อนโดยใช้ if เช่นเดียวกับรายการสัญกรณ์ความเข้าใจ
g_cells = ((row, col) for row in range(0, 3)
for col in range(0, 2) if col == row)
print(type(g_cells))
# <class 'generator'>
for i in g_cells:
print(i)
# (0, 0)
# (1, 1)
ตัวอย่างเช่น หากรายการที่มีองค์ประกอบจำนวนมากถูกสร้างขึ้นโดยใช้สัญลักษณ์ความเข้าใจของรายการแล้ววนซ้ำด้วยคำสั่ง for รายการที่มีองค์ประกอบทั้งหมดจะถูกสร้างขึ้นที่จุดเริ่มต้นหากใช้สัญลักษณ์ความเข้าใจรายการ ในทางกลับกัน หากคุณใช้นิพจน์ตัวสร้าง ทุกครั้งที่มีการวนซ้ำ องค์ประกอบจะถูกสร้างขึ้นทีละตัว ซึ่งจะเป็นการลดจำนวนหน่วยความจำที่ใช้
หากนิพจน์ตัวสร้างเป็นอาร์กิวเมนต์เดียวของฟังก์ชัน วงเล็บกลม () สามารถละเว้นได้
print(sum([i**2 for i in range(5)]))
# 30
print(sum((i**2 for i in range(5))))
# 30
print(sum(i**2 for i in range(5)))
# 30
สำหรับความเร็วในการประมวลผล สัญกรณ์ความเข้าใจรายการมักจะเร็วกว่าสัญกรณ์ตัวสร้างเมื่อองค์ประกอบทั้งหมดได้รับการประมวลผล
อย่างไรก็ตาม เมื่อตัดสินด้วย all() หรือ any() ตัวอย่างเช่น ผลลัพธ์จะถูกกำหนดเมื่อมีเท็จหรือจริง ดังนั้นการใช้นิพจน์ตัวสร้างอาจเร็วกว่าการใช้สัญกรณ์ความเข้าใจรายการ
ไม่มีสัญกรณ์ความเข้าใจทูเปิล แต่ถ้าคุณใช้นิพจน์ตัวสร้างเป็นอาร์กิวเมนต์ของทูเปิล () คุณสามารถสร้างทูเพิลในสัญกรณ์ความเข้าใจ
t = tuple(i**2 for i in range(5))
print(t)
# (0, 1, 4, 9, 16)
print(type(t))
# <class 'tuple'>