Если вы знаете, что всегда имеете дело с классами нового стиля:
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, где множественное наследование может фактически не быть проблемой).