51

我目前正在使用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()and中迭代字典时FermentationChamber.step(),如果在第一次和最后一次迭代之间没有从字典中添加或删除任何元素,字典的迭代顺序是否保证相同?也就是说,我可以假设字典中每次迭代创建的 numpy 数组的顺序不会改变吗?例如,如果字典的格式为 {'Glucose':10, 'Fructose':12},如果从该字典创建的 Array 将始终具有相同的顺序(无论该顺序是什么,只要这是确定性的)。

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

4

6 回答 6

76

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

请参阅此处的文档。

编辑:

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

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

于 2010-01-12T22:43:49.323 回答
22

这取决于 Python 版本。

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 中,从 Python 3.3 开始默认启用 -R 选项,这将不确定性添加到 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

于 2020-03-20T13:55:29.563 回答
8

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

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

一个更健壮的解决方案的例子是Django 的 SortedDict 数据结构

于 2010-01-12T22:49:02.887 回答
7

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

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

于 2010-01-12T22:49:12.763 回答
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)]
于 2010-01-12T22:54:33.857 回答
3

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

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

于 2010-01-13T05:30:13.073 回答