แสดง Output จาก Python Data Structure ยังไงให้อ่านง่าย

How to Print Human-Readable Output from Python Data Structures

Nopnithi Khaokaew (Game)
6 min readNov 15, 2020

— — — — — — — — — — — — — — —
สารบัญเนื้อหาทั้งหมด (My Contents)
— — — — — — — — — — — — — — —

เคยมั้ยบางครั้งที่เราเขียน Python ขึ้นมาเพื่อช่วยในการทำงาน และต้องการความสะดวกรวดเร็วในการพัฒนา เราก็มักจะ print ผลลัพธ์ออกมาดื้อ ๆ จาก data structure เลยโดยไม่มีการจัด format แบบนี้

['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5', '10.0.0.6', '10.0.0.7', '10.0.0.8', '10.0.0.9', '10.0.0.10', '10.0.0.11', '10.0.0.12', '10.0.0.13', '10.0.0.14', '10.0.0.15', '10.0.0.16', '10.0.0.17', '10.0.0.18', '10.0.0.19', '10.0.0.20']

หรือแบบนี้

[{'hostname': 'KCiBk', 'ip': '10.0.0.1', 'os': 'windows', 'version': '2012r2'}, {'hostname': 'tQcOv', 'ip': '10.0.0.2', 'os': 'redhat', 'version': '7'}, {'hostname': 'RwyOa', 'ip': '10.0.0.3', 'os': 'ubuntu', 'version': '18.04'}, {'hostname': 'IpQQa', 'ip': '10.0.0.4', 'os': 'windows', 'version': '2019'}, {'hostname': 'MCJEF', 'ip': '10.0.0.5', 'os': 'windows', 'version': '2019'}, {'hostname': 'OjAhw', 'ip': '10.0.0.6', 'os': 'redhat', 'version': '7.2'}, {'hostname': 'YkWrx', 'ip': '10.0.0.7', 'os': 'windows', 'version': '2019'}, {'hostname': 'KnFfQ', 'ip': '10.0.0.8', 'os': 'windows', 'version': '2016'}, {'hostname': 'iTIpJ', 'ip': '10.0.0.9', 'os': 'windows', 'version': '2019'}, {'hostname': 'KiEzv', 'ip': '10.0.0.10', 'os': 'redhat', 'version': '7.2'}, {'hostname': 'MgyOd', 'ip': '10.0.0.11', 'os': 'ubuntu', 'version': '18.04'}, {'hostname': 'uJEfD', 'ip': '10.0.0.12', 'os': 'redhat', 'version': '8'}, {'hostname': 'TRJjy', 'ip': '10.0.0.13', 'os': 'redhat', 'version': '7'}, {'hostname': 'zPeFd', 'ip': '10.0.0.14', 'os': 'redhat', 'version': '7'}, {'hostname': 'PHrYq', 'ip': '10.0.0.15', 'os': 'windows', 'version': '2016'}, {'hostname': 'CjrAr', 'ip': '10.0.0.16', 'os': 'ubuntu', 'version': '16.04'}, {'hostname': 'NaOrF', 'ip': '10.0.0.17', 'os': 'ubuntu', 'version': '20.04'}, {'hostname': 'PvhvF', 'ip': '10.0.0.18', 'os': 'windows', 'version': '2016'}, {'hostname': 'dIuqi', 'ip': '10.0.0.19', 'os': 'redhat', 'version': '7'}, {'hostname': 'fLHFB', 'ip': '10.0.0.20', 'os': 'ubuntu', 'version': '20.04'}]

วันนี้ผมจะมาสอนวิธีที่จะช่วยให้ output ของ Python ที่แสดงผลบน console window ของเราสวยขึ้น อ่านง่ายขึ้น มีความ human readable มากขึ้นว่างั้นเถอะ

— — — — — — — — — — — — — — — — — — — — — —

ฝากคอร์ส Python for Network Automation ผมด้วยครับ

— — — — — — — — — — — — — — — — — — — — — —

Code ตัวอย่าง

ใครอยากลองเอาไปรันเล่นเพื่อศึกษา ทุกอย่างอยู่ในนี้หมดแล้วครับ

git clone https://github.com/nopnithi/python_pretty_output_tutorial.git

Code ส่วนที่ใช้ Generate ข้อมูลจำลอง

เนื่องด้วยเราไม่มีข้อมูลจริง ก็เลยต้องใช้ code ช่วย generate เอา data ออกมาให้ โดยสมมุติว่าเป็นข้อมูลของ server จำนวน 20 เครื่อง (VMs)

