【问题标题】:Is the order of a Python dictionary guaranteed over iterations?Python 字典的顺序是否在迭代中得到保证?
【发布时间】:2011-01-04 09:53:35
【问题描述】:

我目前正在使用SciPy.integrate.ode 在 Python 中实现一个复杂的微生物食物网。我需要能够轻松地将物种和反应添加到系统中,所以我必须编写一些非常通用的代码。我的方案如下所示:

class Reaction(object):
    def __init__(self):
        #stuff common to all reactions
    def __getReactionRate(self, **kwargs):
        raise NotImplementedError

... Reaction subclasses that 
... implement specific types of reactions


class Species(object):
    def __init__(self, reactionsDict):
        self.reactionsDict = reactionsDict
        #reactionsDict looks like {'ReactionName':reactionObject, ...}
        #stuff common to all species

    def sumOverAllReactionsForThisSpecies(self, **kwargs):
        #loop over all the reactions and return the 
        #cumulative change in the concentrations of all solutes

...Species subclasses where for each species
... are defined and passed to the superclass constructor

class FermentationChamber(object):
    def __init__(self, speciesList, timeToSolve, *args):
        #do initialization

    def step(self):
        #loop over each species, which in turn loops 
        #over each reaction inside it and return a 
        #cumulative dictionary of total change for each 
        #solute in the whole system


if __name__==__main__:
    f = FermentationChamber(...)

    o  = ode(...) #initialize ode solver

    while o.successful() and o.t<timeToSolve:
         o.integrate()

    #process o.t and o.y (o.t contains the time points
    #and o.y contains the solution matrix)

所以,问题是,当我遍历 Species.sumOverAllReactionsForThisSpecies()FermentationChamber.step() 中的字典时,如果在第一个和最后一次迭代?也就是说,我可以假设字典中每次迭代创建的 numpy 数组的顺序不会改变吗?例如,如果字典的格式为 {'Glucose':10, 'Fructose':12},如果从该字典创建的数组将始终具有相同的顺序(不管是什么该顺序是确定性的)。

对不起,我只是想让你知道我来自哪里。

【问题讨论】:

  • @ChinmayKanchi 你介意我大量编辑这个问题吗?所有关于食物网和集成 ODE 的细节都与这个问题无关,这是一个非常好的和重要的问题。
  • Python 3.6+ 在stackoverflow.com/questions/39980323/… 中有很好的介绍

标签: python dictionary numpy scipy scientific-computing


【解决方案1】:

是的,如果不修改,保证相同的顺序。

请参阅文档here

编辑:

关于更改值(但不添加/删除键)是否会影响顺序,这是 C 源代码中的 cmets 所说的:

/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
 * dictionary if it's merely replacing the value for an existing key.
 * This means that it's safe to loop over a dictionary with PyDict_Next()
 * and occasionally replace a value -- but you can't insert new keys or
 * remove them.
 */

这似乎不是实现细节,而是语言的要求。

【讨论】:

  • 啊,太好了!我不确定我是否正确解释了这一点。可以肯定的是,本身是否被修改并不重要,只要键不被修改?
  • 我很确定“没有修改”意味着 没有 修改,句号。更改值可能改变字典排序顺序。
  • @Chinmay,请确保您理解“改变价值”在这里的含义。如果这些值确实是实例,而不是原始值,那么如果您只是更改这些实例的属性,而不是用其他实例替换字典中的实例,那么您实际上并没有“更改字典中的值”并且您赢了不会影响订单。清除吗?
  • Python 中应该有一个数据结构,它给出了二叉树的属性:指定的排序、对数插入和删除,以及双向的恒定时间排序迭代。我已经为 Python 中缺少这个而苦恼了好几次。
  • 也就是说,Python 中的一种常见方法是使用排序数组和二进制搜索进行查找。 Python 的排序算法非常擅长对部分排序的列表进行排序,而 bisect 模块处理搜索(不要自己实现二进制搜索;很容易出错)。这不是二叉树的一般替代品,但它可能就是您所需要的。
【解决方案2】:

这取决于 Python 版本。

Python 3.7+

字典的迭代顺序保证按照插入的顺序。

Python 3.6

字典迭代顺序恰好是 CPython 实现中的插入顺序,但这不是该语言的书面保证。

以前的版本

键和值以非随机的任意顺序迭代,随 Python 实现而变化,并且取决于字典的插入和删除历史。如果键、值和项目视图被迭代而没有对字典进行干预修改,项目的顺序将直接对应。 https://docs.python.org/2/library/stdtypes.html#dictionary-view-objects

