
เขียนบทความเกี่ยวกับ Python มาก็หลายเรื่องล่ะ (บทความ Python ทั้งหมด) แต่ไม่เคยที่จะลงในพื้นฐานของ Python จริงๆเลย เช่นพวก Data structure และ algorithm แบบต่างๆ
ไหนๆก็ไหนๆล่ะ วันนี้จะมาลงมือเล่นกับ Python จริงๆจังๆแล้วโดยจะเริ่มจาก Data structure ทั้งหมดเลย นอกจากนั้นจะมีเกร็ดความรู้เสริมต่างๆกับภาษาอื่นๆสรุปไว้ด้วย เพื่อที่จะให้เข้าใจความแตกต่างของแต่ละภาษาไป โดยมีพื้นฐานเดียวกันนั้นเอง 🙂
Python Data structure มีอะไรบ้าง?
เจ้า Python ไม่ได้มีรูปแบบการเก็บข้อมูลที่แยกออกมาเยอะแยะให้ปวดหัว มันมีหลักๆก็แค่ 4 ตัวนี้
- Lists
- Tuples (Immutable list)
- Sets
- Dictionaries
ซึ่งเจ้าตัวเล็กๆน้อยอย่าง String กับ Integer & Floating point เราไม่พูดถึงล่ะกันน่ะ 🙂
Lists
สั้นๆง่ายเลย List เป็นการเก็บ data แบบ linear data ไล่ไปเป็นเส้นตรงๆไปเลย (คล้ายๆการเก็บข้อมูลแบบ array แหละเพียงแต่ไม่ fixed size ตายตัว ใส่เข้าไปได้เรื่อยๆ)
List Key point
- Not specific type of list
- Mutable
ใน python list จะมีความพิเศษอย่างนึง คือ มันเก็บทุกอย่างตั้งแต่สากกะเบือยันเรือรบเลย ไม่เหมือนในหลายๆภาษาที่จะต้องระบุ type ตายตัวของ list ที่ต้องการจะทําการเก็บข้อมูลลงไป เช่น
List<string> al=new ArrayList<string>(); # java List<int> iList = new List<int>(); # c#
แต่ถ้า python มันใส่ลงไปได้เลย อยากใส่ไรก็ใส่ สบายใจเราก็พอ
myList = ['1',2,3.0,[4,5,6]] #python
จริงๆบางคนก็อาจจะแย้งน่ะว่าถ้า Java หรืออื่นๆก็ใส่ type object ลงไปสิ แค่นั้นก็ทําให้ใส่ทุกค่าลงไปได้เหมือนกัน List<object> แต่ลองคิดดูสิมันต้องมานั่ง cast object อีกก็ลําบากอยู่เหมือนกันน่ะ
วิธีการ Slicing ข้อมูล
การเอาข้อมูลจาก Lists/Arrays/Tuples มาใช้ใน python นอกจากการใช้ for loop ในการดึงข้อมูลออกมา เราสามารทําการ Slice ข้อมูลออกเป็นส่วนๆได้น่ะ เช่น
a = [1, 2, 3, 4, 5, 6, 7, 8] print(a[1:4]) # [2, 3, 4] #basic print(a[1:4:2]) # [2, 4] #advance with inceremental step size 2 แปลว่าจะตัดทุกๆ 2 ตัวนั้นเอง print(a[::-1) # [8, 7, 6, 5, 4, 3, 2, 1] # แบบ reverse จากหน้ามาหลัง วิธีดูง่ายๆคือไม่ระบุเลขนั้นแหละ ?:?: แล้วเพิ่ม step size เป็นติดลบ print(a[-1]) # 3 เอาจากตัวหลังสุดมา
ซึ่งการทําลักษณะนี้จะทําให้เราไม่ต้องไปยุ่งกับ for loop เลย และเร็วกว่ามากๆด้วยล่ะ ถ้าในภาษาอื่นอย่าง Java ก็จะมี
List inputA = input.subList(0, input.size()/2);
ซึ่งก็ดูยุ่งยากอยู่เลยไม่เหมือน slice ใน python 🙂
วิธีการ Looping & Access List
ถ้า access ทั่วไปก็เหมือนทุกภาษาเลย เราใช้ index
หรือ การใช้ enumurate function เพื่อให้มัน count index ให้เรานั้นเอง เราจะได้ไม่ต้องมาหา len แล้วสร้าง range ในการ loop เอง ซึ่งเราสามารถใช้ได้กับทุก data type ที่เป็น iterable ของ python
#access by index list = [1,2,3] print(list[1]) #output: 2 #old fashion car_list = ['honda','toyota','benz'] for index in range(len(car_list)): print(car_list[index]) #enumerate animal_list = ['cat','dog','bird'] for idx, value in enumerate(animal_list): print(idx, value) #output: #0 cat #1 dog #2 bird #https://docs.python.org/2.3/whatsnew/section-enumerate.html #https://www.programiz.com/python-programming/methods/built-in/enumerate
วิธีการ Create List
มีหลากหลายวิธีในการ create list โดยใจะใช้ […] หรือ elegant way ในการจัดการกับ create list/manipulate list โดย Comprehension = map,filter,reduce ก็ได้
โดย format จะเป็น
simple_list = [] # simple list creation data_list = [1,'wording',3] #list with multiple type list = [1,2,3] newList = [item*2 for item in list] #list items: 2,4,6
ด้วยวิธีนี้มันสามารถจัดการ list ได้เร็วกว่าการมานั่งสร้าง function แยกเยอะ

