Direct Download Center

มาคุยกันก่อน


คลิกที่นี่ เข้ามารู้จักกันก่อน
Mr. P

เรียนรู้เพิ่มเติมกับตัวแปร Dictionary

โพสต์17 ต.ค. 2553, 20:10โดยWisit P.   [ อัปเดต 4 ก.พ. 2554, 18:45 ]

วันนี้ เราจะมาทดลองทำความรู้จักกับตัวแปร Dict ให้มากขึ้น ซึ่งถ้าหากเราสามารถเรียกใช้ method เหล่านี้ได้อย่างคล่องแคล่วแล้ว คาดว่า เพื่อนๆ คงจะเกิดไอเดีย มากมายในการเขียนโปรแกรมด้วยภาษาไพธอน ได้อีกเยอะแยะมากมายอย่างแน่นอน

 
 

เริ่มต้นด้วยการเรามาสร้างตัวแปร Dict กันก่อน เพื่อจะทดลอง ในที่นี้ เราสามารถสร้างตัวแปร Dict ได้ด้วยการกำหนดค่าตัวแปร ตาม syntax ของมัน หรือสร้างจากตัวแปรที่ mapping  (Key, value) ไว้แล้ว ในแบบ List ก็ได้

Dictionary Syntax
รูปแบบการสร้างตัวแปร Dict
>>> MyInfo = {'name':'ple','age':33,'Address':'Korat'}
>>> type(MyInfo)
<type 'dict'>

สร้างตัวแปร List 
>>> MyList = [('name','Mr.P'),('University','SUT')]
>>> type(MyList)
<type 'list'>

แล้วใช้คำสั่ง dict() เพื่อสร้างตัวแปร Dict 
>>> d = dict(MyList)
>>> d
{'University': 'SUT', 'name': 'Mr.P'}
>>> type(d)
<type 'dict'>
เราก็จะได้ตัวแปร Dict จากการสร้างจากตัวแปร List 

หรือสร้างโดยใช้รูปแบบนี้ก็ได้ d= dict(name='Mr.P',University='SUT')  (บอกแล้ว ว่ามันเป็นภาษาที่ยืดหยุ่นจริงๆ )

Basic Dictionary Operations
โดยพื้นฐานแล้ว การทำงานกับตัวแปร Dict (Dictionary Operations) นั้น มีหลายวิธีมาก เช่น 

การหาจำนวนคู่สมาชิกใน Dict เราใช้คำสั่ง len()
>>> len(MyInfo)
3
เพราะว่า MyInfo มีจำนวนสมาชิกที่จับคู่กันอยู่ 3 คู่

การแสดงค่า ที่สัมพันธ์กับ key ที่เราระบุ
>>> MyInfo['name']
'ple'
>>> MyInfo['age']
33
>>> 

หรือแม้แต่การลบคู่สมาชิกในตัวแปร Dict เราก็สามารถทำได้ ด้วยคำสั่ง del
>>> del MyInfo['name']
>>> MyInfo
{'Address': 'Korat', 'age': 33}

ข้อควรจำ
  • คีย์ในตัวแปร Dict ไม่ใช่จำนวนตัวเลข ถึงแม้ว่า มันมองดูแล้ว เหมือนจะใช่ แต่จริงๆแล้ว มันไม่ใช่ 
  • เราสามรถแก้ไขค่าในคีย์ใดๆ ในตัวแปร Dict ไำด้ แต่ถ้ามันไม่มีคีย์นั้นอยู่ จะเป็นการสร้างคู่สมาชิกใหม่ในตัวแปร Dict แทน 
สิ่งที่สำคัญอย่างแรกของของตัวแปรแบบ Dict ที่แตกต่างจากตัวแปร List ถึงแม้มันจะมีการจับคู่กับเป็นแบบ key ในลำดับสมาชิก แต่ตัวแปร List ไม่สามารถที่จะเพิ่มค่าลงไปในตัวแปร ที่ไม่มีลำดับ key นั้นอยู่ แต่ตัวแปร dict ทำได้  (พูดแล้วงง ดูตัวอย่างดีกว่า)
>>> x = []
>>> x[42] = 'Foobar'' 
  File "<interactive input>", line 1
    x[42] = 'Foobar''
                    ^
