Раствор
У вас есть два варианта:
- Использовать группу без захвата:
(?:@|#)\w+
- Или, что еще лучше, класс персонажей:
[@#]\w+
Ссылки
Понимание findall
Проблема, с которой вы столкнулись, связана с тем, как findall
возвращает совпадения в зависимости от количества присутствующих групп захвата.
Давайте внимательнее посмотрим на этот шаблон (аннотированный, чтобы показать группы):
((@|#)\w+)
|\___/ |
|group 2 | # Read about groups to understand
\________/ # how they're defined and numbered/named
group 1
Группы захвата позволяют нам сохранять совпадения во вложенных шаблонах в общих шаблонах.
p = re.compile(r'((@|#)\w+)')
m = p.match('@tweet')
print m.group(1)
# @tweet
print m.group(2)
# @
Теперь давайте посмотрим на документацию Python для модуля re
:
findall
: Возвращать все неперекрывающиеся совпадения шаблона в строке в виде списка строк. Строка сканируется слева направо, и совпадения возвращаются в указанном порядке. Если в шаблоне присутствует одна или несколько групп, вернуть список групп; это будет список кортежей, если шаблон имеет более одной группы.
Это объясняет, почему вы получаете следующее:
str = 'lala @tweet boo #this &that @foo#bar'
print(re.findall(r'((@|#)\w+)', str))
# [('@tweet', '@'), ('#this', '#'), ('@foo', '@'), ('#bar', '#')]
Как указано, поскольку шаблон содержит более одной группы, findall
возвращает список кортежей, по одному на каждое совпадение. Каждый кортеж дает вам то, что было захвачено группами для данного матча.
В документации также объясняется, почему вы получаете следующее:
print(re.findall(r'(@|#)\w+', str))
# ['@', '#', '@', '#']
Теперь шаблон имеет только одну группу, а findall
возвращает список совпадений для этой группы.
Напротив, шаблоны, приведенные выше в качестве решений, не имеют групп захвата, поэтому они работают в соответствии с вашими ожиданиями:
print(re.findall(r'(?:@|#)\w+', str))
# ['@tweet', '#this', '@foo', '#bar']
print(re.findall(r'[@#]\w+', str))
# ['@tweet', '#this', '@foo', '#bar']
Ссылки
Вложения