List function ที่น่าสนใจ
python เองก็มี list function ที่เตรียมไว้ให้เราแล้ว ซึ่งช่วยเหลือเวลาเราทํางานได้ระดับนึงเลย ได้แก่
list = [1,2,2,3] #Get Index list.index(2) #จะได้ค่าออกมาเป็น 1 น่ะ เพราะมันหาตําแหน่งของ element ไม่ใช่ access list #Count Item list.count(2) #จะได้ค่าเป็น 2 เพราะมี 2 อยู่ 2 ตัว #Remove first item list.remove(2) # list = [1,2,3] remove แค่ exact value ตัวแรก #Remove from index and return it # return 2 # list = [1,2,3] list.pop(2) #Remove specific index # list = [2,2,3] del(list[0]) #Reverse list list.reverse() # list = [3,2,2,1] #Append list list.append('horse') # list = [1,2,2,3,'horse'] list.append(['cat','dog'] # list = [1,2,2,3,['cat','dog']] #Extend list #เพิ่มค่าใน list แบบ multiple list.extend(['cat','dog']) # list = [1,2,2,3,'cat','dog']
Python List Tip 1:
- น่าสนใจกับ Copy List ใน Python คือมันจะ references value กันเมื่อเราใช้ b = a คือเหมือนชี้ pointer ไปที่ list นั้นแทน แปลว่าถ้า a เปลี่ยน b ก็จะเปลี่ยนด้วย… ซึ่งในทางกลับกัน b เปลี่ยน a ก็เปลี่ยนด้วยเหมือนกัน
- ดังนั้นการ copy list ใน python ที่ถูกคือการ slicing นั้นเอง ซึ่งจะออกมาเป็น b = a[:] เป็นต้น
List Collection Tip 1:
ในบางภาษาอย่าง Java จะมีสิ่งที่เรียกว่า ArrayList หรือ LinkedList ความแตกต่างของเจ้าสองตัวนี้ก็คือเรื่องของการ implementation น่ะ … อย่างที่เรารู้กัน Array เนี่ยมันมีความสามารถพิเศษคือการ read access ที่เร็วมากตาม position ได้ ถูกต้องมั้ย? เช่น System.out.println(a[5]) เป็นต้น ซึ่งเจ้า ArrayList มันก็มีความสามารถแบบนั้นแหละเพราะมันเป็น dymanic re-sizing array…. แต่เจ้า LinkedList มันไม่สามารถทํายังงั้นได้ มันจะต้อง traverse แต่ต้นจนจบเลย โดยช้าเร็วขึ้นอยู่กับ size ของ list แต่มีข้อดีตรงที่ว่าเราสามารถ insertion/deletion ได้เร็วกว่า ArrayList เพราะไม่ต้องทําการ moved (copied) ของข้อมูลไปมาตามหลักของ array นั้นเอง
รูปตัวอย่างของ array และ index ที่เข้าไปเรียกใช้ได้ เหมือนกับ ArrayList (การเพิ่มอะไรลงไปใหม่ไม่คุ้มเลย เพราะมันต้องจัดการ reindex ใหม่)

รูปตัวอย่างของ linkedlist มันจะมี pointer ชี้ไปตัวถัดไป และเวลาอ่านต้องไปที่ล่ะ node จนจบ ทําให้ช้า แต่ง่ายในการ insertion/deletion

