### 無捕獲組和命名組
精心設計的 REs 也許會用很多組,既可以捕獲感興趣的子串,又可以分組和結構化 RE 本身。在復雜的 REs 里,追蹤組號變得困難。有兩個功能可以對這個問題有所幫助。它們也都使用正則表達式擴展的通用語法,因此我們來看看第一個。
Perl 5 對標準正則表達式增加了幾個附加功能,Python 的 re 模塊也支持其中的大部分。選擇一個新的單按鍵元字符或一個以 "\" 開始的特殊序列來表示新的功能,而又不會使 Perl 正則表達式與標準正則表達式產生混亂是有難度的。如果你選擇 "&" 做為新的元字符,舉個例子,老的表達式認為 "&" 是一個正常的字符,而不會在使用 \& 或 [&] 時也不會轉義。
Perl 開發人員的解決方法是使用 (?...) 來做為擴展語法。"?" 在括號后面會直接導致一個語法錯誤,因為 "?" 沒有任何字符可以重復,因此它不會產生任何兼容問題。緊隨 "?" 之后的字符指出擴展的用途,因此 (?=foo)
Python 新增了一個擴展語法到 Perl 擴展語法中。如果在問號后的第一個字符是 "P",你就可以知道它是針對 Python 的擴展。目前有兩個這樣的擴展: (?P<name>...) 定義一個命名組,(?P=name) 則是對命名組的逆向引用。如果 Perl 5 的未來版本使用不同的語法增加了相同的功能,那么 re 模塊也將改變以支持新的語法,與此同時為了兼容性的目的而繼續保持的 Python 專用語法。
現在我們看一下普通的擴展語法,我們回過頭來簡化在復雜 REs 中使用組運行的特性。因為組是從左到右編號的,而且一個復雜的表達式也許會使用許多組,它可以使跟蹤當前組號變得困難,而修改如此復雜的 RE 是十分麻煩的。在開始時插入一個新組,你可以改變它之后的每個組號。
首先,有時你想用一個組去收集正則表達式的一部分,但又對組的內容不感興趣。你可以用一個無捕獲組: (?:...) 來實現這項功能,這樣你可以在括號中發送任何其他正則表達式。
```
#!python
>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()
```
除了捕獲匹配組的內容之外,無捕獲組與捕獲組表現完全一樣;你可以在其中放置任何字符,可以用重復元字符如 "\*" 來重復它,可以在其他組(無捕獲組與捕獲組)中嵌套它。(?:...) 對于修改已有組尤其有用,因為你可以不用改變所有其他組號的情況下添加一個新組。捕獲組和無捕獲組在搜索效率方面也沒什么不同,沒有哪一個比另一個更快。
其次,更重要和強大的是命名組;與用數字指定組不同的是,它可以用名字來指定。
命令組的語法是 Python 專用擴展之一: (?P<name>...)。名字很明顯是組的名字。除了該組有個名字之外,命名組也同捕獲組是相同的。`MatchObject` 的方法處理捕獲組時接受的要么是表示組號的整數,要么是包含組名的字符串。命名組也可以是數字,所以你可以通過兩種方式來得到一個組的信息:
```
#!python
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'
```
命名組是便于使用的,因為它可以讓你使用容易記住的名字來代替不得不記住的數字。這里有一個來自 imaplib 模塊的 RE 示例:
```
#!python
InternalDate = re.compile(r'INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
r'(?P<year>[0-9][0-9][0-9][0-9])'
r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
r'"')
```
很明顯,得到 m.group('zonem') 要比記住得到組 9 要容易得多。
因為逆向引用的語法,象 (...)\1 這樣的表達式所表示的是組號,這時用組名代替組號自然會有差別。還有一個 Python 擴展:(?P=name) ,它可以使叫 name 的組內容再次在當前位置發現。正則表達式為了找到重復的單詞,(\b\w+)\s+\1 也可以被寫成 (?P<word>\b\w+)\s+(?P=word):
```
#!python
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('Paris in the the spring').group()
'the the'
```