เจาะลึกการใช้งาน List, Tuple, Dictionary และ Set ในภาษา Python
ข้อมูลถือเป็นสิ่งสำคัญในทุกภาษาไม่ว่านั่นจะเป็นภาษามนุษย์หรือภาษาโปรแกรมก็ตาม
วัตถุต่างๆบนโลกเราสามารถปาดนิ้วไปชี้พร้อมบอกว่า นี่คือหมู นั่นคือหมา และโน่นคือนายกได้ ลักษณะแบบนี้คือการบ่งชี้ข้อมูลตัวเดียว
นายกไม่ได้มีแค่คนเดียวฉันใด ข้อมูลก็ไม่จำเป็นต้องอยู่โดดเดี่ยวฉันนั้น เราสามารถรวมกลุ่มสิ่งของที่สัมพันธ์กันเพื่อจัดเป็นก้อนเดียวกันได้ เช่น ขีปนาวุธ 5 ลูก เครื่องบินรบอีก 9 ลำ พร้อมกับหมูคุโรบูตะอีกพันล้านตัว แบบนี้เราเรียกว่าคอลเล็กชัน (Collection)
คอลเล็กชันที่สำคัญในภาษา Python ที่เราจะหยิบยกมาพูดถึงกัน ได้แก่ List, Tuple, Dictionary และ Set และเนื่องจากบทความนี้เป็นบทความต่อเนื่องจาก เจาะลึก classes และ objects ใน Python 3 ผู้อ่านจึงควรอ่านบทความดังกล่าวก่อนครับ เพื่อป้องกันสภาวะตบะแตกที่จะพึงเจอได้ในบทความนี้
ชนิดข้อมูลแบบ Mutable และ Immutablea
ชนิดข้อมูลในภาษา Python นั้น หลักๆเราแบ่งได้เป็นสองประเภท คือ ชนิดข้อมูลแบบเปลี่ยนแปลงได้ (Mutable Types) และชนิดข้อมูลแบบเปลี่ยนแปลงไม่ได้ (Immutable Types)
str หรือข้อมูลประเภทข้อความเป็นตัวอย่างของชนิดข้อมูลแบบ Immutable เราเข้าถึงค่าข้อมูลของมันได้ แต่ไม่สามารถแก้ไขได้
1s = 'Hello World'2s[0] # 'H'3s[0] = 'Y' # TypeError: 'str' object does not support item assignment
จากตัวอย่างข้างต้นเมื่อเราเข้าถึงค่า S[0]
เราสามารถทำได้เพราะเป็นการดึงค่าอักขระตัวแรกออกมาจากข้อความ แต่เราไม่สามารถเขียนค่าทับไปที่ S[0]
ได้ นั่นเพราะ str เป็น ชนิดข้อมูลแบบ Immutable นั่นเอง
ไม่ใช่สำหรับข้อมูลประเภท List ที่เป็นชนิดข้อมูลแบบ Mutable เราจึงสามารถเปลี่ยนแปลงค่าของมันได้
1lst = ['A', 'B', 'Z']2lst[2] = 'C' # lst เปลี่ยนเป็น ['A', 'B', 'C']
บทความนี้เราจะได้เรียนรู้คอลเล็กชันต่างๆ โดยมี list, dict และ set เป็นชนิดข้อมูลแบบ Mutable ส่วนของ tuple นั้นจะเป็นตัวอย่างของคอลเล็กชันที่มีประเภทเป็นชนิดข้อมูลแบบ Immutable นั่นเอง
ข้อมูลประเภท List
ลิสต์นั้นเป็นข้อมูลแบบ Mutable เราจึงสามารถแก้ไข เพิ่ม หรือลบ ข้อมูลออกจากลิสต์ได้
การสร้างลิสต์นั้นก็ไม่แตกต่างจากการสร้างอาร์เรย์ในภาษาอื่น เช่น
1l1 = [1, 2, 3]2l2 = ['Java', 'Python', 123] # สามารถสร้างลิสต์ที่เก็บข้อมูลต่างชนิดกันได้
นอกจากนี้เรายังสามารถสร้างลิสต์โดยอาศัยคอนสตรัคเตอร์ของคลาส list ได้ เช่น
1l1 = list('abc') # ['a', 'b', 'c']
กลไกการเข้าถึงและเปลี่ยนแปลงค่าลิสต์ก็เป็นธรรมชาติแบบเดียวกับที่ปรากฎในภาษาอื่น
1l = ['Java', 'Python', 'C++']2print(l[0]) # Java34l[1] = 'Ruby'5print(l) # ['Java', 'Ruby', 'C++']
ทว่ารูปแบบการใช้งานเช่นนี้ในภาษาโปรแกรมอื่นเราเรียกว่าอาร์เรย์ แต่ทำไม Python จึงเรียกสิ่งนี้ว่าลิสต์? แท้จริงแล้วลิสต์เป็น Random Access แบบอาร์เรย์ หรือว่าเป็น Sequential Access แบบ Linked List กันแน่?
List เป็น Random Access และไม่ใช่ Linked List
วิธีการสร้างลิสต์แบบที่แสดงในตัวอย่างข้างต้นนั้นแท้จริงแล้วเราสามารถสร้างได้สองแบบ วิธีแรกคือการใช้ Linked List ดังนี้
1lst = [1, 2, 3]
จากรูปข้างต้นจะพบว่า lst
เป็นเพียงชื่อที่อ้างอิงถึงส่วนหัวของลิสต์เท่านั้น เมื่อเราต้องการเข้าถึงข้อมูลตัวใดเราต้องไล่จากส่วนหัวไปเรื่อยๆจนกว่าจะพบข้อมูลตัวที่ต้องการ เช่น หากต้องการเลข 3 เราต้องวนไล่จากหัวของลิสต์ ผ่านเลข 1 และ 2 ก่อนจะค้นพบข้อมูลคือเลข 3 ในลำดับถัดไป วิธีการแบบนี้เราเรียกว่าเป็น Sequential Access
วิธีการสร้างลิสต์แบบ Sequential Access นั้นจะมีผลต่อประสิทธิภาพของการใช้งาน เราต้องไล่ค้นหาจากต้นลิสต์เสมอ และต้องไล่จากต้นลิสต์ไปจนถึงท้ายลิสต์ก่อนจะทำการเพิ่มของชิ้นถัดไปเพื่อต่อท้ายลิสต์เช่นกัน Python จึงเลือกที่จะสร้างลิสต์ด้วยวิธีการแบบที่สองคือ Random Access
CPython (Official Python ที่เราโหลดใช้งานกันจากเว็บหลัก) เลือกสร้างลิสต์ให้เหมือนอาร์เรย์ในภาษา C
ในแต่ละช่องของลิสต์จะมีขนาดเท่าๆกัน โดย lst
จะทำการชี้อยู่ที่หัวลิสต์ซึ่งมีที่อยู่ที่แน่นอนเป็น Memory Address ตามตัวอย่างคือ 0x123
ในการเข้าถึงอีลีเมนต์ตัวไหนของลิสต์เราจะใช้เวลาในการเข้าถึงเท่ากันทั้งสิ้น นั่นเพราะเราทราบจุดเริ่มต้นของลิสต์ (0x123) และทราบว่าแต่ละช่องของลิสต์มีขนาดเท่ากัน จึงสามารถเข้าสมการ 0x123 + (ความกว้างของช่อง x เลขช่องที่ต้องการเข้าถึง)
เพื่อกระโดดไปยังช่องนั้นได้อย่างรวดเร็ว
จากบทความก่อนหน้า เราทราบกันไปแล้วว่าทุกสรรพสิ่งใน Python เป็นออบเจ็กต์ ดังนั้นสิ่งที่เก็บอยู่ในแต่ละช่องของลิสต์นั้นแท้จริงแล้วจึงไม่ใช่ตัวเลขดังที่ปรากฎ แต่มันคือตัวชี้ (pointer) ที่ชี้ไปหาออบเจ็กต์ของตัวเลขอีกทีนั่นเอง
List กับ Dunder Methods
การเข้าถึงค่าต่างๆในลิสต์นั้น เราสามารถใช้ subscription ได้ด้วยการใส่ []
ดังนี้
1lst = ['Python', 'Java', 'C#']23lst[0] # Python
Python ไม่ได้สร้างไวยากรณ์ใหม่คือ []
ขึ้นมาเพื่อจำเพาะกับลิสต์เท่านั้น แต่ []
ยังสามารถนำไปใช้กับออบเจ็กต์อื่นๆได้หากออบเจ็กต์นั้นมีการสร้างเมธอด __getitem__
เช่น
1class Double:2 def __getitem__(self, index):3 return index * 245double = Double()67print(double[3]) # 6
คลาส Double มีการสร้างเมธอด __getitem__
เราจึงอนุมานได้ว่าเมื่อมีการเรียก []
เมธอดดังกล่าวจะถูกเรียกตาม ผลลัพธ์จากสิ่งที่คนกลับของเมธอดนี้ ก็จะเป็นผลลัพธ์ของการใช้งาน []
เช่นกัน
เราสามารถใช้ตัวดำเนินการ +
และ *
กับลิสต์ได้เช่นกัน นั่นเพราะลิสต์มีการอิมพลีเมนต์ __add__
และ __mul__
เอาไว้
1lst = [1, 2, 3]2l2 = lst * 2
เราพบว่าผลลัพธ์จากการดำเนินการดังกล่าวจะเป็นการสร้างลิสต์ใหม่ให้เกิดขึ้น มิใช่การแก้ไขบนลิสต์เดิม
นอกจากการดำเนินการที่เป็นผลให้เกิดลิสต์ใหม่แล้ว Python ยังมีการดำเนินการประเภทอื่นที่เปลี่ยนแปลงโดยตรงบนลิสต์เดิม เช่น +=
1lst = [1, 2, 3]2lst += [4, 5, 6]
เช่นเดียวกับการบวกและการคูณ การบวกสะสม (+=
) ก็เป็นการเรียก dunder method ที่ชื่อว่า __iadd__
เช่นกัน ผลลัพธ์จากการทำงานจะบวกสะสมไปที่ลิสต์ตัวเดิม ดังนี้
List กับ Slicing
การเข้าถึงค่าข้อมูลภายใต้ลิสต์เราไม่จำเป็นต้องเข้าถึงครั้งละหนึ่งตัว Python มีกลไกของการทำ slice เพื่อเข้าถึงข้อมูลภายใต้ลิสต์ครั้งละหลายๆตัวได้
Slicing นั้นคือการระบุว่าต้องการอีลีเมนต์ตัวที่เท่าไหร่ของลิสต์ โดยสามารถระบุเงื่อนไขได้ถึงสามตัวด้วยกัน คือ จุดเริ่มต้น จุดสิ้นสุด และ step
1lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]23lst[0:3] # [1, 2, 3] เอาข้อมูลตั้งแต่ index ช่องที่ 0 จนถึง 3 โดยไม่รวมช่องที่ 34lst[3:5] # [4, 5] เอาข้อมูลตั้งแต่ index ช่องที่ 3 จนถึง 5 โดยไม่รวมช่องที่ 5
นอกจากนี้เรายังสามารถระบุจำนวนลบเข้าไปใน slice ได้เช่นเดียวกัน โดย -1 จะหมายถึงอินเด็กซ์ของอีลีเมนต์ตัวสุดท้ายในลิสต์
เมธอดที่สำคัญของ List
ลิสต์นั้นมีเมธอดให้เรียกใช้งานอยู่มากมาย แต่สำหรับบทความนี้เราจะมาทำความรู้จักเฉพาะกับเมธอดและฟังก์ชันที่เรียกใช้งานกันบ่อยๆ
ทั้ง list, tuple, dict และ set เราสามารถหาจำนวนอีลีเมนต์ได้ด้วยการเรียกฟังก์ชัน len
โดยฟังก์ชันดังกล่าว Python จะทำการเรียกเมธอด __len__
ภายใต้ออบเจ็กต์ของชนิดข้อมูลนั้นให้อีกที
1len([1, 2, 3, 4]) # 4
เราสามารถที่จะทำการเพิ่มอีลีเมนต์ใหม่เข้าไปในลิสต์ได้ด้วยการต่อท้ายลิสต์เดิมผ่านการใช้ append
1lst = [1, 2, 3]2lst.append(4)34print(lst) # [1, 2, 3, 4]
เราสามารถใช้ del
เพื่อทำการลบสมาชิกในตำแหน่งต่างๆของลิสต์ หรือใช้เมธอด remove
เพื่อลบค่าอีลีเมนต์ออกจากลิสต์นั้นก็ได้เช่นกัน
1languages = ['Python', 'Java', 'C++', 'Ruby']23del languages[1]4print(languages) # ['Python', 'C++', 'Ruby']56languages.remove('C++')7print(languages) # ['Python', 'Ruby']
เมื่อมีการลบสมาชิกจากตำแหน่ง ก็ต้องมีการเพิ่มสมาชิกในตำแหน่งที่ระบุ เราสามารถใช้เมธอด insert เพื่อเพิ่มสมาชิกใหม่เข้าไปยังตำแหน่งที่ระบุได้ สมาชิกเดิมที่เคยอยู่ในตำแหน่งดังกล่าวก็จะเคลื่อนถัดไปอีกหนึ่งตำแหน่ง
1languages = ['Python', 'Java', 'C++', 'Ruby']23languages.insert(0, 'Elixir')4print(languages) # ['Elixir', 'Python', 'Java', 'C++', 'Ruby']
reverse เป็นเมธอดสำหรับการเรียงลำดับแบบย้อนกลับของลิสต์
1lst = [1, 2, 3]2lst = lst.reverse()34print(lst) # [3, 2, 1]
แต่หากเราต้องการเรียงลำดับลิสต์โดยไม่ได้เรียงย้อนกลับแบบ reverse เราสามารถใช้ sort หรือ sorted ได้ โดย sorted จะทำการสร้างผลลัพธ์จากการเรียงลำดับเป็นลิสต์ใหม่ ในขณะที่ sort จะทำการแก้ไขด้วยการเรียงลำดับบนลิสต์เดิม
1lst = [4, 1, 3]23print(sorted(lst)) # [1, 3, 4]4print(lst) # [4, 1, 3]56lst.sort()7print(lst) # [1, 3, 4]
List Comprehension
ลิสต์นั้นเป็นข้อมูลแบบสายลำดับ (Sequential Types) เราจึงสามารถเข้าถึงข้อมูลแต่ละตัวของลิสต์แบบเป็นลำดับได้
เมื่อเราต้องการสร้างลิสต์ใหม่ด้วยการดำเนินการซักอย่างกับลิสต์ตัวเดิม เราสามารถวนลูปเพื่อเข้าถึงอีลีเมนต์แต่ละตัวของลิสต์เพื่อดำเนินการและสร้างผลลัพธ์ตามต้องการได้
1lst = [1, 2, 3, 4, 5]2new_lst = []34for item in lst:5 new_lst.append(item * 2)
จากตัวอย่างข้างต้น เราต้องการสร้างลิสต์ใหม่จากลิสต์เดิมด้วยการนำ 2 เข้าคูณทุกๆอีลีเมนต์ของลิสต์เดิม วิธีการนี้เราสามารถลดรูปเพื่อลดความซับซ้อนลงได้ด้วยการใช้ List Cpmprehension
List Comprehension เป็นวิธีการสร้างลิสต์แบบหนึ่งที่ช่วยให้เราสร้างลิสต์ใหม่ขึ้นมาได้ง่ายขึ้น จากตัวอย่างเดิมเราสามารถแก้ไขให้เป็นรูปแบบของ List Comprehension ได้ดังนี้
1lst = [1, 2, 3, 4, 5]2new_lst = [item * 2 for item in lst] # ทุกๆ item ใน lst ให้นำไปคูณสอง
Generator Expression
List Comprehension นั้นเป็นวิธีการสร้างลิสต์ขึ้นมาใหม่ทั้งก้อนทำให้เกิดผลเสียอย่างหนึ่งเมื่อเราไม่ต้องการใช้ข้อมูลทั้งหมดของลิสต์ในเวลานั้น
1for item in [item * 2 for item in lst]:2 print(item)
จากโค้ดข้างต้นพบว่าเราต้องการเข้าถึงค่าของ item ทีละตัว แต่การใช้ list comprehension นั้นจะเป็นการสร้างลิสต์ใหม่ทั้งหมดขึ้นมาก่อน หาก lst มีข้อมูลหนึ่งแสนตัว ลิสต์ใหม่จากการสร้างด้วย list comprehension ก็จะปรากฎแสนตัวก่อนการวนลูปเช่นกัน นับเป็นสิ่งที่สิ้นเปลืองเพราะเราต้องการข้อมูลแค่ครั้งละ item ไม่มีความจำเป็นใดๆที่ต้องสร้างลิสต์ทั้งหมดขึ้นมาก่อน
Generator Expression (ต่อไปนี้ขอเรียกว่า genexp) เกิดมาเพื่อแก้ไขปัญหานี้ การใช้งาน genexp เพียงแค่เปลี่ยนจาก []
เป็น ()
ดังนี้
1for item in (item * 2 for item in lst):2 print(item)
genexp จะทำการสร้างข้อมูลขึ้นทีละตัวเฉพาะส่วนที่ใช้ขณะนั้น เหตุนี้ในการวนลูปรอบแรกจึงเกิดการนำ item ตัวแรกของลิสต์เดิมมาคูณสองแล้วส่งผลลัพธ์ออกมาพิมพ์ โดยไม่มีการสร้างส่วนที่เหลือของลิสต์ไว้ในหน่วยความจำตั้งแต่แรก วิธีนี้จึงดีกว่าในแง่ของการใช้งานหน่วยความจำอย่างมีประสิทธิภาพสูงสุด
รู้จัก Tuple
เราทราบกันไปแล้วครับว่า List เป็นชนิดข้อมูลแบบ Sequential Types ที่มีความสามารถในการเปลี่ยนแปลงค่าได้
Tuple นั้นก็เป็น Sequential Types เช่นเดียวกับลิสต์ครับ เพียงแต่ Tuple เป็นชนิดข้อมูลแบบ Immutable นั่นหมายความว่าคุณจะไม่สามารถเปลี่ยนแปลงค่าของมันได้เลย
1t = (1, 2, 3) # สามารถใช้ () เพื่อแทนการประกาศ tuple ได้2t[0] = 4 # TypeError: 'tuple' object does not support item assignment
โดยธรรมชาติของ tuple นั้น ค่าต่างๆที่อยู่ภายใต้ตัวมันเราเปลี่ยนแปลงแก้ไขไม่ได้ แต่ทำไมตัวอย่างข้างล่างนี้จึงทำการเพิ่มค่าเข้าไปในลิสต์ที่เป็นอีลีเมนต์ตัวนึงของ tuple ได้ ?
1t = (1, 2, [3, 4])2t[2].append(5)34print(t) # (1, 2, [3, 4, 5])
เรากล่าวว่า tuple แก้ไขค่าไม่ได้ แต่ตอนนี้เรากลับเพิ่มค่า 5 เข้าไปในลิสต์ที่อยู่ใต้ tuple ได้ซะงั้น?
จากรูปข้างต้นเราพบว่าในอินเด็กซ์ช่องที่สองนั้นเราไม่ได้เก็บลิสต์ครับ แต่เราเก็บตัวชี้ (pointer) ไปหาลิสต์ต่างหาก เมื่อเราทำการเพิ่มเลข 5 เข้าไปในลิสต์ เราไม่ได้เปลี่ยนค่าของตัวชี้เลย จึงไม่ผิดคอนเซปต์ของ tuple ที่ห้ามเปลี่ยนค่า ตรงจุดนี้โค้ดของเราจึงทำงานได้อย่างราบลื่น
หากเราแก้ไขโค้ดของเราใหม่ ดังนี้
1t = (1, 2, [3, 4])2t[2] = [3, 4, 5] # TypeError: 'tuple' object does not support item assignment
แบบนี้จะพบบข้อผิดพลาดเกิดขึ้น นั่นเพราะเราทำการเปลี่ยนตัวชี้จากเดิมที่ชี้ไปยัง [3, 4]
ให้ชี้ไปยังลิสต์ใหม่คือ [3, 4, 5]
เมื่อมีการเปลี่ยนแปลงเกิดขึ้นจึงผิดคอนเซปต์ของ tuple ที่ห้ามแก้ไขค่าใดๆทั้งสิ้นนั่นเอง
Tuple ไม่ได้เป็นแค่ Immutable List
หลายคนมักเข้าใจว่า tuple แตกต่างจาก list เพียงแค่เป็น immutable แต่ความจริงนั้นยังมีอีกสิ่งที่ทำให้ tuple แตกต่างจาก list
เราทราบแล้วว่า tuple ไม่สามารภเปลี่ยนแปลงค่าได้ เมื่อตัวมันเองเปลี่ยนแปลงค่าไม่ได้และมีลำดับของข้อมูลที่ชัดเจน เราจึงกล่าวได้ว่าข้อมูลแต่ละตัวใน tuple สามารถใช้เพื่อสื่อความหมายได้
1t = (404, 'Not Found')
เมื่อเราประกาศตัวแปร t ขึ้นมาเป็น tuple เรามั่นใจได้ว่าข้อมูลที่ t ชี้อยู่จะไม่ถูกเปลี่ยนแปลงแน่ๆ เราจึงกล่าวได้อย่างสนิทใจจากข้อเท็จจริงในข้อมูลได้ว่า t[0]
คือ HTTP Status Code ในขณะที่ t[1]
คือ HTTP Status Message
รู้จัก namedtuple
เพื่อให้ tuple ของเราสื่อความเป็น record ที่ใช้ในการเก็บข้อมูลเชิงความหมายมากขึ้น จึงสมควรที่เราจะใช้ namedtuple เพื่อประกาศชนิดข้อมูลใหม่ ดังนี้
1from collections import namedtuple23HTTPStatus = namedtuple('HTTPStatus', 'code message')45res = HTTPStatus(code=404, message='Not Found')67print(res.code) # 4048print(res.message) # Not Found
จากตัวอย่างข้างต้น HTTPStatus จะเป็นชนิดข้อมูลใหม่ที่เก็บข้อมูลเชิง record ไว้สื่อความหมายถึง code และ message ของ HTTP Status นั่นเอง
Tuple Unpacking
เมื่อเราเขียน tuple ขึ้นมาในเชิง record เพื่อสื่อความหมายแต่ละค่าในตัวมัน จึงไม่ใช่เรื่องแปลกที่เราจะสามารถแกะค่าต่างๆภายใน tuple ออกมาใส่ตัวแปรได้
1code, message = (404, 'Not Found')23print(code) # 4044print(message) # Not Found
ในความเป็นจริงแล้ว ไม่ใช่แค่ tuple ที่สามารถแงะข้อมูลภายในออกมาได้ list ก็สามารถดึงข้อมูลภายในออกมาในทำนองเดียวกันได้เช่นกัน
1code, message = [404, 'Not Found']23print(code) # 4044print(message) # Not Found
สร้างข้อมูลแบบ key-value ด้วย Dictionary
list และ tuple นั้นใช้อินเด็กซ์เพื่อบ่งบอกการเข้าถึงค่าข้อมูลภายใน โดยอินเด็กซ์จะเป็นตัวเลขจำนวนเต็มเท่านั้น สำหรับภาษาอื่นเรามี Associative Array ที่ทำให้เราสามารถเข้าถึงค่าข้อมูลผ่าน key ที่ไม่จำเป็นต้องเป็นเลขจำนวนเต็ม
ภาษา Python สามารถสร้าง associative array ได้เช่นกันผ่าน dict โดยมีเงื่อนไขว่า key จะเป็นค่าอะไรก็ได้ขอแค่ให้เป็น hashable object ก็พอ
1d = { 'key1': 'Hello', 'key2': 'World' } # สร้าง dict ผ่าน {}2d['key1'] # Hello
รู้จัก Hashable Object
จากหัวข้อก่อนหน้า สิ่งที่จะเป็นคีย์สำหรับดิกชันนารีได้นั้นต้องเป็น hashable object เท่านั้น
hashable object คือออบเจ็กต์ที่มีค่าแฮชและเป็นค่าเดิมเสมอตลอดช่วงอายุของมัน
ชนิดข้อมูลแบบ Immutable ทั้งหลาย เช่น str หรือ tuple จะเป็น hashable ยกเว้น tuple นั้นมีข้อมูลแบบ mutable อื่นปนอยู่ด้วย
1t1 = (1, 2, 3)2hash(t1) # หาค่าได้34t2 = (1, 2, [3, 4])5hash(t2) # TypeError: unhashable type: 'list'
สำหรับ User-defined Types เช่นการสร้างคลาสและออบเจ็กต์ขึ้นมาเอง โดยปกติจะถือว่าเป็น hashable object นั่นเพราะค่าปกติของ hash เหล่านี้ก็คือค่า id ของออบเจ็กต์ซึ่งจะเป็นค่าเดิมเสมอตลอดช่วงอายุของมันนั่นเอง
โดยสรุปจึงกล่าวได้ว่า Numeric Types, str, boolean, tuple และ User-defined Types สามารถนำมาสร้างเป็นคีย์ของดิกชันนารีได้ นั่นเพราะชนิดข้อมูลเหล่านี้ล้วนเป็น hashable objects
Dictionary Comprehension
เมื่อลิสต์มี list comprehension จึงไม่แปลกที่ดิกชันนารีก็จะมี dictionary comprehension บ้าง
1http_statuses = [2 (200, 'OK'),3 (201, 'Created'),4 (404, 'Not Found')5]67d = {}89for code, message in http_statuses:10 d[message] = code1112# {'OK': 200, 'Created': 201, 'Not Found': 404}
ตัวอย่างข้างต้นเรามีลิสต์ของ tuple ที่เป็นคู่ของ code และ message ของ HTTP Status เราใช้ for เพื่อวนรอบในการเพิ่ม key และ value เข้าไปในดิกชันนารี d
จากตัวอย่างนี้เราสามารถใช้ dictionary comprehension เพื่อสร้าง d
ขึ้นมาแบบง่ายๆได้ดังนี้
1http_statuses = [2 (200, 'OK'),3 (201, 'Created'),4 (404, 'Not Found')5]67d = {message: code for code, message in http_statuses}
ง่ายใช่ไหมละ!
ชนิดข้อมูลประเภท Set
เซตทางคณิตศาสตร์หมายถึงกลุ่มของสิ่งใดๆที่มีเงื่อนไขแน่นอนว่าสิ่งใดอยู่ในกลุ่มนั้นและสิ่งใดไม่อยู่ในกลุ่มนั้น เช่น เซตของนางสาวไทย แบบนี้เรียกว่าเซต เพราะเราบอกได้ว่าใครเป็นนางสาวไทยบ้าง แต่กลุ่มของคนหล่อนั้นไม่เป็นเซต เพราะเราบอกไม่ได้ว่าคนหล่อคือใคร มันขึ้นอยู่กับมาตรฐานของแต่ละคน โดยข้อกำหนดหนึ่งของเซตคือสมาชิกภายในเซตจะไม่ซ้ำกัน
เราสามารถสร้างข้อมูลประเภทเซตได้ผ่านทางคอนสตรัคเตอร์ของคลาส set ดังนี้
1s = set([1, 2, 3, 4, 2, 1, 1, 2]) # หรือสร้างจาก {1, 2, 3, 4}23print(s) # {1, 2, 3, 4}
จากตัวอย่างข้างต้นเป็นการยืนยันว่าเซตจะประกอบด้วยสมาชิกที่ไม่ซ้ำค่ากัน คำถามคือเราจะรู้ได้อย่างไรว่าค่าสมาชิกที่เราจะเพิ่มเข้าไปใหม่ในเซตนั้นซ้ำกันหรือไม่?
สมาชิกภายใต้เซตต้องเป็น hashable object เพื่อให้เราบอกได้ว่าสมาชิกตัวที่ใส่ใหม่เป็นของที่มีอยู่แล้วหรือไม่นั่นเอง
ตัวดำเนินการระหว่างเซต
ตัวดำเนินการพื้นฐานของเซตที่เราจะพูดถึงกัน ได้แก่ union, intersection และ difference
ตัวดำเนินการ Union
การนำเซตสองเซตมา union กันนั้นคือการรวมสมาชิกของทั้งสองเซตเข้าด้วยกัน เราสามารถสร้างเซตใหม่จากการรวมสมาชิกในเซตทั้งสองได้ผ่านการเรียกเมธอด union หรือการใช้ |
1s1 = {1, 2, 3, 4}2s2 = {4, 5, 6}34print(s1.union(s2)) # {1, 2, 3, 4, 5, 6}5print(s1 | s2) # {1, 2, 3, 4, 5, 6}
ตัวดำเนินการ intersection
Intersection คือการหาจุดร่วมของสมาชิกระหว่างเซต ผลลัพธ์จากการดำเนินการจะได้เซตใหม่ที่ประกอบด้วยสมาชิกที่ปรากฎในเซตทั้งสอง เราสามารถใช้เมธอด intersection หรือ &
ได้เพื่อทำ intersection
1s1 = {1, 2, 3, 4}2s2 = {4, 5, 6}34print(s1.intersection(s2)) # {4}5print(s1 & s2) # {4}
ตัวดำเนินการ difference
ผลต่างของเซตคือ difference เป็นการสร้างเซตใหม่โดยลบสมาชิกที่ปรากฎในเซตที่สองออกจากเซตตั้งต้น การหาผลต่างนี้สามารถทำผ่านเมธอด difference หรือ -
ก็ย่อมได้
1s1 = {1, 2, 3, 4}2s2 = {4, 5, 6}34print(s1.difference(s2)) # {1, 2, 3}5print(s1 - s2) # {1, 2, 3}
สรุป
บทความนี้ได้พาเพื่อนๆไปรู้จักกับชนิดข้อมูลแบบกลุ่มต่างๆของ Python ในความเป็นจริงแล้ว Python ยังมีข้อมูลกลุ่มต่างๆอีกมากครับ เช่น defaultdict, frozenset เป็นต้น เพื่อนๆที่สนใจสามารถอ่านรายละเอียดเพิ่มเติมจากเอกสารหลักของ Python3 ได้ครับ
สารบัญ
- ชนิดข้อมูลแบบ Mutable และ Immutablea
- ข้อมูลประเภท List
- List เป็น Random Access และไม่ใช่ Linked List
- List กับ Dunder Methods
- List กับ Slicing
- เมธอดที่สำคัญของ List
- List Comprehension
- Generator Expression
- รู้จัก Tuple
- Tuple ไม่ได้เป็นแค่ Immutable List
- รู้จัก namedtuple
- Tuple Unpacking
- สร้างข้อมูลแบบ key-value ด้วย Dictionary
- รู้จัก Hashable Object
- Dictionary Comprehension
- ชนิดข้อมูลประเภท Set
- ตัวดำเนินการระหว่างเซต
- สรุป