from random import choice
import string
devices = []
addresses = []
for i in range(1, 21):
device = {
'hostname': (choice(string.ascii_letters) +
choice(string.ascii_letters) +
choice(string.ascii_letters) +
choice(string.ascii_letters) +
choice(string.ascii_letters)),
'ip': '10.0.0.' + str(i),
'os': choice(['ubuntu', 'redhat', 'windows'])
}
if device['os'] == 'ubuntu':
device['version'] = choice(['16.04', '18.04', '20.04'])
elif device['os'] == 'redhat':
device['version'] = choice(['6', '7', '7.1', '7.2', '8'])
elif device['os'] == 'windows':
device['version'] = choice(['2012r2', '2016', '2019'])
addresses.append(device['ip'])
devices.append(device)

ซึ่งเดี๋ยวผมจะเอา raw data ที่ได้จาก code นี้(ตัวแปร addresses และ devices) ไปใช้ในแต่ละตัวอย่างต่อไปนะครับ

1) ตัวอย่างของปัญหา

สมมุติว่าผมเขียน Python ขึ้นมาช่วยในการเช็คว่า ณ ตอนนี้มี server ออนไลน์ทั้งหมดกี่ตัว พร้อมแสดง information ต่าง ๆ ออกมา

แต่ผมขี้เกียจเขียน code เยอะ ก็เลยใช้การ print เอา output ออกมาจาก data structure โดยตรงเลย โดยไม่ได้เขียนฟังก์ชั่นการแสดงผลอะไร

ปัญหาก็คือ output ที่ได้มันจะอ่านยากสำหรับ user ที่ใช้ script นี้ เราจะทำยังไงหละถึงจะง่ายทั้งฝั่ง developer และ user

2) เตรียม Library ให้เรียบร้อย

ติดตั้ง tabulate ก่อน

$ pip install tabulate
Collecting tabulate
Downloading tabulate-0.8.7-py3-none-any.whl (24 kB)
Installing collected packages: tabulate
Successfully installed tabulate-0.8.7

จากนั้น import pprint, tabulate และ itemgetter เข้ามา

from pprint import pprint
from tabulate import tabulate
from operator import itemgetter

3) แสดงผล List

ใช้ print (ธรรมดา)

print(addresses)

จะได้

['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5', '10.0.0.6', '10.0.0.7', '10.0.0.8', '10.0.0.9', '10.0.0.10', '10.0.0.11', '10.0.0.12', '10.0.0.13', '10.0.0.14', '10.0.0.15', '10.0.0.16', '10.0.0.17', '10.0.0.18', '10.0.0.19', '10.0.0.20']

ใช้ pprint

pprint(addresses, width=-1)

จะได้

['10.0.0.1',
'10.0.0.2',
'10.0.0.3',
'10.0.0.4',
'10.0.0.5',
'10.0.0.6',
'10.0.0.7',
'10.0.0.8',
'10.0.0.9',
'10.0.0.10',
'10.0.0.11',
'10.0.0.12',
'10.0.0.13',
'10.0.0.14',
'10.0.0.15',
'10.0.0.16',
'10.0.0.17',
'10.0.0.18',
'10.0.0.19',
'10.0.0.20']

4) แสดงผล Dictionary

ใช้ print (ธรรมดา)

print(devices[0])

จะได้

{'hostname': 'KCiBk', 'ip': '10.0.0.1', 'os': 'windows', 'version': '2012r2'}

ใช้ pprint

pprint(devices[0], width=-1)

จะได้

{'hostname': 'KCiBk',
'ip': '10.0.0.1',
'os': 'windows',
'version': '2012r2'}

5) แสดงผล List ของ Dictionary

ใช้ print (ธรรมดา)

print(devices)

จะได้

