2010-01-12 2 views
21

В настоящее время я реализую сложную микробную пищу в Интернете на Python, используя 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}, если Array, созданный из этого словаря, будет всегда имеет тот же порядок (неважно, что это за заказ, так как пока он детерминирован).

Извините за мега-пост, я просто хотел сообщить вам, откуда я.

+0

@ChinmayKanchi не возражаете, если я серьезно отредактировал этот вопрос? Все детали о пищевых сетях и интеграции ODE не имеют ничего общего с вопросом, который является очень хорошим и важным. – LondonRob

ответ

4

Python 3.1 имеет collections.OrderedDict класс, который может быть использован для этой цели. Это тоже очень эффективно: «Время работы Big-O для всех методов такое же, как для обычных словарей».

Сам code for OrderedDict совместим с Python 2.x, хотя некоторые унаследованные методы (из модуля _abcoll) используют функции только для Python 3. Тем не менее, они могут быть изменены с кодом 2.x с минимальными усилиями.

+1

Это фактически не отвечает на вопрос, и использование OrderedDict будет, в то же время гарантируя детерминированный порядок, увеличивать использование ресурсов (не уверен, каким образом именно это). Как показывают другие ответы, нормальный dict уже имеет эту гарантию, поэтому нет необходимости использовать OrderedDict (для этой конкретной usecase). –

43

Да, тот же заказ гарантирован, если он не изменен.

См. Документы here.

Edit:

Что касается, если изменение значения (но не добавление/удаление ключа) будет влиять на порядок, это то, что комментарии в 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. 
*/ 

Похоже, что это не деталь реализации, а требование языка.

+0

Ах, отлично! Я не был уверен, что правильно это интерпретирую. Чтобы быть уверенным, не имеет значения, изменились ли сами _values_, правильно, до тех пор, пока ключи отсутствуют? –

+2

Я уверен, что «без изменений» означает * нет * модификаций, периода. Изменение значений * может * изменять порядок сортировки словаря. –

+0

Проклятие! Похоже, мне придется пересмотреть этот алгоритм. Есть ли упорядоченная структура данных типа карты в scipy/numpy или стандартной библиотеке python? Я бы предпочел не зависеть от большего количества библиотек, чем должен. –

8

Предоставлено no Изменения сделаны в словаре, да. See the docs here.

Однако словари неупорядочены по природе в Python. В целом, это не лучшая практика полагаться на словари для чувствительных отсортированных данных.

Примером более надежного решения будет Django's SortedDict data structure.

7

Если вы хотите, чтобы заказ был последовательным, я бы сделал что-то, чтобы заставить конкретный заказ. Хотя вы могли бы убедить себя, что заказ гарантирован, и вы можете быть правы, это кажется хрупким для меня, и это будет загадочно для других разработчиков.

Например, вы указываете всегда в своем вопросе. Важно ли, чтобы это был тот же порядок в Python 2.5 и 2.6? 2,6 и 3,1? CPython и Jython? Я не стал бы на это рассчитывать.

+0

Точно! Хрупкость этого - то, что помешало бы мне сделать это ... –

+1

Хорошая точка. Я не был уверен, насколько хрупким было бы, когда я задал этот вопрос. Пересмотр этого алгоритма определенно в порядке. –

5

Я также рекомендовал бы не полагаться на то, что порядок словарей неслучайный.

Если вы хотите, встроенный в решение сортировки вы СЛОВАРЬ прочитать http://www.python.org/dev/peps/pep-0265/

Вот наиболее соответствующий материал:

Этот PEP отвергается, поскольку потребность в ней была в значительной степени выполняются py2.4 в сортируется() встроенная функция:

>>> 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)]