SyntaxError: EOL while scanning single-quoted string

error เห็นๆ ถ้าเป็นตัวแปร List แต่ถ้าตัวแปรเป็น Dict หล่ะ
>>> x = {}
>>> x[42] = 'Foobar'
>>> x
{42: 'Foobar'}

ถึงแม้จะไม่มี key ที่ 42 อยู่ในตัวแปร Dict มันก็จะไม่ Error แต่จะเป็นการสร้างคู่สมาชิกใหม่ให้เลยทันที

มาดูตัวอย่างการนำตัวแปร Dict ไปใช้กันดีกว่า (อันนี้ ลอกเค้ามาเลย) เป็นตัวอย่างการประยุกต์เอาตัวแปร Dict ไปทำเป็น Database โดยเก็บชื่อคน แล้วก็ที่อยู่ และเบอร์โทรศัพท์ เอาไว้ให้ค้นหา

# A simple database
# A dictionary with person names as keys. Each person is represented as
# another dictionary with the keys 'phone' and 'addr' referring to their phone
# number and address, respectively.

people = {
'Alice': {
'phone': '2341',
'addr': 'Foo drive 23'
},
'Beth': {
'phone': '9102',
'addr': 'Bar street 42'
},
'Cecil': {
'phone': '3158',
'addr': 'Baz avenue 90'
}
}
# Descriptive labels for the phone number and address. These will be used
# when printing the output.
labels = {
'phone': 'phone number',
'addr': 'address'
}
name = raw_input('Name: ')
# Are we looking for a phone number or an address?
request = raw_input('Phone number (p) or address (a)? ')
# Use the correct key:
if request == 'p': key = 'phone'
if request == 'a': key = 'addr'
# Only try to print information if the name is a valid key in our dictionary:
if name in people: print "%s's %s is %s." % (name, labels[key], people[name][key])

ผลการรัน 

เมื่อเราใส่ชื่อ Alice แล้วกดตัว p จะได้ผลลัพธ์
>>> 
Alice's phone number is 2341.

เขียนไป เขียนมาชักจะเหนื่อยแหะ เรื่องของตัวแปร Dict ยังไม่หมดนะเนี่ย เหลืิอเยอะเลยหล่ัะ จะไหวไหมเนี้ย เอาน่าๆ ดูกันต่อ

อีกตัวอย่างหนึ่งที่น่าสนใจ ก็คือ เราสามารถส่งค่าของตัวแปร Dict เข้าไปในขั้นตอนการ print อันนี้ ผมไม่แน่ใจว่าเค้าเรียกว่าขั้นตอนอะไร คล้ายๆกับเราต้องการส่งค่าในตัวแปร เข้าไปใน printf("Hello, Mr. %s",name); ของภาษาซี ประมาณนี้ แต่เมื่อเป็นไพธอน เค้าก็สามารถทำได้เหมือนกัน แถมยังยืดหยุ่นกว่าอีก เรามาดูตัวอย่างการส่งค่าเข้าไปกัน 

ในตัวอย่างนี้ เค้ามีตัวแปรที่เก็บค่า string ที่เป็น โค๊ด HTML แต่ว่า มันเป็นแค่ Template เท่านั้น โดยเค้าจะใส่เครื่องหมาย %(key)s ไว้ตามตำแหน่ง tag ที่เค้าต้องการให้มันเป็น dynamic code นั่นหมายความว่า เค้าสามารถใช้ code นี้ในการทำแม่แบบให้กับทุก page ได้ โดยใช้โค๊ดนี้ เพียงโค๊ดเดียว แล้วทำการส่งค่าเข้าไป ให้แตกต่างกัน 

>>> template = '<html><head><title>%(title)s</title></head><body><h1>%(title)s</h1><p>%(text)s</p></body>'
>>> print template
<html><head><title>%(title)s</title></head><body><h1>%(title)s</h1><p>%(text)s</p></body>
>>> print template % data
<html><head><title>My Home Page</title></head><body><h1>My Home Page</h1><p>Welcome to my home page!</p></body>

