Python代碼閱讀(第21篇):將變量名稱轉換為蛇式命名風格


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模塊的substr.replacestr.splitstr.lowerstr.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而獲得的字符串。 如果樣式沒有找到,則不加改變地返回stringrepl可以是字符串或函數。 向后引用像是\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函數。先看下輸入參數。

strings.replace('-', ' ')將待轉換的字符串中的'-'使用' '替換。

pattern'([A-Z]+)',其中(...)表示他是一個組合,匹配括號內的正則表達式,並在匹配完成之后,組合的內容可以被獲取,並可以在之后用\number轉義序列進行再次匹配或使用,例如上個例子中的\1'([A-Z]+)'的組合表示要匹配一個或多個大寫字母,並盡可能匹配出最長的子字符串。

replr' \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'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM