underscore - snake case naming style python
¿Elegante función de Python para convertir CamelCase a snake_case? (30)
Aquí está mi solución:
def un_camel(text):
""" Converts a CamelCase name into an under_score name.
>>> un_camel(''CamelCase'')
''camel_case''
>>> un_camel(''getHTTPResponseCode'')
''get_http_response_code''
"""
result = []
pos = 0
while pos < len(text):
if text[pos].isupper():
if pos-1 > 0 and text[pos-1].islower() or pos-1 > 0 and /
pos+1 < len(text) and text[pos+1].islower():
result.append("_%s" % text[pos].lower())
else:
result.append(text[pos].lower())
else:
result.append(text[pos])
pos += 1
return "".join(result)
Es compatible con los casos de esquina discutidos en los comentarios. Por ejemplo, convertirá getHTTPResponseCode
en get_http_response_code
como debería.
Ejemplo:
>>> convert(''CamelCase'')
''camel_case''
Aquí hay algo que hice para cambiar los encabezados en un archivo delimitado por tabulaciones. Estoy omitiendo la parte donde solo edité la primera línea del archivo. Podrías adaptarlo a Python fácilmente con la biblioteca re. Esto también incluye separar números (pero mantiene los dígitos juntos). Lo hice en dos pasos porque era más fácil que decirle que no pusiera un guión bajo al comienzo de una línea o pestaña.
Paso uno ... encuentra letras mayúsculas o enteros precedidos por letras minúsculas, y precede con un guión bajo:
Buscar:
([a-z]+)([A-Z]|[0-9]+)
Reemplazo:
/1_/l/2/
Paso dos ... toma lo anterior y ejecútalo de nuevo para convertir todas las mayúsculas a minúsculas:
Buscar:
([A-Z])
Reemplazo (es barra diagonal inversa, L minúscula, barra diagonal inversa, una):
/l/1
Conciso sin expresiones regulares, pero HTTPResponseCode => httpresponse_code:
def from_camel(name):
"""
ThisIsCamelCase ==> this_is_camel_case
"""
name = name.replace("_", "")
_cas = lambda _x : [_i.isupper() for _i in _x]
seq = zip(_cas(name[1:-1]), _cas(name[2:]))
ss = [_x + 1 for _x, (_i, _j) in enumerate(seq) if (_i, _j) == (False, True)]
return "".join([ch + "_" if _x in ss else ch for _x, ch in numerate(name.lower())])
Creo que esta solución es más directa que las respuestas anteriores:
import re
def convert (camel_input):
words = re.findall(r''[A-Z]?[a-z]+|[A-Z]{2,}(?=[A-Z][a-z]|/d|/W|$)|/d+'', camel_input)
return ''_''.join(map(str.lower, words))
# Let''s test it
test_strings = [
''CamelCase'',
''camelCamelCase'',
''Camel2Camel2Case'',
''getHTTPResponseCode'',
''get200HTTPResponseCode'',
''getHTTP200ResponseCode'',
''HTTPResponseCode'',
''ResponseHTTP'',
''ResponseHTTP2'',
''Fun?!awesome'',
''Fun?!Awesome'',
''10CoolDudes'',
''20coolDudes''
]
for test_string in test_strings:
print(convert(test_string))
Qué salidas:
camel_case
camel_camel_case
camel_2_camel_2_case
get_http_response_code
get_200_http_response_code
get_http_200_response_code
http_response_code
response_http
response_http_2
fun_awesome
fun_awesome
10_cool_dudes
20_cool_dudes
La expresión regular coincide con tres patrones:
-
[AZ]?[az]+
: minúsculas consecutivas que, opcionalmente, comienzan con una letra mayúscula. -
[AZ]{2,}(?=[AZ][az]|/d|/W|$)
: dos o más letras mayúsculas consecutivas. Utiliza un lookahead para excluir la última letra mayúscula si va seguida de una letra minúscula. -
/d+
: Números consecutivos.
Al usar re.findall
obtenemos una lista de "palabras" individuales que se pueden convertir a minúsculas y unirlas con guiones bajos.
Echa un vistazo a la excelente libreta de esquemas.
https://github.com/schematics/schematics
Le permite crear estructuras de datos escritas que pueden serializar / deserializar desde Python a Javascript, por ejemplo:
class MapPrice(Model):
price_before_vat = DecimalType(serialized_name=''priceBeforeVat'')
vat_rate = DecimalType(serialized_name=''vatRate'')
vat = DecimalType()
total_price = DecimalType(serialized_name=''totalPrice'')
El uso de expresiones regulares puede ser el más corto, pero esta solución es mucho más legible:
def to_snake_case(s):
snake = "".join(["_"+c.lower() if c.isupper() else c for c in s])
return snake[1:] if snake.startswith("_") else snake
En caso de que alguien necesite transformar un archivo fuente completo, aquí hay un script que lo hará.
# Copy and paste your camel case code in the string below
camelCaseCode ="""
cv2.Matx33d ComputeZoomMatrix(const cv2.Point2d & zoomCenter, double zoomRatio)
{
auto mat = cv2.Matx33d::eye();
mat(0, 0) = zoomRatio;
mat(1, 1) = zoomRatio;
mat(0, 2) = zoomCenter.x * (1. - zoomRatio);
mat(1, 2) = zoomCenter.y * (1. - zoomRatio);
return mat;
}
"""
import re
def snake_case(name):
s1 = re.sub(''(.)([A-Z][a-z]+)'', r''/1_/2'', name)
return re.sub(''([a-z0-9])([A-Z])'', r''/1_/2'', s1).lower()
def lines(str):
return str.split("/n")
def unlines(lst):
return "/n".join(lst)
def words(str):
return str.split(" ")
def unwords(lst):
return " ".join(lst)
def map_partial(function):
return lambda values : [ function(v) for v in values]
import functools
def compose(*functions):
return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x)
snake_case_code = compose(
unlines ,
map_partial(unwords),
map_partial(map_partial(snake_case)),
map_partial(words),
lines
)
print(snake_case_code(camelCaseCode))
Estaba buscando una solución al mismo problema, excepto que necesitaba una cadena; p.ej
"CamelCamelCamelCase" -> "Camel-camel-camel-case"
Partiendo de las bonitas soluciones de dos palabras aquí, se me ocurrió lo siguiente:
"-".join(x.group(1).lower() if x.group(2) is None else x.group(1) /
for x in re.finditer("((^.[^A-Z]+)|([A-Z][^A-Z]+))", "stringToSplit"))
La mayor parte de la lógica complicada es evitar minúsculas en la primera palabra. Aquí hay una versión más simple si no te importa alterar la primera palabra:
"-".join(x.group(1).lower() for x in re.finditer("(^[^A-Z]+|[A-Z][^A-Z]+)", "stringToSplit"))
Por supuesto, puede precompilar las expresiones regulares o unirse con un guión bajo en lugar de un guión, como se explica en las otras soluciones.
Este método simple debe hacer el trabajo:
import re
def convert(name):
return re.sub(r''([A-Z]*)([A-Z][a-z]+)'', lambda x: (x.group(1) + ''_'' if x.group(1) else '''') + x.group(2) + ''_'', name).rstrip(''_'').lower()
- Buscamos letras mayúsculas que están precedidas por cualquier número de letras mayúsculas (o cero), y seguidas por cualquier número de caracteres en minúscula.
- Un guión bajo se coloca justo antes de que aparezca la última letra mayúscula encontrada en el grupo, y uno se puede colocar antes de esa letra mayúscula en caso de que esté precedido por otras letras mayúsculas.
- Si hay guiones bajos, quítalos.
- Finalmente, toda la cadena de resultados se cambia a minúsculas.
(tomado de here , ver ejemplo de trabajo en línea )
Este no es un método elegante, es una implementación de muy bajo nivel de una máquina de estado simple (máquina de estado de campo de bits), posiblemente el modo más antipónico para resolver esto, sin embargo, el módulo también implementa una máquina de estado demasiado compleja para resolver este sencillo Tarea, entonces creo que esta es una buena solución.
def splitSymbol(s):
si, ci, state = 0, 0, 0 # start_index, current_index
''''''
state bits:
0: no yields
1: lower yields
2: lower yields - 1
4: upper yields
8: digit yields
16: other yields
32 : upper sequence mark
''''''
for c in s:
if c.islower():
if state & 1:
yield s[si:ci]
si = ci
elif state & 2:
yield s[si:ci - 1]
si = ci - 1
state = 4 | 8 | 16
ci += 1
elif c.isupper():
if state & 4:
yield s[si:ci]
si = ci
if state & 32:
state = 2 | 8 | 16 | 32
else:
state = 8 | 16 | 32
ci += 1
elif c.isdigit():
if state & 8:
yield s[si:ci]
si = ci
state = 1 | 4 | 16
ci += 1
else:
if state & 16:
yield s[si:ci]
state = 0
ci += 1 # eat ci
si = ci
print('' : '', c, bin(state))
if state:
yield s[si:ci]
def camelcaseToUnderscore(s):
return ''_''.join(splitSymbol(s))
splitsymbol puede analizar todos los tipos de casos: UpperSEQUENCEInterleaved, under_score, BIG_SYMBOLS y cammelCasedMethods
Espero que sea útil
Esto es bastante minucioso:
def convert(name):
s1 = re.sub(''(.)([A-Z][a-z]+)'', r''/1_/2'', name)
return re.sub(''([a-z0-9])([A-Z])'', r''/1_/2'', s1).lower()
Funciona con todos estos (y no daña las versiones que ya han sido desbloqueadas):
>>> convert(''CamelCase'')
''camel_case''
>>> convert(''CamelCamelCase'')
''camel_camel_case''
>>> convert(''Camel2Camel2Case'')
''camel2_camel2_case''
>>> convert(''getHTTPResponseCode'')
''get_http_response_code''
>>> convert(''get2HTTPResponseCode'')
''get2_http_response_code''
>>> convert(''HTTPResponseCode'')
''http_response_code''
>>> convert(''HTTPResponseCodeXYZ'')
''http_response_code_xyz''
O si lo vas a llamar un millón de veces, puedes precompilar las expresiones regulares:
first_cap_re = re.compile(''(.)([A-Z][a-z]+)'')
all_cap_re = re.compile(''([a-z0-9])([A-Z])'')
def convert(name):
s1 = first_cap_re.sub(r''/1_/2'', name)
return all_cap_re.sub(r''/1_/2'', s1).lower()
No olvides importar el módulo de expresiones regulares.
import re
Hay una biblioteca de inflexión en el índice del paquete que puede manejar estas cosas por usted. En este caso, estarías buscando inflection.underscore()
:
>>> inflection.underscore(''CamelCase'')
''camel_case''
He tenido bastante buena suerte con este:
import re
def camelcase_to_underscore(s):
return re.sub(r''(^|[a-z])([A-Z])'',
lambda m: ''_''.join([i.lower() for i in m.groups() if i]),
s)
Obviamente, esto podría optimizarse para la velocidad un poco si lo desea.
import re
CC2US_RE = re.compile(r''(^|[a-z])([A-Z])'')
def _replace(match):
return ''_''.join([i.lower() for i in match.groups() if i])
def camelcase_to_underscores(s):
return CC2US_RE.sub(_replace, s)
Ligeramente adaptado de https://.com/users/267781/matth que usan generadores.
def uncamelize(s):
buff, l = '''', []
for ltr in s:
if ltr.isupper():
if buff:
l.append(buff)
buff = ''''
buff += ltr
l.append(buff)
return ''_''.join(l).lower()
Muy bonito RegEx propuesto en este sitio :
(?<!^)(?=[A-Z])
Si Python tiene un método de división de cadenas, debería funcionar ...
En Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
No en la biblioteca estándar, pero encontré esta secuencia de comandos que parece contener la funcionalidad que necesita.
No sé por qué todo esto es tan complicado.
para la mayoría de los casos, la expresión simple ([AZ]+)
hará el truco
>>> 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''
Para ignorar el primer personaje, simplemente agregue 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''
Si desea separar ALLCaps a all_caps y esperar números en su cadena, todavía no necesita hacer dos ejecuciones separadas solo use |
Esta expresión ((?<=[a-z0-9])[AZ]|(?!^)[AZ](?=[az]))
puede manejar casi todos los escenarios del libro
>>> 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''
Todo depende de lo que desee, así que use la solución que mejor se adapte a sus necesidades, ya que no debería ser demasiado complicada.
nJoy!
No tengo idea de por qué usar ambas llamadas .sub ()? :) No soy un experto en expresiones regulares, pero simplifiqué la función a esta, que es adecuada para mis necesidades específicas, solo necesitaba una solución para convertir camelCasedVars de la solicitud POST a vars_with_underscore:
def myFunc(...):
return re.sub(''(.)([A-Z]{1})'', r''/1_/2'', "iTriedToWriteNicely").lower()
No funciona con nombres como getHTTPResponse, porque escuché que es una mala convención de nomenclatura (debería ser como getHttpResponse, obviamente es que es mucho más fácil memorizar este formulario).
Personalmente, no estoy seguro de cómo cualquier cosa que use expresiones regulares en python pueda ser descrita como elegante. La mayoría de las respuestas aquí solo hacen trucos RE de "código de golf". La codificación elegante se supone que es fácil de entender.
def to_snake_case(not_snake_case):
final = ''''
for i in xrange(len(not_snake_case)):
item = not_snake_case[i]
if i < len(not_snake_case) - 1:
next_char_will_be_underscored = (
not_snake_case[i+1] == "_" or
not_snake_case[i+1] == " " or
not_snake_case[i+1].isupper()
)
if (item == " " or item == "_") and next_char_will_be_underscored:
continue
elif (item == " " or item == "_"):
final += "_"
elif item.isupper():
final += "_"+item.lower()
else:
final += item
if final[0] == "_":
final = final[1:]
return final
>>> to_snake_case("RegularExpressionsAreFunky")
''regular_expressions_are_funky''
>>> to_snake_case("RegularExpressionsAre Funky")
''regular_expressions_are_funky''
>>> to_snake_case("RegularExpressionsAre_Funky")
''regular_expressions_are_funky''
Prefiero evitar re si es posible:
myString="ThisStringIsCamelCase" ''''.join([''_''+i.lower() if i.isupper() else i for i in myString]).lstrip(''_'') ''this_string_is_camel_case''
Sin ninguna biblioteca:
def camelify(out):
return (''''.join(["_"+x.lower() if i<len(out)-1 and x.isupper() and out[i+1].islower()
else x.lower()+"_" if i<len(out)-1 and x.islower() and out[i+1].isupper()
else x.lower() for i,x in enumerate(list(out))])).lstrip(''_'').replace(''__'',''_'')
Un poco pesado, pero
CamelCamelCamelCase -> camel_camel_camel_case
HTTPRequest -> http_request
GetHTTPRequest -> get_http_request
getHTTPRequest -> get_http_request
Tantos métodos complicados ... Simplemente encuentre todo el grupo "Titulado" y únase a su variante de caja inferior con un guión bajo.
>>> import re
>>> def camel_to_snake(string):
... groups = re.findall(''([A-z0-9][a-z]*)'', string)
... return ''_''.join([i.lower() for i in groups])
...
>>> camel_to_snake(''ABCPingPongByTheWay2KWhereIsOurBorderlands3???'')
''a_b_c_ping_pong_by_the_way_2_k_where_is_our_borderlands_3''
Si no desea hacer números como el primer carácter del grupo o grupo separado, puede usar la máscara ([Az][a-z0-9]*)
.
Un ejemplo horrendo usando expresiones regulares (puedes limpiar esto fácilmente :)):
def f(s):
return s.group(1).lower() + "_" + s.group(2).lower()
p = re.compile("([A-Z]+[a-z]+)([A-Z]?)")
print p.sub(f, "CamelCase")
print p.sub(f, "getHTTPResponseCode")
Funciona para getHTTPResponseCode sin embargo!
Alternativamente, usando lambda:
p = re.compile("([A-Z]+[a-z]+)([A-Z]?)")
print p.sub(lambda x: x.group(1).lower() + "_" + x.group(2).lower(), "CamelCase")
print p.sub(lambda x: x.group(1).lower() + "_" + x.group(2).lower(), "getHTTPResponseCode")
EDITAR: También debería ser bastante fácil ver que hay espacio para mejoras en casos como "Prueba", porque el guión bajo se inserta incondicionalmente.
Use: str.capitalize()
para convertir la primera letra de la cadena (contenida en la variable str) en una letra mayúscula y devuelve la cadena completa.
Ejemplo: Comando: "hello" .capitalize () Salida: Hello
Wow, acabo de robar esto de fragmentos de Django. ref http://djangosnippets.org/snippets/585/
Bastante elegante
camelcase_to_underscore = lambda str: re.sub(''(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'', ''_//1'', str).lower().strip(''_'')
Ejemplo:
camelcase_to_underscore(''ThisUser'')
Devoluciones:
''this_user''
stringcase es mi biblioteca para esto; p.ej:
>>> from stringcase import pascalcase, snakecase
>>> snakecase(''FooBarBaz'')
''foo_bar_baz''
>>> pascalcase(''foo_bar_baz'')
''FooBarBaz''
Por el gusto de hacerlo:
>>> def un_camel(input):
... output = [input[0].lower()]
... for c in input[1:]:
... if c in (''ABCDEFGHIJKLMNOPQRSTUVWXYZ''):
... output.append(''_'')
... output.append(c.lower())
... else:
... output.append(c)
... return str.join('''', output)
...
>>> un_camel("camel_case")
''camel_case''
>>> un_camel("CamelCase")
''camel_case''
O, más por diversión.
>>> un_camel = lambda i: i[0].lower() + str.join('''', ("_" + c.lower() if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" else c for c in i[1:]))
>>> un_camel("camel_case")
''camel_case''
>>> un_camel("CamelCase")
''camel_case''
''''.join(''_''+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip(''_'')
re.sub("(.)([A-Z])", r''/1_/2'', ''DeathToCamelCase'').lower()
def convert(camel_str):
temp_list = []
for letter in camel_str:
if letter.islower():
temp_list.append(letter)
else:
temp_list.append(''_'')
temp_list.append(letter)
result = "".join(temp_list)
return result.lower()
def convert(name):
return reduce(
lambda x, y: x + (''_'' if y.isupper() else '''') + y,
name
).lower()
Y si necesitamos cubrir un caso con una entrada que ya no está disponible:
def convert(name):
return reduce(
lambda x, y: x + (''_'' if y.isupper() and not x.endswith(''_'') else '''') + y,
name
).lower()