Dictionary Methods
เมทธอดแรกที่เราจะได้เรียนรู้ นั่นก็คือ 

เมทธอด clear เป็นเมทธอด ที่เอาไว้สำหรับล้างค่าสมาชิกในตัวแปร Dict ซึ่งชื่อมันก็บอกอยู่แล้ว ว่า clear  ซึ่งเราสามารถเอาตัวแปรไปรับค่าที่มัน return มาได้ด้วย ซึ่งเมทธอดนี้ จะ return ค่า None มาให้ (ไม่รู้ จะเอาทำไม ) แล้วเราจะเอา เมทธอด clear ไว้ทำไร มาดูความแตกต่างของ 2 ตัวอย่างนี้ครับ

ตัวอย่างแรก เรา clear ค่าของตัวแปร dict โดยสร้างตัวแปร dict ค่าว่างๆ ทับลงไป
>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x = {}
>>> x
{}
>>> y
{'key': 'value'}

ตัวอย่างที่สอง เรา clear ค่าโดยใช้เมทธอด clear
>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x.clear()
>>> y
{}

เห็นความแตกต่างไหมครับ งงหล่ะสิ ผมก็งงเหมือนกัน 555+  ความหมายของมันก็คือ ทั้งตัวแปร x และ y อ้างไปถึงข้อมูล dictionary ที่เดียวกัน แต่เมื่อเราทำการ initial หรือกำหนดค่าว่างให้กับตัวแปร x จะไม่มีผลกับตัวแปร y แต่ถ้าเราเรียกใช้ method clear() เราจะพบว่าค่าในตัวแปร y ถูก clear ไปด้วยเหมือนกัน

เมทธอด copy ค่า dictionary ใหม่ โดยที่ได้ค่า key และ value เหมือนตัวที่มัน copy มาทุกประการ แล้วถ้าหากคุณทำการเปลี่ยนแปลงค่าในตัว copy จะไม่มีผลในตัวต้นฉบับ แต่ ถ้าคุณลบค่าในตัว copy ออกไป จะมีผลให้ตัวต้นฉบับ ค่าหายไปด้วยเช่นกัน (แปลกดีไหมหล่ะ)

>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y['username'] = 'mlh'
>>> y['machines'].remove('bar')
>>> y
{'username': 'mlh', 'machines': ['foo', 'baz']}
>>> x
{'username': 'admin', 'machines': ['foo', 'baz']}

เพื่อหลีกเลี่ยงปัญหา เมื่อเวลาที่เราต้องการแก้ไขตัว copy แต่ไม่อยากให้ส่งผลถึงตัวต้นฉบับ เราสามารถเรียกใช้คำสั่ง deepcopy ซึ่งอยู่ใน module copy ได้ 
>>> from copy import deepcopy
>>> d = {}
>>> d['names'] = ['Alfred', 'Bertrand']
>>> c = d.copy()
>>> dc = deepcopy(d)
>>> d['names'].append('Clive')
>>> c
{'names': ['Alfred', 'Bertrand', 'Clive']}
>>> dc
{'names': ['Alfred', 'Bertrand']}

จะเห็นว่า เมื่อเราแก้ไข ตัว copy จะไม่ส่งผลถึงตัวต้นฉบับ เพราะตัว copy ถูกสร้างมาจากคำสั่ง deepcopy นั่นเอง

fromkeys เป็นเมทธอด ที่เอาไว้สร้างตัวแปร dict ซึ่งจะได้ค่า key ตาม parameter ที่ใส่เข้าไป แต่ค่า value ของตัวแปร dict จะมีค่าเป็น None ดูตัวอย่าง
>>> {}.fromkeys(['info1','info2'])
{'info1': None, 'info2': None}

หรือจะเรียกคำสั่งแบบนี้ก็ได้ (ซึ่งดูดีกว่า ดูเป็นมาตรฐาน OOP ดี)
>>> dict.fromkeys(['info1','info2'])
{'info1': None, 'info2': None}

แล้วถ้าเกิดเราไม่อยากให้ ค่้าเริ่มต้นมีค่าเป็น None หล่ะ เราจะกำหนดอย่างไร python ก็ยังมีคำตอบให้คุณเสมอ ดูตัวอย่าง
>>> dict.fromkeys(['info1','info2'],'Unknow')
{'info1': 'Unknow', 'info2': 'Unknow'}

