Захват именованных групп в регулярном выражении с помощью re.findall

Когда я пытался ответить на этот вопрос: regex для разделения% возрастов и значений в python я заметил, что мне пришлось переупорядочить группы из результата findall. Например:

data = """34% passed 23% failed 46% deferred"""
result = {key:value for value, key in re.findall('(\w+)%\s(\w+)', data)}
print(result)
>>> {'failed': '23', 'passed': '34', 'deferred': '46'}

Вот результат findall:

>>> re.findall('(\w+)%\s(\w+)', data)
>>> [('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]

Есть ли способ изменить/указать порядок групп, который возвращает re.findall:

[('passed', '34'), ('failed', '23'), ('deferred', '46')]

Просто чтобы уточнить, вопрос:

Можно ли указать порядок или изменить порядок групп для возврата функции re.findall?

Я использовал приведенный выше пример, чтобы создать словарь, чтобы указать причину/вариант использования, когда вы хотите изменить порядок (сделав ключ значением, а значение — ключом).

Дальнейшее уточнение:

Чтобы обрабатывать группы в более крупных и сложных регулярных выражениях, вы можете называть группы, но эти имена доступны только при выполнении re.search pr re.match. Из того, что я прочитал, findall имеет фиксированные индексы для групп, возвращаемых в кортеже. Вопрос в том, кто-нибудь знает, как эти индексы можно изменить. Это поможет упростить работу с группами и сделать ее интуитивно понятной.


person ashwinjv    schedule 02.09.2014    source источник
comment
Невозможно изменить порядок групп, возвращаемых findall, но их легко изменить постфактум, как я показал во втором ответе: stackoverflow.com/a/25629693/20789   -  person Dan Lenski    schedule 02.09.2014
comment
Это то, что я предполагал, но не смог найти документацию, подтверждающую это. Отсюда мой вопрос здесь.   -  person ashwinjv    schedule 02.09.2014


Ответы (3)


Возьмите 3, основываясь на дальнейшем разъяснении намерений ОП в этот комментарий.

Эшвин прав, что findall не сохраняет именованные группы захвата (например, (?P<name>regex)). finditer на помощь! Он возвращает отдельные объекты соответствия один за другим. Простой пример:

data = """34% passed 23% failed 46% deferred"""
for m in re.finditer('(?P<percentage>\w+)%\s(?P<word>\w+)', data):
    print( m.group('percentage'), m.group('word') )
person Dan Lenski    schedule 02.09.2014

За комментарий ОП к моему первому ответу : Если вы просто пытаетесь переупорядочить список из двух кортежей следующим образом:

[('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]

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

[('passed', '34'), ('failed', '23'), ('deferred', '46')]

Есть простое решение: используйте понимание списка с синтаксисом среза sequence[::-1], чтобы изменить порядок элементов отдельных кортежей:

a = [('34', 'passed'), ('23', 'failed'), ('46', 'deferred')]
b = [x[::-1] for x in a]
print b
person Dan Lenski    schedule 02.09.2014
comment
Я знаю, как изменить порядок кортежей, вопросы заключаются в том, чтобы указать порядок повторного поиска. - person ashwinjv; 02.09.2014
comment
Порядок что до re-findall? Я покажу вам, как взять вывод re.findall и изменить его так, чтобы он имел желаемый порядок. - person Dan Lenski; 02.09.2014
comment
Чтобы обрабатывать группы в более крупных и сложных регулярных выражениях, вы можете называть группы, но эти имена доступны только при выполнении re.search pr re.match. Из того, что я прочитал, findall имеет фиксированные индексы для групп, возвращаемых в кортеже. Вопрос в том, кто-нибудь знает, как эти индексы можно изменить. Это поможет упростить работу с группами и сделать ее интуитивно понятной. - person ashwinjv; 02.09.2014
comment
Документация здесь: docs.python.org/3.1/library/re. html#re.findall говорит, что вы получите список кортежей с группами, но не говорит об индексах групп в этом кортеже. - person ashwinjv; 02.09.2014
comment
Ах, именованные группы - это отдельная тема (тоже не в вашем вопросе). Вы правы, что findall возвращает только захваченные группы и игнорирует имена; однако вы можете просто использовать finditer вместо того, чтобы возвращать объекты соответствия , с помощью которого вы сможете получить доступ к именованным группам. - person Dan Lenski; 02.09.2014
comment
Этого сэра я и искал. Если вы можете добавить/изменить свой ответ, я приму его. Спасибо - person ashwinjv; 02.09.2014

Как вы определили во втором примере, re.findall возвращает группы в исходном порядке.

Проблема в том, что стандартный тип Python dict никаким образом не сохраняет порядок ключей. Вот руководство для Python 2.x, в котором это делается явно, но это все еще верно для Python 3.x: https://docs.python.org/2/library/stdtypes.html#dict.items

Вместо этого вы должны использовать collections.OrderedDict:

from collections import OrderedDict as odict

data = """34% passed 23% failed 46% deferred"""
result = odict((key,value) for value, key in re.findall('(\w+)%\s(\w+)', data))
print(result)
>>> OrderedDict([('passed', '34'), ('failed', '23'), ('deferred', '46')])

Обратите внимание, что вы должны использовать форму парного конструктора (dict((k,v) for k,v in ...), а не конструктор понимания dict ({k:v for k,v in ...}). Это потому, что последний создает экземпляры dicttype, которые не могут быть преобразованы в OrderedDict без потери порядка ключей... что, конечно же, вы пытаетесь сохранить в первую очередь.

person Dan Lenski    schedule 02.09.2014
comment
Мне было интересно, могу ли я указать или изменить первоначальный порядок возврата для re.findall. Преобразование в dict было еще одним примером того, когда я хочу изменить порядок групп. - person ashwinjv; 02.09.2014
comment
Ваш вопрос вообще не дает понять, что вы пытаетесь изменить порядок. Пожалуйста, отредактируйте его, чтобы уточнить это. - person Dan Lenski; 02.09.2014
comment
Обновление: Python dict сохраняет порядок ключей для новых версий Python (см. также SPEC SO Post ) - person dreftymac; 13.04.2019