Impresora de listas con formato de coma
Estoy trabajando en un proyecto de práctica de lista del libro "Automatizar las cosas aburridas con Python" que pide esto:
Escriba una función que tome un valor de lista como argumento y devuelva una cadena con todos los elementos separados por una coma y un espacio, con e insertado antes del último elemento. Por ejemplo, pasar la lista de correo no deseado anterior a la función devolvería 'manzanas, plátanos, tofu y gatos'. Pero su función debería poder trabajar con cualquier valor de lista que se le pase. Asegúrese de probar el caso en el que se pasa una lista vacía [] a su función.
Hasta ahora se me ocurrió esto:
def comma_code(iterable):
'''
Function that loops through every value in a list and prints it with a comma after it,
except for the last item, for which it adds an "and " at the beginning of it.
Each item is str() formatted in output to avoid concatenation issues.
'''
for i, item in enumerate(iterable):
if i == len(iterable)-1 and len(iterable) != 1: # Detect if the item is the last on the list and the list doesn't contain only 1 item (BONUS)
print('and ' + str(iterable[-1])) # Add 'and ' to the beginning
elif len(iterable) == 1: # BONUS: If list contains only 1 item,
print('Just ' + str(iterable[-1])) # replace 'and ' with 'Just '
else: # For all items other than the last one
print(str(iterable[i]) + ',', end=" ") # Add comma to the end and omit line break in print
Hay muchos comentarios porque soy bastante nuevo y estoy tratando de dejar todo lo más claro posible para mi yo futuro.
Ahora me pregunto si hay una mejor manera de hacer esto y también (pregunta subjetiva) si hay algo en mi código que debería cambiar para una mejor legibilidad y / o estilo. Como dije, soy bastante nuevo y me gustaría elegir buenas prácticas de codificación desde el principio.
Estas son un par de listas que ejecuté en la función:
spam = ['apples', 'bananas', 'tofu', 'cats']
bacon = [3.14, 'cat', 11, 'cat', True]
enty = [1]
Y esta es la salida de trabajo:
apples, bananas, tofu, and cats
3.14, cat, 11, cat, and True
Just 1
Gracias por adelantado.
Respuestas
Un enfoque alternativo
- La rutina es la misma para todos los tipos de entradas, excepto cuando el
len(list) == 1. Es por eso que podemos usar una función generadora para simplificar la rutina, y luego unaifdeclaración simple para trabajar con la excepción
def comma_code(iterable):
result = ', '.join(str(value) for value in iterable[:-1])
return f"{result}, and {iterable[-1]}"
Ahora, dado que tenemos que tratar con un caso especial, una sentencia if puede funcionar
def comma_code(iterable):
if not iterable: return None
if len(iterable) == 1: return f"Just {iterable[0]}"
result = ', '.join(str(value) for value in iterable[:-1])
return f"{result}, and {iterable[-1]}"
La pregunta no ha indicado qué hacer si el tamaño del contenedor es 0, por lo tanto, acabo de regresar None. Como lo indicó @Stef en los comentarios, también puede devolver una cadena vacía que es solo ''.
Preferiría, Noneya que es fácil rastrear si ocurre un problema.
Además, también puede diseñar una función simple que verifique si el contenedor está vacío. Si es así, la función puede imprimir una advertencia personalizada que puede ayudarlo a depurar su código
Explicación
result = ', '.join(str(value) for value in iterable[:-1])
Esto se llama una función de generador , básicamente estamos añadiendo str(value) + ', 'por cada valueen iterable.
iterable[:-1]de modo que solo iteramos hasta el penúltimo elemento.
para ser completamente detallado, :-1dice todo iterablehasta el último elemento
Bien hecho
Primero me gusta tu actitud "para el futuro yo". Mantenga esa práctica de codificación limpia. Realmente ayudará.
Manipule los estuches laterales a un lado
Extraería la parte de unión (",", "y") y particularmente el caso único ("Just"):
def print_joined(iterable):
# in unlikely case of nothing (fail-fast)
if len(iterable) == 0:
# raise an error or print sorry instead of returning silently
return
# special intro if single
elif len(iterable) == 1:
print('Just ' + str(iterable[0]))
return
## multiples concatenated
for i, item in enumerate(iterable):
join = ', ' # default separator
## multiples concatenated
joined_items = ''
for i, item in enumerate(iterable):
junction = ', ' # default separator
if i == len(iterable)-2:
junction = ' and ' # special separator before last
elif i == len(iterable)-1:
junction = '' # last item ends without separator
joined_items += str(item) + junction # you defined item on loop, so use it
print(joined_items)
Esta alternativa solo aclara los diferentes casos. Aunque es un poco detallado y no muy pitónico (sin generador, sin formato de cadena basado en plantillas), se centra en la robustez .
Para darle al método extenso la estructura del cuerpo y separar las diferentes partes para facilitar la lectura , inserté líneas vacías (espaciado vertical): preferencia individual de estilo.
Todos los casos de borde se detectan al principio para volver / fallar rápidamente (evitar el bucle obsoleto). Esta es la mejor práctica cuando se trata de validación.
El propósito principal se puede leer fácilmente desde la última línea (la línea de retorno): imprimir elementos unidos. Las comodidades laterales como las cadenas de unión basadas en cajas se ponen literalmente como tope . El refinamiento se puede introducir en iteraciones posteriores ( extensibilidad ), según su experiencia, por ejemplo:
- formato / impresión mejorados (utilizando las funciones de conveniencia de Python)
- devolver una cadena (haciendo que la función sea un formateador, independiente de la interfaz de salida)
- parametrizar cadenas de unión (para personalización / localización en lugar de codificadas)
Usabilidad: gramática para usar la coma antes anden las listas
Me pregunté sobre la coma antes ande investigué. El fenómeno llamado coma serial o coma de Oxford , se describe en la publicación del blog Grammarly Comma Before And . Básicamente dice:
- Por lo general, pones una coma antes y cuando conecta dos cláusulas independientes.
- Casi siempre es opcional poner una coma antes y en una lista.
- No debe usar una coma antes y si solo menciona dos cualidades.
Como supongo que la función debería enumerar elementos puros (nombres, sustantivos, verbos), no cláusulas, implementé una simple and(sin coma serial ) para manejar también 2 elementos gramaticalmente correctamente.
Por lo tanto, siempre que las diferentes uniones no se puedan parametrizar todavía, debe aclarar (implícitamente) las reglas de unión aplicadas en un comentario (doc-string).