[{'hostname': 'KCiBk', 'ip': '10.0.0.1', 'os': 'windows', 'version': '2012r2'}, {'hostname': 'tQcOv', 'ip': '10.0.0.2', 'os': 'redhat', 'version': '7'}, {'hostname': 'RwyOa', 'ip': '10.0.0.3', 'os': 'ubuntu', 'version': '18.04'}, {'hostname': 'IpQQa', 'ip': '10.0.0.4', 'os': 'windows', 'version': '2019'}, {'hostname': 'MCJEF', 'ip': '10.0.0.5', 'os': 'windows', 'version': '2019'}, {'hostname': 'OjAhw', 'ip': '10.0.0.6', 'os': 'redhat', 'version': '7.2'}, {'hostname': 'YkWrx', 'ip': '10.0.0.7', 'os': 'windows', 'version': '2019'}, {'hostname': 'KnFfQ', 'ip': '10.0.0.8', 'os': 'windows', 'version': '2016'}, {'hostname': 'iTIpJ', 'ip': '10.0.0.9', 'os': 'windows', 'version': '2019'}, {'hostname': 'KiEzv', 'ip': '10.0.0.10', 'os': 'redhat', 'version': '7.2'}, {'hostname': 'MgyOd', 'ip': '10.0.0.11', 'os': 'ubuntu', 'version': '18.04'}, {'hostname': 'uJEfD', 'ip': '10.0.0.12', 'os': 'redhat', 'version': '8'}, {'hostname': 'TRJjy', 'ip': '10.0.0.13', 'os': 'redhat', 'version': '7'}, {'hostname': 'zPeFd', 'ip': '10.0.0.14', 'os': 'redhat', 'version': '7'}, {'hostname': 'PHrYq', 'ip': '10.0.0.15', 'os': 'windows', 'version': '2016'}, {'hostname': 'CjrAr', 'ip': '10.0.0.16', 'os': 'ubuntu', 'version': '16.04'}, {'hostname': 'NaOrF', 'ip': '10.0.0.17', 'os': 'ubuntu', 'version': '20.04'}, {'hostname': 'PvhvF', 'ip': '10.0.0.18', 'os': 'windows', 'version': '2016'}, {'hostname': 'dIuqi', 'ip': '10.0.0.19', 'os': 'redhat', 'version': '7'}, {'hostname': 'fLHFB', 'ip': '10.0.0.20', 'os': 'ubuntu', 'version': '20.04'}]

ใช้ pprint

pprint(devices, width=-1)

จะได้

[{'hostname': 'KCiBk',
'ip': '10.0.0.1',
'os': 'windows',
'version': '2012r2'},
{'hostname': 'tQcOv',
'ip': '10.0.0.2',
'os': 'redhat',
'version': '7'},
{'hostname': 'RwyOa',
'ip': '10.0.0.3',
'os': 'ubuntu',
'version': '18.04'},
{'hostname': 'IpQQa',
'ip': '10.0.0.4',
'os': 'windows',
'version': '2019'},
{'hostname': 'MCJEF',
'ip': '10.0.0.5',
'os': 'windows',
'version': '2019'},
{'hostname': 'OjAhw',
'ip': '10.0.0.6',
'os': 'redhat',
'version': '7.2'},
{'hostname': 'YkWrx',
'ip': '10.0.0.7',
'os': 'windows',
'version': '2019'},
{'hostname': 'KnFfQ',
'ip': '10.0.0.8',
'os': 'windows',
'version': '2016'},
{'hostname': 'iTIpJ',
'ip': '10.0.0.9',
'os': 'windows',
'version': '2019'},
{'hostname': 'KiEzv',
'ip': '10.0.0.10',
'os': 'redhat',
'version': '7.2'},
{'hostname': 'MgyOd',
'ip': '10.0.0.11',
'os': 'ubuntu',
'version': '18.04'},
{'hostname': 'uJEfD',
'ip': '10.0.0.12',
'os': 'redhat',
'version': '8'},
{'hostname': 'TRJjy',
'ip': '10.0.0.13',
'os': 'redhat',
'version': '7'},
{'hostname': 'zPeFd',
'ip': '10.0.0.14',
'os': 'redhat',
'version': '7'},
{'hostname': 'PHrYq',
'ip': '10.0.0.15',
'os': 'windows',
'version': '2016'},
{'hostname': 'CjrAr',
'ip': '10.0.0.16',
'os': 'ubuntu',
'version': '16.04'},
{'hostname': 'NaOrF',
'ip': '10.0.0.17',
'os': 'ubuntu',
'version': '20.04'},
{'hostname': 'PvhvF',
'ip': '10.0.0.18',
'os': 'windows',
'version': '2016'},
{'hostname': 'dIuqi',
'ip': '10.0.0.19',
'os': 'redhat',
'version': '7'},
{'hostname': 'fLHFB',
'ip': '10.0.0.20',
'os': 'ubuntu',
'version': '20.04'}]

ใช้ for loop + dict.items() + F-strings

for device in devices:
for key, value in device.items():
print(f'{key:>10s}: {value}')
print('-' * 30)

ขออธิบายนิดเดียวละกันครับ ตรง :>10s เป็นการจัด format ให้กับส่วน key โดยเตรียมระยะหรือจำนวนตัวอักษรไว้ 10 ตัวและก็จัดชิดขวาไปแบบรูปด้านล่างนี้

จะได้