Tuples
Tuples คือ immutable list (แก้ไขไม่ได้) ที่เมื่อสร้างขึ้นมาแล้วจะไม่สามารถแก้ค่าใดๆได้เลย โดยเก็บข้อมูลเป็น linear เหมือน List ทั่วไปนั้นแหละ
โดยวิธีการสร้าง Tuples ขึ้นมาใช้งานตามตัวอย่างนี้เลย
#packing style tup1 = ('physics','chemistry',1997,2000) #วิธีสร้าง tuples print(tup1) #output : ('physics','chemistry',1997,2000) #comma style tupX = 'accident', print(type(tupX)) #output: tuple type # เราสามารถนํามาประยุกต์ใช้กับ zip() # zip เป็น function ที่ใช้ในการรวม Array หลายๆตัว tup2 = (1,2,3) pairs = zip(tup1,tup2) #output: ('physics','chemistry',1997,2000,1,2,3)
เหตุผลที่ใช้ Tuples แทนที่จะใช้ List ก็น่าจะเป็นเรื่อง ความเร็วของ Tuples เมื่อใช้ โดยหลักการณ์เลือกใช้ก็ง่ายๆเลย คือเมื่อเราไม่ต้องการจะเปลี่ยนแปลงค่าอะไรใน list นั้น จงเลือกใช้ tuples เพราะมันเร็วส์กว่า Lists นิดหน่อย ลองอ่านผลการทดสอบดูน่ะ 🙂
Python Built-in Function ที่น่าสนใจพวก Zip() ที่ merge iterate หลายๆตัวเข้าด้วยกัน เป็นต้น
วิธีการ Looping and Access Tuples
การเอาค่าจาก Tuples ทําได้ทั้งระบุ index หรือใช้ unpack ของ Tuple นั้นเอง
tup1 = ('physics','chemistry',1997,2000) #index style print(tup1[0]) #physics #unpacking style a,b,c,d = tup1 print(a) #physics print(b) #chemistry print(c) #1997 print(d) #2000
Sets
จุดเด่นของ set คือการที่มันจะ unique data ให้เหลือตัวเดียวนั้นเอง โดยเราเอามาใช้ร่วมกับ List ได้ เพื่อ remove dulpicate data ให้เป็น Unique
Set Key point
- Unique
- Unordered
- Mutable
Creating Set
เราสามารถสร้าง set ตรงๆ หรือสร้างจาก list ก็ได้โดยใช้ built-in function set() หรือสร้างด้วย {…} เลยก็ได้น่ะ
game_list = ['Rov','Call of duty','Rov'] example_set = set(game_list) # {'Rov','Call of duty'} #วิธีสร้าง set แบบ classic basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} #หรือแบบนี้ classic style x = set() #แล้วค่อยๆ add ลงไป x.add(1) # {1} #add new list to set new_list_for_add = ['cookie','ham'] x.update(new_list_for_add) #output - {1,'cookie','ham'}
Dictionaries
มาถึงตัวสุดท้ายแล้ววว dictionaries เป็นการเก็บข้อมูลแบบ Keys-Values ที่เราใช้กันโคตรบ่อย
Creating Dictionary
# วิธีสร้าง dictionary tel = {'jack': 4098, 'sape': 4139} # วิธีการเพิ่ม dictionary tel['guido'] = 4127 # show all key print(tel.keys()) # show all value print(tel.values()) # show all dictionary print(tel) #True วิธีการเช็คข้อมูลอยู่ใน dictionaries หรือไม่ 'guido' in tel #True 'Jacky' not in tel
วิธีการ Looping and Access Dictionaries
เนื่องจากมากเป็น key,values เวลาเราเรียกค่าขึ้นมาใช้ เราก็ต้องเลือก key และ values มาใช้นั้นเอง
d = {'car':'honda','money':12345} #iterate for key, value in d.items(): print(key,value) #output #car honda #money 12345 #iterate with index for idx, item in enumerate(d.items()): print(idx,item) #output #1 (car,honda) #2 (money,12345) #https://www.programiz.com/python-programming/methods/built-in/enumerate
วิธีการสร้าง Dictionary จาก CSV file
รู้หรือไม่ว่าปกติแล้วการอ่าน csv ใน python จะออกมาเป็น list [] แต่เราสามารถอ่านออกมาเป็น Dictionary {} ได้เหมือนกันโดยเปลี่ยนแค่คําสั่ง แล้วจะได้ออกมาเป็น Tuples () พร้อม key,value ที่ต้องการ เช่น
import csv # Basic csv file csvfile = open('file.csv','r') for row in csv.reader(csvfile): print(row) #output: list [1,2,3] # csv file with Dictionary for row in csv.Dictreader(csvfile): print(row) csvfile.close() #output: dictionary [('column1',1),('column2',2),('column3',3)]
Dictionary function ที่น่าใช้
d = {'car':'honda','money':12345} # Get Dict. Keys #function ในการจัดการกับ keys print(d.get('money','No money')) #output: 12345 #ในทางกลับกันถ้าเราเลือก Key ที่ไม่มีมันจะ Thrown exception ให้เรา เช่น print(d.get('house','No house')) #output: No house # remove key dictionary del d['money'] #safety remove dictionary d.pop('money','No key')
Collection Modules น่าสนใจ
- Counter
- DefaultDict
- Nametuple
Counter เพื่อจัดการนับจํานวนข้อมูลใน list
ถ้าเราต้องการหาค่าของ Duplicate Value ใน List ให้นึกถึง Counter
from collections import Counter a = [.....สมมุติว่าข้อมูลมหาศาลมาก....] counter_a = Counter(a) print(counter_a) #output: tuple ของ dictionary ที่จะ count ให้ว่ามีข้อมูลอะไรบ้างที่ซํ้ากันใน list นั้น # ({'a': 20, 'b': 35})
DefaultDict เพื่อลดความเสี่ยงจาก KeyError
Dictionary เป็น container type ที่เก็บข้อมูลแบบ Key-Value โดยมักจะ thrown error ออกมาเป็น KeyError ถ้าไม่มี Key นั้น
เจ้า Default Dictionary ลดความเสี่ยงนั้นโดยการตั้ง value ให้เป็น default สําหรับ dictionary นั้นๆ
from collections import defaultdict somedict = {} print(somedict[9999]) #keyerror somedict = defaultdict(int) print(somedict[9999]) #output > 0
เรายังสามารถนํา default dictionary มาประยุกต์ในการจัดการกับโครงสร้างของข้อมูลที่หน้าตาไม่รู้อีกด้วย เช่น เราต้องการจะสร้าง dictionary ของรายการอาหารที่คนชอบทานมากที่สุดแต่เราไม่รู้ว่า รายการอาหารนั้นถูกสร้างเป็น Key หรือยัง?
สิ่งที่เราต้องทําก็คงไม่พ้น … เราต้องนั่งเช็ค key รายการอาหารก่อนที่จะนับเพิ่มลงไปอีกตัว
food_list = {} for menu,value in food_list: if menu not in food_counter: food_counter[menu] = [] food_counter[menu].append[value] # แต่ถ้าเราใช้ default dictionary น่ะ โลกเปล่ียนไปเลย # เราสร้าง dictionary ที่มี default value เป็น list ไว้ แปลว่ามาถึงเรา append ได้เลย ไม่ต้องเช็คค่า # โดยเราสามารถสร้าง default_factory เป็น int,list ก็ได้ from collections import defaultdict food_counter = defaultdict(list) for menu,value in food_list: food_counter[menu].append(value)
สรุปง่ายๆแล้ว สมมุติถ้าเราอยากใช้ dictionary ที่มี default value ด้วย เราควรจะใช้ defaultdic เป็นตัวหลักเลย
สร้าง Key ให้กับ Tuple ด้วย NamedTuple
Tuple คือ immutable list เนอะ ซึ่งโดยปกติแล้วมันจะเก็บข้อมูลแบบ sequence ไปเรื่อยๆเลย โดยเราต้องดึงค่ามาจาก index ไม่ใช่ key
แต่ด้วย NamedTuple เราจะสามารถสร้าง Key ให้กับ Tuple ได้ 🙂 เพื่อที่จะเรียกใช้งานง่ายๆนั้นเอง
from collections import namedtuple #สร้าง key ของ tuple ออกมา DataDetails = namedtuple('DataDetails', ['data', 'time']) data_entries = [] for data, time in entries_data: data = DataDetails(date,time) data_entries.append(data) # access namedtuple # เอาค่าเฉพาะ 5 ตัวแรกขึ้นมาโชว์ for data in data_entries[:5]: print(data.time) print(data.data)
เราควรจะใช้ namedtuple แทน tuple ธรรมดาทุกๆที่เลยใน code ของเราเพราะมันทําให้อ่านง่าย และ เป็น pythonic style ด้วย 🙂
อย่างที่เรารู้กัน namedtuple เป็น immutable เพราะฉะนั้นเราไม่สามารถเปลี่ยนแปลงค่าได้ แต่เรามีเทคนิคที่สามารถแปลงเป็น dictionary ได้ !
DataDetail = namedtuple('DataDetails', ['data', 'time']) data = DataDetail('Test Data','20:00 PM') type(data) #output - namedtuple type(data._asDict()) #output - OrderedDict print(data._asDict()['time']) #output - 20:00 PM
แล้วไงต่อ?
หลังจากเรียนรู้เรื่องของ Data structure แบบต่างๆไปแล้ว การจะเป็น Software Engineer ที่ดีต้องรู้ว่าเราควรเลือกเครื่องมือแบบไหนมาใช้งาน ไม่ใช่รู้อันไหนเลยใช้อันนั้น เพราะฉะนั้นสิ่งที่ต้องกังวลคือเรื่องของ Performance issues นั้นเอง และ เจ้าปัญหาเรื่องของ Performance หลักๆนั้นก็มาจากผลของการ Algorithm ที่ไม่มีประสิทธิ์ภาพเพียงพอนั้นเอง โดยเราใช้วิธีวัดจากสิ่งที่เรียกว่า Big O นั้นเอง เพราะฉะนั้นไว้ต่อในบทความหน้าเรื่องของ Algorithm และ Big O น่ะครับ เพื่อให้เป็นพื้นฐานที่ดีของการเขียนโปรแกรมนั้นเอง 🙂