【问题标题】:Printing a Dictionary Items Sorted by Its Keys [duplicate]打印按其键排序的字典项目[重复]
【发布时间】:2015-02-24 02:50:18
【问题描述】:
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()

是否有另一种方法可以通过键对字典进行排序以在不使用新变量的情况下保持以下输出?:

111      Employee1  
222      Employee2  
333      Employee3

【问题讨论】:

  • 这一定是无数问题的重复
  • 这个问题涉及打印按其键排序的字典所包含的项目。表示为重复的问题中接受的答案建议使用OrderedDict,它按排序顺序存储其键。

标签: python sorting python-3.x dictionary


【解决方案1】:

对于大量员工,接近最优的可能是:

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 worst-case O(N log N) - 但通常更好,因为“timsort”,Python的排序算法(自然归并排序的变体)在真实世界。例如,如果您只添加一名员工(可能需要在中间添加一个随机 id)然后调用displayEmployees,这只是O(N) -- timsort 魔术。

“Effective Java”成名的 Josh Bloch,当时是 Google 员工,在一次技术演讲中展示 Python 的 timsort 时,打个比方说:-),在去大马士革的路上被闪电击中 - 拿出了他的笔记本电脑(我记得我们都坐在前排)并开始黑客攻击。不久之后,timsort 也变成了 Java 对对象数组进行排序的方式(唉,不是原语数组——出于技术原因,它必须保持为“快速排序”的不太健壮的变体)。

顺便说一句,timsort 以其发明者 Tim Peters 的名字命名,在 Python 圈中也被称为“timbot”(在 Python 社区中作为“机器人”涉及能够非常快速地回答很多技术问题,通常是正确的;蒂姆是第一个如此荣幸的人)。第二个是 F.Lundh,“效果机器人”。我后来很荣幸被命名为第三个(据我所知是最后一个),称为“martellibot”。但是,我从来没有开发出任何算法像 timsort 一样酷!-)

TL;DR:使用bisect 来维护排序顺序的列表是一个经典且显然很酷的想法,但是,不要这样做。我不记得曾经看到过明显获胜的情况。通常,最好只将append 新内容添加到列表中,并根据需要进行排序;有时,标准库中的模块heapq(插入为O(log N),而不是O(N),如bisect)可能更适合特殊应用程序。

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

【讨论】:

    【解决方案2】:

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

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

    或对项目进行内联排序:

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

    【讨论】:

      【解决方案3】:

      常规 dicts 不记得顺序。如果在其余代码中保持 dict 的顺序对您很重要,一种替代方法是使用 Python 的 collections 模块中的 OrderedDict。您可以在添加员工时执行此操作:

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

      这将始终保持您的 self.employees 字典有序,并将您的显示代码减少到:

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

      【讨论】:

      • 每次创建一个全新的字典效率低下,打印的时候也可以排序
      • 是的,这就是为什么我说仅当订单对功能很重要时才说,而不仅仅是打印。不过感谢您提供的信息。
      【解决方案4】:

      前段时间,我用__str__ 方法创建了一个dict 类,它或多或少地以您想要的方式显示项目。看看你有没有兴趣:

      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 += "{} | {}\n".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()
      

      输出:

      111 | Employee1
      222 | Employee2
      333 | Employee3
      

      【讨论】:

      • 诚然,这更多是为了好玩,但仍然如此!
      • len(str(max(self.keys()))) 为您提供按字母顺序排列的最后一个键的长度——不是最长的键!相反,您需要max(len(k) for k in self.keys()) 或类似...
      • ...你为什么这么说?在这种情况下,键是整数。
      • 如果它们都是 非负 整数(不仅仅是更一般的“数字”),那么 len(str(max(self.keys()))) 恰好等于 max(len(str(k)) for k in self.keys()) [省略了 @ 987654328@ 在我之前的评论中,现在编辑为时已晚]。后者更通用,如果您碰巧知道所有键都是非负整数(“员工 ID”的常见但非通用约定),则前者更快。
      猜你喜欢
      • 1970-01-01
      • 2016-10-12
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      相关资源
      最近更新 更多