Elegant Python function to convert CamelCase to snake_case? Elegant Python function to convert CamelCase to snake_case? python python

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!