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

时间:2010-01-12 22:42:02

标签: python dictionary numpy scipy scientific-computing

我目前正在使用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},那么从这个字典创建的数组总是具有相同的顺序(无关紧要)只要它是确定性的,那个顺序就是。

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

6 个答案:

答案 0 :(得分:59)

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

请参阅文档here

修改

关于更改值(但不添加/删除键)会影响订单,这就是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.
 */

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

答案 1 :(得分:8)

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

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

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

答案 2 :(得分:7)

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

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

答案 3 :(得分: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)]

答案 4 :(得分:6)

这取决于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中已更改:保证字典顺序为插入顺序。此行为是3.6版CPython的实现细节。 https://docs.python.org/3.8/library/stdtypes.html

  • Python 3.6的新增功能:此新实现的顺序保留方面被视为实现细节,因此不应依赖(将来可能会有所变化,但是希望拥有新的dict实现在更改语言规范以对所有当前和将来的Python实现强制执行顺序保留语义之前,先在该语言中使用该版本;还可以帮助保持与仍旧有效的随机迭代顺序的旧版本语言的向后兼容性。 3.5)。 https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-compactdict

答案 5 :(得分:3)

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

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