参考链接:https://geek-docs.com/python/python-tutorial/python-prettytable.html#PrettyTable-4(我承认链接这个写得更详细)。
我们都知道 MySQL 的输出一般像下边这样的(格式化输出打印,很好看):
mysql> select * from tb_person;
+----+----------+-------------+------+------+-------------+---------------------+
| id | name | phone | age | sex | description | create_time |
+----+----------+-------------+------+------+-------------+---------------------+
| 1 | zhangsan | 132****2889 | 25 | M | NoDesc | 2020-11-30 20:03:07 |
| 3 | lisi | 152****7873 | 18 | F | None | 2020-11-30 20:08:33 |
| 5 | wangwu | 136****2908 | 25 | M | Nothing | 2020-11-30 20:10:11 |
| 10 | zhaoliu | 138****5322 | 15 | M | Nothing | 2020-11-30 20:12:11 |
+----+----------+-------------+------+------+-------------+---------------------+
就连 Ruby 都有 terminal-table 来输出这种表格,比如说 Ruby 像 MySQL 那样格式化打印表格,那么 Python 怎么像这样格式化打印结果呢?
prettytable 安装
pip3 install prettytable
[root@master ~]# pip3 install prettytable
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting prettytable
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/94/d5/52e48f3bcf66f838d411ad85c3ac9550c2451d082623e2d4d4df7411ed5c/prettytable-2.0.0-py3-none-any.whl
Requirement already satisfied: setuptools in /usr/lib/python3.6/site-packages (from prettytable)
Collecting wcwidth (from prettytable)
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/59/7c/e39aca596badaf1b78e8f547c807b04dae603a433d3e7a7e04d67f2ef3e5/wcwidth-0.2.5-py2.py3-none-any.whl
Installing collected packages: wcwidth, prettytable
Successfully installed prettytable-2.0.0 wcwidth-0.2.5
prettytable 导入
import prettytable as pt
prettytable 使用
import prettytable as pt
# 按行添加数据
tb = pt.PrettyTable()
tb.field_names = ["id", "name", "phone", "age", "sex", "description"]
tb.add_row([1, "zhangsan", "132****2889", 25, "M", "NoDesc"])
tb.add_row([3, "lisi", "152****7873", 18, "F", "None"])
tb.add_row([5, "wangwu", "136****2908", 25, "M", "Nothing"])
tb.add_row([10,"zhaoliu", "138****5322", 15, "M", "Nothing"])
print(tb)
# 按列添加数据
create_time = ["2020-11-30 20:03:07", "2020-11-30 20:08:33", "2020-11-30 20:10:11", "2020-11-30 20:12:11"]
tb.add_column("create_time", create_time)
print(tb)
prettytable 输出
[root@master python3_learning]# python3 test.py
+----+----------+-------------+-----+-----+-------------+
| id | name | phone | age | sex | description |
+----+----------+-------------+-----+-----+-------------+
| 1 | zhangsan | 132****2889 | 25 | M | NoDesc |
| 3 | lisi | 152****7873 | 18 | F | None |
| 5 | wangwu | 136****2908 | 25 | M | Nothing |
| 10 | zhaoliu | 138****5322 | 15 | M | Nothing |
+----+----------+-------------+-----+-----+-------------+
+----+----------+-------------+-----+-----+-------------+---------------------+
| id | name | phone | age | sex | description | create_time |
+----+----------+-------------+-----+-----+-------------+---------------------+
| 1 | zhangsan | 132****2889 | 25 | M | NoDesc | 2020-11-30 20:03:07 |
| 3 | lisi | 152****7873 | 18 | F | None | 2020-11-30 20:08:33 |
| 5 | wangwu | 136****2908 | 25 | M | Nothing | 2020-11-30 20:10:11 |
| 10 | zhaoliu | 138****5322 | 15 | M | Nothing | 2020-11-30 20:12:11 |
+----+----------+-------------+-----+-----+-------------+---------------------+
看看,是不是和 MySQL 数据库查询的结果是一样的,惊喜吗?敢动吗?
它还可以输出 html 的格式:
print(tb.get_html_string())
<table>
<tr>
<th>id</th>
<th>name</th>
<th>phone</th>
<th>age</th>
<th>sex</th>
<th>description</th>
<th>create_time</th>
</tr>
<tr>
<td>1</td>
<td>zhangsan</td>
<td>132****2889</td>
<td>25</td>
<td>M</td>
<td>NoDesc</td>
<td>2020-11-30 20:03:07</td>
</tr>
<tr>
<td>3</td>
<td>lisi</td>
<td>152****7873</td>
<td>18</td>
<td>F</td>
<td>None</td>
<td>2020-11-30 20:08:33</td>
</tr>
<tr>
<td>5</td>
<td>wangwu</td>
<td>136****2908</td>
<td>25</td>
<td>M</td>
<td>Nothing</td>
<td>2020-11-30 20:10:11</td>
</tr>
<tr>
<td>10</td>
<td>zhaoliu</td>
<td>138****5322</td>
<td>15</td>
<td>M</td>
<td>Nothing</td>
<td>2020-11-30 20:12:11</td>
</tr>
</table>
网页显示效果和命令行直接打印的差不多哟!
prettytable 其他命令
更多信息自己凭着好奇心去探讨哟!
tb.clear() # 清空对象数据
tb.clear_rows() # 清空数据行(field_names 的设置还是在的)
tb.del_row(row_index) # 删除指定索引的数据行
tb.get_html_string() # 转换成 html 格式
tb.field_names = [...] # 设置字段名
tb.border = False # 是否添加边框(无边框效果如下)
id name phone age sex description create_time
1 zhangsan 132****2889 25 M NoDesc 2020-11-30 20:03:07
3 lisi 152****7873 18 F None 2020-11-30 20:08:33
10 zhaoliu 138****5322 15 M Nothing 2020-11-30 20:12:11
tb.header = False # 是否显示字段行(不显示效果如下)
+----+----------+-------------+-------+---+---------+---------------------+
| 1 | zhangsan | 132****2889 | 25.23 | M | NoDesc | 2020-11-30 20:03:07 |
| 3 | lisi | 152****7873 | 18 | F | None | 2020-11-30 20:08:33 |
| 5 | wangwu | 136****2908 | 25 | M | Nothing | 2020-11-30 20:10:11 |
| 10 | zhaoliu | 138****5322 | 15 | M | Nothing | 2020-11-30 20:12:11 |
+----+----------+-------------+-------+---+---------+---------------------+
tb.print_empty = True # 空数据的时候打印表框架还是空字符串(是的效果如下)
++
||
++
++
tb.align['name'] = 'r' # name 字段列右对齐
...
实例演示
# test.json
{
"title": "Hackbench Performance Testing",
"unit": "KB/s",
"x_name": "bs|test_size",
"tables": {
"fio.read_iops": {
"average": {
"dimensions": [
"compare_dimension",
"openeuler 20.03"
],
"source": [
[
"4k|1G",
"4k|80G",
"16k|1G",
"32k|1G",
"64k|1G",
"128k|1G",
"256k|1G",
"512k|1G",
"1024k|1G"
],
[
"openeuler 20.03",
144076.2903315,
11601.099817,
37865.30472368628,
21145.10375497826,
14010.34254665909,
6701.240849466667,
3205.077255,
1367.476930860465,
673.3270888666667
]
]
},
"standard_deviation": {
"dimensions": [
"compare_dimension",
"openeuler 20.03"
],
"source": [
[
"4k|1G",
"4k|80G",
"16k|1G",
"32k|1G",
"64k|1G",
"128k|1G",
"256k|1G",
"512k|1G",
"1024k|1G"
],
[
"openeuler 20.03",
195,
0,
214,
205,
188,
183,
180,
191,
191
]
]
}
},
"fio.write_iops": {
"average": {
"dimensions": [
"compare_dimension",
"centos 7.6",
"openeuler 20.03"
],
"source": [
[
"4k|1G",
"16k|1G",
"32k|1G",
"64k|1G",
"128k|1G",
"256k|1G",
"512k|1G",
"1024k|1G"
],
[
"centos 7.6",
345243.028251,
142698.794456,
62108.34762725,
34747.729395,
26330.187008999997,
10317.85034025,
7471.708886999999,
3558.2993653999997
],
[
"openeuler 20.03",
122003.54468561111,
33528.52637123529,
31469.058358695653,
13870.135498022726,
8249.707439577778,
4329.454872088889,
1976.5380473953487,
1141.003158088889
]
]
},
"standard_deviation": {
"dimensions": [
"compare_dimension",
"centos 7.6",
"openeuler 20.03"
],
"source": [
[
"4k|1G",
"16k|1G",
"32k|1G",
"64k|1G",
"128k|1G",
"256k|1G",
"512k|1G",
"1024k|1G"
],
[
"centos 7.6",
97,
95,
122,
125,
100,
130,
101,
103
],
[
"openeuler 20.03",
174,
188,
171,
197,
181,
175,
170,
176
]
]
},
"change": {
"dimensions": [
"compare_dimension",
"centos 7.6 vs openeuler 20.03"
],
"source": [
[
"4k|1G",
"16k|1G",
"32k|1G",
"64k|1G",
"128k|1G",
"256k|1G",
"512k|1G",
"1024k|1G"
],
[
"centos 7.6 vs openeuler 20.03",
183.0,
325.6,
97.4,
150.5,
219.2,
138.3,
278.0,
211.9
]
]
}
}
}
}
# test.py
#!/usr/bin/env python3
import os
import sys
import json
import prettytable as pt
# receive compare_template.yaml and auto pretty show compare results
class TableShow:
def __init__(self, result_dict, row_size=8):
self.title = result_dict['title']
self.tables = result_dict['tables']
self.row_size = row_size
self.tb = None
def show_table(self):
for (table_title, table) in self.tables.items():
self.tb = pt.PrettyTable()
self.tb.header = True
self.set_field_names(table, table_title)
self.set_align(table_title)
self.add_row(table)
self.print_table()
def set_field_names(self, table, table_title):
field_names = [table_title]
field_names.extend(table['average']['source'][0])
self.tb.field_names = field_names
def set_align(self, table_title):
for field_name in self.tb.field_names:
self.tb.align[field_name] = 'r'
self.tb.align[table_title] = 'l'
def add_row(self, table):
row_names = ['average', 'standard_deviation', 'change']
max_size = max([len(row_name) for row_name in row_names])
for row_name in row_names:
if row_name not in table:
continue
dimensions_size = len(table[row_name]['dimensions'])
for index in range(1, dimensions_size):
row = table[row_name]['source'][index]
row_title = ' '. join([row_name + ' ' * (max_size - len(row_name)), row[0]])
if row_name == 'change':
format_data_row = ["%.1f%%" % data for data in row[1:]]
else:
format_data_row = ["%.2f" % data for data in row[1:]]
self.tb.add_row([row_title, *format_data_row])
def get_row_num(self):
row_num, rem = divmod(len(self.tb.field_names[1:]), self.row_size)
if rem > 0:
row_num += 1
self.tb.row_num = row_num
def print_table(self):
print(self.title)
self.get_row_num()
for row in range(self.tb.row_num):
start = 1 + row * self.row_size
end = start + self.row_size
# set the field names to include in displays
self.tb.fields = [self.tb.field_names[0], *self.tb.field_names[start:end]]
print(self.tb)
print()
if __name__ == '__main__':
# template_yaml = sys.argv[1]
# result_dict = json.loads(os.popen(f"compare -t {template_yaml}").read())
result_dict = json.load(open("test.json"))
table_show = TableShow(result_dict, 9)
table_show.show_table()
Hackbench Performance Testing
+------------------------------------+-----------+----------+----------+----------+----------+---------+---------+---------+----------+
| fio.read_iops | 4k|1G | 4k|80G | 16k|1G | 32k|1G | 64k|1G | 128k|1G | 256k|1G | 512k|1G | 1024k|1G |
+------------------------------------+-----------+----------+----------+----------+----------+---------+---------+---------+----------+
| average openeuler 20.03 | 144076.29 | 11601.10 | 37865.30 | 21145.10 | 14010.34 | 6701.24 | 3205.08 | 1367.48 | 673.33 |
| standard_deviation openeuler 20.03 | 195.00 | 0.00 | 214.00 | 205.00 | 188.00 | 183.00 | 180.00 | 191.00 | 191.00 |
+------------------------------------+-----------+----------+----------+----------+----------+---------+---------+---------+----------+
Hackbench Performance Testing
+--------------------------------------------------+-----------+-----------+----------+----------+----------+----------+---------+----------+
| fio.write_iops | 4k|1G | 16k|1G | 32k|1G | 64k|1G | 128k|1G | 256k|1G | 512k|1G | 1024k|1G |
+--------------------------------------------------+-----------+-----------+----------+----------+----------+----------+---------+----------+
| average centos 7.6 | 345243.03 | 142698.79 | 62108.35 | 34747.73 | 26330.19 | 10317.85 | 7471.71 | 3558.30 |
| average openeuler 20.03 | 122003.54 | 33528.53 | 31469.06 | 13870.14 | 8249.71 | 4329.45 | 1976.54 | 1141.00 |
| standard_deviation centos 7.6 | 97.00 | 95.00 | 122.00 | 125.00 | 100.00 | 130.00 | 101.00 | 103.00 |
| standard_deviation openeuler 20.03 | 174.00 | 188.00 | 171.00 | 197.00 | 181.00 | 175.00 | 170.00 | 176.00 |
| change centos 7.6 vs openeuler 20.03 | 183.0% | 325.6% | 97.4% | 150.5% | 219.2% | 138.3% | 278.0% | 211.9% |
+--------------------------------------------------+-----------+-----------+----------+----------+----------+----------+---------+----------+