มาถึง method สำคัญอีกตัวหนึ่ง ที่เห็นใช้บ่อยที่สุด แล้วปลอดภัยที่สุดในการอ้างถึงค่าในตัวแปร dict ซึ่งเป็นตัวที่ผมเห็นเค้าใช้บ่อยที่สุด 
หากคุณจะต้องอ้างถึงค่าที่อยู่ในตัวแปร dict คุณสามารถทำได้โดย เพียงแค่ ระบุ key ที่ต้องการ เพียงเท่านี้ เราก็จะได้ค่าที่ต้องการออกมา
>>> d = {'name':'ple'}
>>> d['name']
'ple'

แล้วถ้าเกิด ในโปรแกรมของเรา มีการกระทำบางอย่างก่อนหน้านี้ ซึ่งมีผลทำให้ key 'name' หายไปหล่ะ โดยที่เราไม่ได้ทราบล่วงหน้ามาก่อน หรือคาดไม่ถึง จะเกิดอะไรขึ้น หากเราอ้างถึง key ตัวนั้น
>>> d.clear()
>>> d
{}
>>> d['name']
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
KeyError: 'name'

เกิด error ขึ้นสิครับ ที่หนักไปกว่านั้น โปรแกรมเราอาจจะหยุดทำงานได้ ปัญหานี้ เราสามารถป้องกันได้โดยใช้ method ที่ชื่อว่า get โดยเจ้า method ตัวนี้จะทำการดึงค่า value จาก key ที่เรากำหนด แต่ถ้าหากไม่เจอค่า key นั้นๆ มันก็จะ return ค่า None กลับมาให้เรา 

>>> d.get('name')
>>> 

หรือถ้าเราต้องการให้มัน return อย่างอื่นมาก็ได้ เช่น เราอาจให้มัน return เป็น error message มาให้ผู้ใช้ก็ได้ เช่น

>>> k = 'name'
>>> d.get(k,'Invalid key '+ k)
'Invalid key name'

เห็นไหมหล่ะ โปรแกรมเราดูดีขึ้นเยอะเลย หรือว่าเราจะเช็คว่ามี key นั้นอยู่ก่อนหรือเปล่า ก็สามารถทำได้ ด้วย method ที่ชื่อว่า has_key ดูตัวอย่างเลยหล่ะกัน
>>> d = {}
>>> d.has_key('name')
False
>>> d['name'] = 'Wisit'
>>> d.has_key('name')
True

items คือ คำสั่งอีกตัวหนึ่งที่เอาไว้ดึงค่าออกจาก dict โดยค่าที่ได้ออกมา จะกลายเป็นตัวแปรประเภท list  
>>> d = {'title': 'My site', 'url': 'https://sites.google.com/site/pythonclassroom'}
>>> a = d.items()
>>> type(a)
<type 'list'>
>>> a
[('url', 'https://sites.google.com/site/pythonclassroom'),
 ('title', 'My site')]
>>> a[1]
('title', 'My site')
>>> a[0]
('url', 'https://sites.google.com/site/pythonclassroom')

เช่นเดียวกันกับเมทธอด values และ keys ที่เอาไว้ดึงค่า value และ ค่า key จากตัวแปร dictionary ค่าที่ได้ออกมาจะกลายเป็นตัวแปร list  ดูตัวอย่างครับ
>>> d
{'title': 'My site', 'url': 'https://sites.google.com/site/pythonclassroom'}
>>> d.keys()
['url', 'title']
>>> d.values()
['https://sites.google.com/site/pythonclassroom', 'My site']
>>> v = d.values()
>>> type(v)
<type 'list'>
>>> k = d.keys()
>>> type(k)
<type 'list'>

แล้วถ้าหากเราต้องการดึงค่าออกมาจริงๆหล่ะ โดยให้ค่าที่ถูกดึงออกมาจากตัวแปร dict หายไปด้วย python ก็มีคำสั่งคล้ายๆกับภาษาอื่นๆ เพื่อนคงเคยได้ยิน นั่นคือ คำสั่ง pop สังเกตจากตัวอย่างครับ
>>> d = {'1':'one','2':'two','3':'three'}
>>> d
{'1': 'one', '2': 'two', '3': 'three'}
>>> d.pop('2')
'two'
>>> d
{'1': 'one', '3': 'three'}
>>> 

