Elegant Python function to convert CamelCase to snake_case?
Camel case to snake case
import rename = 'CamelCaseName'name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()print(name) # camel_case_name
If you do this many times and the above is slow, compile the regex beforehand:
pattern = re.compile(r'(?<!^)(?=[A-Z])')name = pattern.sub('_', name).lower()
To handle more advanced cases specially (this is not reversible anymore):
def camel_to_snake(name): name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()print(camel_to_snake('camel2_camel2_case')) # camel2_camel2_caseprint(camel_to_snake('getHTTPResponseCode')) # get_http_response_codeprint(camel_to_snake('HTTPResponseCodeXYZ')) # http_response_code_xyz
To add also cases with two underscores or more:
def to_snake_case(name): name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) name = re.sub('__([A-Z])', r'_\1', name) name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name) return name.lower()
Snake case to camel case
name = 'snake_case_name'name = ''.join(word.title() for word in name.split('_'))print(name) # SnakeCaseName
There's an inflection library in the package index that can handle these things for you. In this case, you'd be looking for inflection.underscore()
:
>>> inflection.underscore('CamelCase')'camel_case'
I don't know why these are all so complicating.
for most cases, the simple expression ([A-Z]+)
will do the trick
>>> re.sub('([A-Z]+)', r'_\1','CamelCase').lower()'_camel_case' >>> re.sub('([A-Z]+)', r'_\1','camelCase').lower()'camel_case'>>> re.sub('([A-Z]+)', r'_\1','camel2Case2').lower()'camel2_case2'>>> re.sub('([A-Z]+)', r'_\1','camelCamelCase').lower()'camel_camel_case'>>> re.sub('([A-Z]+)', r'_\1','getHTTPResponseCode').lower()'get_httpresponse_code'
To ignore the first character simply add look behind (?!^)
>>> re.sub('(?!^)([A-Z]+)', r'_\1','CamelCase').lower()'camel_case'>>> re.sub('(?!^)([A-Z]+)', r'_\1','CamelCamelCase').lower()'camel_camel_case'>>> re.sub('(?!^)([A-Z]+)', r'_\1','Camel2Camel2Case').lower()'camel2_camel2_case'>>> re.sub('(?!^)([A-Z]+)', r'_\1','getHTTPResponseCode').lower()'get_httpresponse_code'
If you want to separate ALLCaps to all_caps and expect numbers in your string you still don't need to do two separate runs just use |
This expression ((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))
can handle just about every scenario in the book
>>> a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')>>> a.sub(r'_\1', 'getHTTPResponseCode').lower()'get_http_response_code'>>> a.sub(r'_\1', 'get2HTTPResponseCode').lower()'get2_http_response_code'>>> a.sub(r'_\1', 'get2HTTPResponse123Code').lower()'get2_http_response123_code'>>> a.sub(r'_\1', 'HTTPResponseCode').lower()'http_response_code'>>> a.sub(r'_\1', 'HTTPResponseCodeXYZ').lower()'http_response_code_xyz'
It all depends on what you want so use the solution that best suits your needs as it should not be overly complicated.
nJoy!