Сортировать по типу объекта - PullRequest
1 голос
/ 28 мая 2010

У меня есть код, который статически регистрирует пары (type, handler_function) во время загрузки модуля, в результате получается такой вот что:

HANDLERS = {
  str: HandleStr,
  int: HandleInt,
  ParentClass: HandleCustomParent,
  ChildClass: HandleCustomChild
  }

def HandleObject(obj):
  for data_type in sorted(HANDLERS.keys(), ???):
    if isinstance(obj, data_type):
      HANDLERS[data_type](obj)

Где ChildClass наследуется от ParentClass. Проблема в том, что, поскольку это диктат, порядок не определен - но как мне исследовать объекты типа, чтобы выяснить ключ сортировки ?

Результирующий порядок должен быть дочерними классами, за которыми следуют суперклассы (сначала наиболее конкретные типы). Например. str предшествует basestring, а ChildClass предшествует ParentClass. Если типы не связаны, не имеет значения, куда они идут по отношению друг к другу.

Ответы [ 3 ]

5 голосов
/ 28 мая 2010

Если вы знаете, что всегда имеете дело с классами нового стиля:

def numberofancestors(klass):
    return len(klass.mro())

или, если вы беспокоитесь, в миксе могут быть классы старого стиля:

import inspect

def numberofancestors(klass):
    return len(inspect.getmro(klass))

, а затем, в любом случае,

sorted(HANDLERS, key=numberofancestors, reversed=True)

даст вам то, что вам нужно (вам не нужна часть .keys()).

@ Предложение Игнасио о топологической сортировке теоретически верно, но, поскольку, учитывая класс, вы можете легко и быстро получить число его предшественников (AKA «предки» ... в странном смысле слова, посредством которого вы ' один из ваших предков ;-), с этими numberofancestors функциями мой подход гораздо более практичен: он основан на очевидном факте, что у любого производного класса есть по крайней мере еще один «предок», чем у любого из его базовых классов, и поэтому , с этим key=, он всегда будет сортировать перед любой из своих баз.

Несвязанные классы могут заканчиваться в произвольном порядке (точно так же, как они могут быть в топологическом порядке), но вы дали понять, что вас это не волнует.

Редактировать : ФП, рассуждая в следующей ветке комментариев об оптимальной поддержке для случаев множественного наследования, придумал принципиально иную идею, чем оригинальная «предварительная сортировка», встроенная в вопрос, но его предложение о том, как реализовать эту радикальную идею, не является оптимальным:

[h for h in [HANDLERS.get(c) for c in type(obj).mro()] if h is not None][0]

идея хороша (если интересует поддержка множественного наследования), но, вероятно, лучшая реализация будет (Python 2.6 или выше):

next(Handlers[c] for c in type(obj).mro() if c in Handlers)

Обычно adict.get(k) and check for not None быстрее, чем if k in adict: adict[k], но это не совсем нормальный случай, потому что для использования get требуется создание «поддельного» списка из одного элемента и «зацикливание» его для имитации назначения.

В более общем смысле, создание целого списка с помощью понимания только для того, чтобы взять его [0] -й элемент - это лишняя работа - встроенная функция next, вызываемая для genexp, действует больше как first, например, "дай мне первый элемент «генаэксперта» и не выполняет никакой дополнительной работы. Он вызывает StopIteration вместо IndexError, если listcomp / genexp пуст, но обычно это не проблема; у вас также может быть второй аргумент для next, который будет использоваться в качестве «значения по умолчанию», если genexp пуст.

В 2.5 и более ранних версиях вам придется использовать (thegenexp).next() вместо этого (и нет способа дать ему аргумент по умолчанию), но, хотя синтаксически и менее блестящий, он более или менее эквивалентен конструкции 2.6 и лучше в семантике и скорости.

Я уверен, что рад, что обсуждение продолжилось в комментариях, потому что я думаю, что этот результирующий вывод является полезным и потенциально полезным (хотя, возможно, не в точной среде приложения OP, где множественное наследование может фактически не быть проблемой).

1 голос
/ 28 мая 2010

Выполните топологическую сортировку с __bases__ членами каждого класса.

0 голосов
/ 28 мая 2010

Используйте collections.OrderedDict из Python 2.7 или 3.1.Он написан на чистом Python, поэтому вы можете легко подключить его или адаптировать к более ранней версии, если это необходимо.

OrderedDict будет поддерживать порядок вставки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...