迭代过程中Python字典的顺序是否得到保证?

Chi*_*chi 34 python dictionary numpy scientific-computing scipy

我目前正在使用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)
Run Code Online (Sandbox Code Playgroud)

所以,问题是,当我遍历字典中Species.sumOverAllReactionsForThisSpecies()FermentationChamber.step(),是迭代顺序保证是相同的字典的,如果没有元素被添加或从第一个和最后一个迭代的字典中删除?也就是说,我可以假设在字典的每次迭代中创建的numpy数组的顺序不会变化吗?例如,如果字典具有格式{'Glucose':10,'Fructose':12},如果从该字典创建的数组将始终具有相同的顺序(无论该顺序是什么,只要这是确定性的).

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

tru*_*ppo 65

是的,如果未修改,则保证相同的订单.

请参阅此处的文档.

编辑:

关于如果更改值(但不添加/删除键)会影响订单,这就是C源中的注释所说的:

/* 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.
 */
Run Code Online (Sandbox Code Playgroud)

它似乎不是一个实现细节,而是语言的要求.

  • 我很确定"没有修改"意味着*没有*修改,期限.更改值*可能*更改字典排序顺序. (3认同)
  • @Chinmay,请确保你理解"改变价值"的含义.如果这些值实际上是实例,而不是原语,那么如果你只更改那些实例上的属性但不用其他实例替换字典中的实例,那么你实际上并没有在字典中"更改值"而你赢了不会影响订单.明确? (2认同)

use*_*610 17

这取决于 Python 版本。

蟒蛇 3.7+

字典迭代顺序保证按插入顺序排列。

蟒蛇 3.6

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

以前的版本

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

-R 选项

Python 2.6 添加了-R 选项作为(事实证明不够充分)防止散列泛洪攻击的保护。在 Python 2 中,启用受影响的字典迭代顺序(上面指定的属性仍然保留,但特定的迭代顺序会因程序的一次执行而异)。因此,默认情况下该选项处于关闭状态。

在 Python 3 中,-R 选项默认开启自 Python 3.3 起,这为 dict 迭代顺序添加了不确定性,因为每次运行 Python 解释器时,哈希计算的种子值都是随机生成的。这种情况一直持续到 CPython 3.6,它以某种方式更改了 dict 实现,以便条目的哈希值不会影响迭代顺序。

来源

  • 在 3.7 版更改: 字典顺序保证是插入顺序。此行为是 3.6 中 CPython 的实现细节。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

  • 感谢您制作此汇编!实际上来到这里看起来就像你的答案。 (2认同)

Gab*_*ley 8

如果没有对字典进行修改,答案是肯定的.请参阅此处的文档.

但是,字典在Python中本质上是无序的.通常,依靠字典来处理敏感的排序数据并不是最佳做法.

一个更强大的解决方案的例子是Django的SortedDict数据结构.


Ned*_*der 7

如果您希望订单保持一致,我会采取措施强制执行特定订单.虽然你可能能够说服自己订单得到保证,而你可能是对的,但对我来说这似乎很脆弱,对其他开发者来说这将是神秘的.

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


Der*_*itz 6

我还建议不要依赖字典顺序是非随机的.

如果你想要一个内置的解决方案来排序你的字典,请阅读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)]
Run Code Online (Sandbox Code Playgroud)


Joh*_*gko 3

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

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

  • 这实际上并没有回答问题,并且使用 OrderedDict 在保证确定性排序的同时,还会增加资源使用(但不确定具体以何种方式)。正如其他答案所示,普通的字典已经有这个保证,所以不需要使用 OrderedDict (对于这个特定的用例)。 (8认同)