คำสั่ง popitem ก็คล้ายๆคำสั่ง pop แต่ต่างกันตรงที่ คำสั่ง popitem จะดึงทั้ง key และ value ออกมาพร้อมกันเลยทีเดียว
>>> d
{'1': 'one', '3': 'three'}
>>> a = d.popitem()
>>> a
('1', 'one')
>>> d
{'3': 'three'}

แต่สิ่งหนึ่งที่ต้องระวังก็คือ คำสั่ง popitem มันจะ random คือ มันไม่สามารถที่จำกำหนดได้ ว่าเราจะเอาคู่ไหนออกมา 
>>> d = {'1':'one','2':'two','3':'three','4':'four','5':'five'}
>>> d.popitem()
('1', 'one')
>>> d
{'2': 'two', '3': 'three', '4': 'four', '5': 'five'}
>>> d.popitem()
('3', 'three')
>>> d
{'2': 'two', '4': 'four', '5': 'five'}
>>> 

จะเห็นว่า จู่ๆ มันก็ pop ค่า '3' ออกมาซะอย่างงั้น แทนที่จะเอา '2' ไปก่อน ก็อย่างที่บอก มัน random ครับ

คำสั่งต่อมา คือ setdefault คำสั่งนี้ มีบางอย่าง คล้ายๆคำสั่ง get แต่ต่างกันตรงที่ คำสั่ง setdefault จะเป็นการ set ค่า value ให้กับ key ที่เราระบุ แต่หากที่ key นั้น มีค่าอยู่แล้ว มันก็จะไม่ทำการ set ค่า value ใหม่ลงไปทับแต่อย่างไร (คล้ายๆ คำสั่ง get ตรงที่ระบุ key แล้ว ถ้ามันเจอว่ามีอยู่แล้ว ก็จะไม่ทำอะไร...คล้ายตรงไหนเนี้ย!!!) ดูตัวอย่าง ครับ
 
>>> d = {'1':'one','2':'two','3':'three','4':'four','5':'five'}
>>> d.setdefault('6','six')
'six'
>>> d
{'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six'}
>>> 
>>> d.setdefault('1','First')
'one'
>>> d
{'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six'}

คำสั่งสุดท้าย ที่จะนำเสนอ นั่นก็คือ คำสั่ง update เป็นคำสั่งที่เอาไว้อัพเดทค่า value ในตัวแปร dictionary ด้วยค่าจากตัวแปร dictionary อีกตัวหนึ่ง หากค่า key ตรงกัน   
>>> d = {'title':'Mr.','name':'Wisit P.','Position':'Engineer'}
>>> a = {'Posiotion':'Technician'}
>>> d.update(a)
>>> d
{'Posiotion': 'Technician',
 'Position': 'Engineer',
 'name': 'Wisit P.',
 'title': 'Mr.'}

แต่ถ้า key ไม่ตรงกัน ก็จะไม่เกิดการ update ในตัวแปร d แต่อย่างใด
>>> b = {'Sex':'male'}
>>> d.update(b)
>>> d
{'Posiotion': 'Technician',
 'Position': 'Engineer',
 'Sex': 'male',
 'name': 'Wisit P.',
 'title': 'Mr.'}

จะเห็นว่า ตัวแปร dictionary นั้น มีลูกเล่นอีกหลากหลาย แล้วก็เป็นตัวแปรที่เค้าใช้กันบ่อยมากๆ ส่วนมาก จะพบเห็น เอาไว้สำหรับตั้งค่าเริ่มต้นต่างๆ แต่ถ้าต้องการแก้ไข หรืออ้างอิง หรือเปลี่ยนแปลงใดๆ เพื่อให้เหมาะสมกับงาน ณ ขณะนั้น เราก็สามารถกระทำได้อย่างง่ายดาย ลองๆไปหัดใช้ดูกันให้คล่องๆนะครับ