Python 程式碼閱讀合集介紹:為什麼不推薦Python初學者直接看專案原始碼
本篇閱讀的程式碼實現將變數名稱轉換為蛇式命名風格(snake case)的功能。
本篇閱讀的程式碼片段來自於30-seconds-of-python。
snake
from re import sub
def snake(s):
return '_'.join(
sub('([A-Z][a-z]+)', r' \1',
sub('([A-Z]+)', r' \1',
s.replace('-', ' '))).split()).lower()
# EXAMPLES
snake('camelCase') # 'camel_case'
snake('some text') # 'some_text'
snake('some-mixed_string With spaces_underscores-and-hyphens') # 'some_mixed_string_with_spaces_underscores_and_hyphens'
snake('AllThe-small Things') # "all_the_small_things"
snake
函式使用正規表示式將字串變形、分解成單詞,並加上_
作為分隔符組合起來。函式主要使用了re
模組的sub
、str.replace
、str.split
、str.lower
和str.join
。在正式分析snake
函式的邏輯之前,先介紹下其中使用到的其他函式的作用。
str.replace(old, new[, count])
返回字串的副本,其中出現的所有子字串old
都將被替換為new
如果給出了可選引數count
,則只替換前count
次出現。
str.split(sep=None, maxsplit=-1)
返回一個由字串內單片語成的列表,使用sep
作為分隔字串。 如果給出了maxsplit
,則最多進行maxsplit
次拆分(因此,列表最多會有maxsplit+1
個元素)。 如果maxsplit
未指定或為-1
,則不限制拆分次數(進行所有可能的拆分)。
如果sep
未指定或為None
,則會應用另一種拆分演算法:連續的空格會被視為單個分隔符,開頭和結尾如果包含空格的話,將不會拆分出空字串。 因此,使用None
拆分空字串或僅包含空格的字串將返回 []
。
>>> '1 2 3'.split()
['1', '2', '3']
>>> '1 2 3'.split(maxsplit=1)
['1', '2 3']
>>> ' 1 2 3 '.split()
['1', '2', '3']
str.join(iterable)
返回一個由iterable
中的字串拼接而成的字串。
str.lower()
返回原字串的副本,其所有區分大小寫的字元均轉換為小寫。
re.sub(pattern, repl, string, count=0, flags=0)
返回通過使用repl
替換在string
最左邊非重疊出現的pattern
而獲得的字串。 如果樣式沒有找到,則不加改變地返回string
。repl
可以是字串或函式。 向後引用像是\6
會用樣式中第6
組所匹配到的子字串來替換。 例如下面的例子中第一組匹配到的是myfun
,所以在替換的時候,\1
使用myfun
替換,所以在結果中\npy_
後面接著的是myfun
。
帶有'r'
字首的字串是原始字串,反斜槓不必做任何特殊處理。 因此r”\n”
表示包含'\'
和'n'
兩個字元的字串,而"\n"
則表示只包含一個換行符的字串。
>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
... r'static PyObject*\npy_\1(void)\n{',
... 'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'
snake
執行邏輯
首先分析一下snake
函式最裡面的sub
函式。先看下輸入引數。
string
是s.replace('-', ' ')
將待轉換的字串中的'-'
使用' '
替換。
pattern
是'([A-Z]+)'
,其中(...)
表示他是一個組合,匹配括號內的正規表示式,並在匹配完成之後,組合的內容可以被獲取,並可以在之後用\number
轉義序列進行再次匹配或使用,例如上個例子中的\1
。'([A-Z]+)'
的組合表示要匹配一個或多個大寫字母,並儘可能匹配出最長的子字串。
repl
是r' \1'
,代表使用組合匹配出來的字串前增加一個空格,替換匹配出來的字串。例如'abcDEF'
經過匹配和替換將變成'abc DEF'
。sub('([A-Z]+)', r' \1', 'abcDEF') # 'abc DEF'
因此,snake
函式最裡面的sub
函式的輸出是將原始字串中的'-'
使用' '
替換,再匹配字串中的一個或多個連續的大些字母,在前面增加一個空格。例如原始字串是'abc-abcDEF-ABc'
經過第一個sub
函式轉換後變成'abc abc DEF ABc'
(注意'ABc'
前面有兩個空格)。
接下來再分析一下第二層的sub
函式。還是先看一下輸入引數。
string
是上個sub
的輸出,在前面的例子中,是'abc abc DEF ABc'
(注意'ABc'
前面有兩個空格)。
pattern
是'([A-Z][a-z]+)'
。它也是一個組合,表示要匹配一個大寫字母后面跟著一個或多個小寫字母的形式,並儘可能匹配出最長的子字串。
repl
還是r' \1'
,代表使用組合匹配出來的字串前增加一個空格,替換匹配出來的字串。
因此,第二層sub
的輸出是簡單的匹配一個大寫字母后面跟著一個或多個小寫字母的形式,在前面加一個空格。繼續使用前面的例子,這層的輸入字串是'abc abc DEF ABc'
(注意'ABc'
前面有兩個空格),輸出是'abc abc DEF A Bc'
(注意'A'
前面有兩個空格)。
然後snake
函式將第二層sub
輸出的字串使用str.split
函式分成字串列表。再將得到的字串列表使用'-'
作為分隔符組合起來。最後使用str.lower
將組合後的字串轉換成小寫。延續上面的例子,最終輸出的字串為:'abc_abc_def_a_bc'