-R 选项

Python 2.6 添加了-R option 作为(事实证明不足)针对哈希泛洪攻击的保护。在 Python 2 中,将其打开受影响的字典迭代顺序(上面指定的属性仍保持不变,但具体的迭代顺序会因程序的一次执行与下一次执行不同)。因此,默认情况下该选项处于关闭状态。

在 Python 3 中,自 Python 3.3 以来的 -R option is on by default 为 dict 迭代顺序添加了不确定性,因为每次运行 Python 解释器时,都会随机生成哈希计算的种子值。这种情况一直持续到 CPython 3.6 改变了 dict 实现,使得条目的哈希值不会影响迭代顺序。

来源

  • 在 3.7 版中更改:保证字典顺序为插入顺序。这种行为是 CPython 3.6 的实现细节。 https://docs.python.org/3.8/library/stdtypes.html

  • Python 3.6 中的新功能:这个新实现的顺序保留方面被认为是一个实现细节,不应依赖(这在未来可能会改变,但希望在语言中使用这个新的 dict 实现在更改语言规范以强制所有当前和未来的 Python 实现保持顺序的语义之前的几个版本;这也有助于保持与随机迭代顺序仍然有效的旧版本语言的向后兼容性,例如 Python 3.5)。 https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-compactdict

【讨论】:

  • 感谢您的编译!实际上来到这里看起来像你的答案。
  • T. WInters 和其他人 [1] 在“Google 的软件工程”中对 dict 迭代保证进行了很好的两页讨论。没有什么令人惊讶的,但它是重点和参考哈希泛洪攻击[2]。 [1] books.google.cz/… [2] youtube.com/watch?v=Vdrab3sB7MU
【解决方案3】:

如果对字典进行no修改,答案是肯定的。 See the docs here.

但是,Python 中的字典本质上是无序的。一般来说,依赖字典来存储敏感的排序数据并不是最佳做法。

Django's SortedDict data structure 是一个更强大的解决方案示例。

【讨论】:

    【解决方案4】:

    如果您希望订单保持一致,我会做一些事情来强制执行特定订单。尽管您可能能够说服自己订单是有保证的,而且您可能是对的,但对我来说它似乎很脆弱,而对其他开发者来说它会很神秘。

    例如,您在问题中强调总是。在 Python 2.5 和 2.6 中保持相同的顺序是否重要? 2.6 和 3.1? CPython 和 Jython?我不会指望那些。

    【讨论】:

    • 好点。当我问这个问题时,我不确定它会有多脆弱。重新考虑这个算法肯定是有必要的。
    【解决方案5】:

    我也建议不要依赖字典顺序是非随机的这一事实。

    如果您想要一个内置的解决方案来对您的字典进行排序,请阅读 http://www.python.org/dev/peps/pep-0265/

    这是最相关的材料:

    此 PEP 被拒绝,因为对它的需求在很大程度上已被 由 Py2.4 的 sorted() 内置函数实现:

        >>> sorted(d.iteritems(), key=itemgetter(1), reverse=True)
        [('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]
    
    or for just the keys:
    
        >>> sorted(d, key=d.__getitem__, reverse=True)
        ['b', 'd', 'c', 'a', 'e']
    
    Also, Python 2.5's heapq.nlargest() function addresses the common use
    case of finding only a few of the highest valued items:
    
        >>> nlargest(2, d.iteritems(), itemgetter(1))
        [('b', 23), ('d', 17)]
    

    【讨论】:

      【解决方案6】:

      Python 3.1 有一个collections.OrderedDict 类可用于此目的。它也非常高效:“所有方法的 Big-O 运行时间都与常规字典相同。”

      code for OrderedDict 本身与 Python 2.x 兼容,尽管一些继承的方法(来自 _abcoll 模块)确实使用 Python 3 独有的功能。但是,可以毫不费力地将它们修改为 2.x 代码。

      【讨论】:

      • 这实际上并没有回答这个问题,并且使用 OrderedDict 将在保证确定性排序的同时增加资源使用(虽然不确定具体是什么方式)。正如其他答案所表明的那样,普通的 dict 已经有了这个保证,所以不需要使用 OrderedDict (对于这个特定的用例)。
      猜你喜欢
      • 1970-01-01
      • 2015-09-18
      • 2015-10-03
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      相关资源
      最近更新 更多