关于python:打印按键排序的字典项

Printing a Dictionary Items Sorted by Its Keys

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Company(object):
    def __init__(self, name):
        self.name = name
        self.employees = {}

    def addEmployee(self, id, name):
        self.employees[id] = name

    def displayEmployees(self):
        tmp = [ (k,v) for k,v in self.employees.items() ]
        tmp.sort()
        for k,v in tmp:
            print(k, '\t', v)

a = Company('The Company')
a.addEmployee(111, 'Employee1')
a.addEmployee(222, 'Employee2')
a.addEmployee(333, 'Employee3')
a.displayEmployees()

有没有另一种方法可以按字典的键对其进行排序,以便在不使用新变量的情况下保持以下输出?:

1
2
3
111      Employee1  
222      Employee2  
333      Employee3


对于大量员工,近乎最佳的可能是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Company(object):
    def __init__(self, name):
        self.name = name
        self.employee_ids = []
        self.employees = {}
        self.sorted = True

    def addEmployee(self, id, name):
        self.employee_ids.append(id)
        self.employees[id] = name
        self.sorted = False

    def displayEmployees(self):
        if not self.sorted:
            self.employee_ids.sort()
            self.sorted = True
        for k in self.employee_ids:
            print k, '\t', self.employees[k]

这就需要O(N)插入N个雇员,而每次插入都将self.employee_ids分类,这样的操作O(N squared)就可以完成。作为交换,这种方法使displayEmployees成为最坏情况的O(N log N),但通常由于"timsort"(python的排序算法(自然合并排序的变体)在现实世界中的出色性能而变得更好。例如,如果只添加一个雇员(随机ID可能需要放在中间),那么调用displayEmployees,这就是O(N)--timsort magic。

"有效爪哇"名声的Josh Bloch,当时是一名谷歌员工,在一个技术演讲中,展示了Python的Tim排序,然后隐喻地说:——在大马士革的路上被闪电击中——拔出他的笔记本电脑(我记得我们都坐在前排)开始黑客攻击。不久之后,TimSope成为Java对对象数组排序的方式(唉,不是一个原语数组),因为技术上的原因,它必须保持"快速排序"的一个不太健壮的变体。

顺便说一句,timsort是以其发明者tim peters命名的,在python循环中也被称为"tim bot"(作为python社区中的"bot",需要能够快速、正确地回答许多技术问题;tim是第一个如此受人尊敬的人)。第二个是F.Lundh,"Effbot"。后来我很荣幸地被命名为第三个(据我所知是最后一个),名为"马泰利机器人"。然而,我从来没有开发过比timsort酷十分之一的算法!-)

tl;dr:使用bisect按顺序维护一个列表是一个经典的、显然很酷的想法,但不要这样做。我不记得曾经看到过一种情况,那是一个明显的胜利。通常情况下,最好只将append种新东西列在列表中,并根据需要进行排序;偶尔,标准库中的模块heapq(插入部分为O(log N),而不是bisect中的模块O(N)可能更适合特殊应用。

另一个注意事项:self.sorted标志很小(?)只有当您可能重复调用displayEmployees,而中间没有addEmployee调用时,优化才是值得的;如果这样的模式不会发生,您可以通过省略它来简化代码,而不会产生任何不良影响--这不会改变big-o行为,无论如何:—)


只对键进行排序,并使用sorted()函数查找值:

1
2
3
def displayEmployees(self):
    for key in sorted(self.employees):
        print(key, self.employees[key], sep='\t')

或直接对项目排序:

1
2
3
def displayEmployees(self):
    for key, value in sorted(self.employees.items()):
        print(key, value, sep='\t')


几天前,我用__str__方法做了一个dict类,它或多或少地按照您想要的方式显示项目。看看你是否感兴趣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class EmployeeDict(dict):
    '''Just a dictionary, but with a better display of data.

    '''


    def __str__(self):
        str_output =""
        maxlen = len(str(max(self.keys())))
        for key in sorted(self.keys()):
            str_output +="{} | {}
"
.format(str(key).rjust(maxlen), self[key])
        return str_output


class Company(object):
    def __init__(self, name):
        self.name = name
        self.employees = EmployeeDict()

    def addEmployee(self, id, name):
        self.employees[id] = name

    def displayEmployees(self):
        print(str(self.employees))

a = Company('The Company')
a.addEmployee(111, 'Employee1')
a.addEmployee(222, 'Employee2')
a.addEmployee(333, 'Employee3')
a.displayEmployees()

输出:

1
2
3
111 | Employee1
222 | Employee2
333 | Employee3


普通听写不记得顺序。如果在代码的其余部分中保持dict的顺序对您很重要,那么另一种选择是使用Python的collections模块中的OrderedDict。您可以在添加员工时执行此操作:

1
2
3
4
5
6
7
from collections import OrderedDict

# ...

def addEmployee(self, id, name):
    self.employees[id] = name
    self.employees = OrderedDict(sorted(self.employees.items()))

这将始终保持您的self.employeesdict的顺序,并将您的显示代码减少到:

1
2
3
def displayEmployees(self):
    for k,v in self.employees.items():
        print(k, '\t', v)