hostname: KCiBk
ip: 10.0.0.1
os: windows
version: 2012r2
------------------------------
hostname: tQcOv
ip: 10.0.0.2
os: redhat
version: 7
------------------------------
hostname: RwyOa
ip: 10.0.0.3
os: ubuntu
version: 18.04
------------------------------
hostname: IpQQa
ip: 10.0.0.4
os: windows
version: 2019
------------------------------
hostname: MCJEF
ip: 10.0.0.5
os: windows
version: 2019
------------------------------
hostname: OjAhw
ip: 10.0.0.6
os: redhat
version: 7.2
------------------------------
hostname: YkWrx
ip: 10.0.0.7
os: windows
version: 2019
------------------------------
hostname: KnFfQ
ip: 10.0.0.8
os: windows
version: 2016
------------------------------
hostname: iTIpJ
ip: 10.0.0.9
os: windows
version: 2019
------------------------------
hostname: KiEzv
ip: 10.0.0.10
os: redhat
version: 7.2
------------------------------
hostname: MgyOd
ip: 10.0.0.11
os: ubuntu
version: 18.04
------------------------------
hostname: uJEfD
ip: 10.0.0.12
os: redhat
version: 8
------------------------------
hostname: TRJjy
ip: 10.0.0.13
os: redhat
version: 7
------------------------------
hostname: zPeFd
ip: 10.0.0.14
os: redhat
version: 7
------------------------------
hostname: PHrYq
ip: 10.0.0.15
os: windows
version: 2016
------------------------------
hostname: CjrAr
ip: 10.0.0.16
os: ubuntu
version: 16.04
------------------------------
hostname: NaOrF
ip: 10.0.0.17
os: ubuntu
version: 20.04
------------------------------
hostname: PvhvF
ip: 10.0.0.18
os: windows
version: 2016
------------------------------
hostname: dIuqi
ip: 10.0.0.19
os: redhat
version: 7
------------------------------
hostname: fLHFB
ip: 10.0.0.20
os: ubuntu
version: 20.04
------------------------------

ใช้ tabulate และ itemgetter

tabular_output = tabulate(sorted(devices, key=itemgetter('os', 'version')), headers='keys')
print(tabular_output)

สำหรับ code ด้านบน เป็นการใช้ tabulate ช่วยสร้าง output ออกมาเป็นตาราง ซึ่งจุดสำคัญที่อยากอธิบายมี 2 ส่วน

ส่วนแรก sorted(devices, key=itemgetter('os', 'version')) เป็นการบอกว่าข้อมูลใน devices list นั้นผมต้องการจัดเรียงโดยเรียงจากข้อมูลที่ key os ก่อน สังเกตว่า output จะไล่มาจาก redhat, ubuntu แล้วตามด้วย windows

จากนั้นเรียงลำดับอีกครั้งจาก version สังเกต redhat ก็จะเรียงจากเวอร์ชั่น 7 → 7. 2 → 8 หรือ ubuntu ก็จาก 16.04 → 18.04 → 20.04 เป็นต้น

ส่วนที่สองคือ headers='keys' ซึ่งเป็นการบอก tabulate ว่าให้ใช้ key ใน dictionary ในการสร้าง header ของตารางให้เราอัตโนมัติ

เพราะฉะนั้นจะได้

hostname    ip         os       version
---------- --------- ------- ---------
tQcOv 10.0.0.2 redhat 7
TRJjy 10.0.0.13 redhat 7
zPeFd 10.0.0.14 redhat 7
dIuqi 10.0.0.19 redhat 7
OjAhw 10.0.0.6 redhat 7.2
KiEzv 10.0.0.10 redhat 7.2
uJEfD 10.0.0.12 redhat 8
CjrAr 10.0.0.16 ubuntu 16.04
RwyOa 10.0.0.3 ubuntu 18.04
MgyOd 10.0.0.11 ubuntu 18.04
NaOrF 10.0.0.17 ubuntu 20.04
fLHFB 10.0.0.20 ubuntu 20.04
KCiBk 10.0.0.1 windows 2012r2
KnFfQ 10.0.0.8 windows 2016
PHrYq 10.0.0.15 windows 2016
PvhvF 10.0.0.18 windows 2016
IpQQa 10.0.0.4 windows 2019
MCJEF 10.0.0.5 windows 2019
YkWrx 10.0.0.7 windows 2019
iTIpJ 10.0.0.9 windows 2019

ก็หวังว่าจะเป็นประโยชน์สำหรับทุกท่านที่เขียน Python และขี้เกียจแบบผมครับ

— — — — — — — — — — — — — — —
สารบัญเนื้อหาทั้งหมด (My Contents)
— — — — — — — — — — — — — — —

--

--

Nopnithi Khaokaew (Game)
Nopnithi Khaokaew (Game)

Written by Nopnithi Khaokaew (Game)

Cloud Solutions Architect & Hobbyist Developer | 6x AWS Certified, CKA, CKAD, 2x HashiCorp Certified (Terraform, Vault), etc.

No responses yet