Python – Un poco de orden, por favor

Objetivo: mostrar cómo ordenar los elementos de una lista.

Benditas sean las listas. ¡Qué sería de nosotros, pythonistas, sin su admirable flexibilidad! He perdido la cuenta de los problemas que he podido resolver con elegancia apoyándome simplemente en estas sólidas estructuras.

Vamos a ampliar el repertorio de las posibilidades que nos ofrecen las listas. Nos ocuparemos hoy de ordenarlas.

Tomemos, por ejemplo, la siguiente lista numérica:

>>> a = [7, -1, 5, 3]

Ordenarla resulta tan sencillo como aplicar el método sort(), disponible en los objetos de tipo lista:

>>> a.sort()
>>> a
[-1, 3, 5, 7]

Bien simple. No obstante hay algo que debes tener siempre presente: esta modificación es «in situ», atacando directamente al objeto original, sin crear una copia.

Si lo que quieres es generar una nueva lista ordenada, pero sin afectar la original, puedes hacer uso de la función sorted(), que toma como argumento una secuencia y devuelve otra diferente ordenada.

>>> a = [7, -1, 5, 3]
>>> b = sorted(a)
>>> a
[7, -1, 5, 3]
>>> b
[-1, 3, 5, 7]

Mucho cuidado con hacer esto:

>>> a = [7, -1, 5, 3]
>>> b = a.sort()

Naturalmente, a se modificaría, ordenándose. ¿Pero qué crees que valdría b?

>>> print(b)
None

Nada. b no vale nada.

Si no entiendes por qué sucede esto, no te pierdas la lectura del artículo Python – Hace falta valor, en el que se desentraña el misterio.

Lo bueno de la función sorted() es que el argumento puede ser una secuencia en general, no solamente una lista. Así, objetos no mutables, como las tuplas, que no disponen del método sort(), podrían beneficiarse de su utilización.

La única limitación de sort() y sorted() es que los elementos han de ser comparables. Si no, Python difícilmente podrá deducir su orden.

La siguiente lista, con tipos diferentes, no podrá ser ordenada por sort():

>>> listamixta = [1, 'a', 5, 'casa']
>>> listamixta.sort()
Traceback (most recent call last):
  File "<pyshell#83>", line 1, in <module>
    listamixta.sort()
TypeError: unorderable types: str() < int()

Ordenar una lista en sentido inverso, de mayor a menor, es igualmente fácil haciendo uso de un argumento opcional, reverse:

>>> a = [7, -1, 5, 3]
>>> a.sort(reverse = True)
>>> a
[7, 5, 3, -1]

La función sorted() dispone también de la misma posibilidad:

>>> a = [7, -1, 5, 3]
>>> sorted(a, reverse = True)
[7, 5, 3, -1]

Observa que, en este último ejemplo, a no ha modificado su valor, puesto que sorted() ha creado un objeto diferente:

>>> a
[7, -1, 5, 3]

Voy a plantearte ahora un problema interesante que servirá de preludio al argumento que presentaremos a continuación y que aumentará sobremanera la potencia de nuestras ordenaciones.

Imagina que queremos ordenar alfabéticamente la siguiente lista:

>>> frutas = ['pera', 'Manzana', 'fresa']

Observa que he comenzado en mayúsculas la palabra Manzana.

El método sort(), estrictamente, cumple su función:

>>> frutas.sort()
>>> frutas
['Manzana', 'fresa', 'pera']

Las letras mayúsculas se almacenan internamente con un código más bajo que las correspondientes minúsculas, de modo que Manzana aparece antes que fresa, pese a que tal vez no fuera eso lo que nos gustaría.

¿Cómo hacer para que la ordenación no tenga en cuenta que la M está en mayúsculas y la trate como si fuera minúscula, pero dejando que aparezca en el resultado tal como fue escrita?

Imagina que podemos crear un tratamiento temporal previo que procese cada término conviertiéndolo completamente en minúsculas para que luego sort trabaje sobre ese resultado, pero sin olvidar cuáles eran los términos originales.

Esto se logra con un nuevo argumento, key:

>>> frutas = ['pera', 'Manzana', 'fresa']
>>> frutas.sort(key = str.lower)
>>> frutas
['fresa', 'Manzana', 'pera']

Y ahora sí, fresa aparece antes que Manzana.

Presta atención: key recibe como valor el nombre de una función que requiera un único argumento. El valor devuelto por la función será utilizado después como base de trabajo para la ordenación.

El método lower(), que se aplica a los objetos de tipo str, strings, convierte una cadena de caracteres toda en minúsculas. Observa que he dicho el nombre de una función, por eso lower se muestra sin paréntesis en el argumento key.

Otro ejemplo. Reorganicemos nuestra macedonia, pero esta vez de modo que las frutas aparezcan ordenadas de acuerdo a su longitud:

>>> frutas = ['pera', 'Manzana', 'fresa']
>>> frutas.sort(key = len)
>>> frutas
['pera', 'fresa', 'Manzana']

Como sabes, la función len devuelve el número de elementos de una secuencia, esto es, el número de letras de que se compone un string, en nuestro caso. Ese total será tomado como criterio para la ordenación.

Podemos utilizar key para saltarnos la limitación que nos impedía ordenar listas mixtas.

>>> listamixta = [1, 'a', 5, 'casa']
>>> listamixta.sort(key = str)
>>> listamixta
[1, 5, 'a', 'casa']

La función str convierte un objeto en string, de modo que los números ya serán comparables con el resto de valores y sort() podrá realizar su trabajo. Observa que la lista resultado sigue siendo mixta; la conversión a string sólo se ha realizado a nivel interno.

Podemos ir más allá y crear incluso nuestras propias funciones para utilizarlas en sort().

Por ejemplo, esta sencilla función invierte un string:

>>> def invertir(cadena):
  return cadena[::-1]

>>> invertir('pimiento')
'otneimip'

Si no entiendes cómo trabaja esta función, echa un vistazo al artículo El mundo al revés.

Vamos a aprovecharla entonces como criterio de ordenación para que sort() ordene la lista atendiendo a la última letra de cada palabra, en lugar de la primera, tal como haría por omisión.

>>> planetas = ['mercurio', 'venus', 'tierra', 'marte']
>>> planetas.sort(key = invertir)
>>> planetas
['tierra', 'marte', 'mercurio', 'venus']

Fíjate por dónde, nuestro planeta el primero…

La función sorted(), como cabría esperar, dispone también del argumento key.

Interesante. Las posibilidades creativas son inmensas. Prácticamente significa que puedes hacer que Python ordene una lista o, por lo general, cualquier secuencia, por cualquier criterio que puedas imaginar. Es lo que me encanta de Python: este lenguaje rezuma creatividad lo mires por donde lo mires.

Javier Montero Gabarró


Python – Un poco de orden, por favor


El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)


El Club del Autodidacta


Consulta el índice completo de artículos relacionados con Python.

18 opiniones en “Python – Un poco de orden, por favor”

  1. En cada artículo tuyo sobre Python aprendo cosas sobre temas que ya creía conocer.
    Lo que tiene muchísimo potencial es el argumento key. Lo pienso tener en mente hasta que lo use.

  2. Muchas gracias por este pequeño curso de Python. No sé si ya lo habrás abandonado. En todo caso, me gustaría saber si conoces el tema de los árboles de decisión en Python. Es un tema que me gustaría aprender. Pero no hay mucha información. Este tipo de código se suele utilizar en inteligencia artificial, en juegos de ajedrez, adivinar el animal que uno esta pensando, etc…Supongo, que se deberá utilizar la programación orientada a objetos. Aunque no sé, si también se puede hacer mediante funciones. Saludos!

    1. Qué tal, Juan:
      No, no lo he abandonado ni tengo intención de hacerlo, pero hay rachas en las que el tiempo que puedo dedicar a escribir es menos del que desearía.

      La programación orientada a objetos es una opción en Python. Para mí, desde luego, la mejor, salvo que necesites un rendimiento crítico que requiera, por eficiencia, recurrir sólo a estructuras de datos nativas. Cualquier problema puede resolverse con POO o sin ella, incluso los árboles de decisión, por complejos que puedan parecer.

      No sé si conoces el algoritmo de Christopher Roach, una referencia clásica orientativa.

      No he trabajado directamente con árboles de decisión. He utilizado árboles genéricos en Python en un programa en el que tuve que diseñar mi propio parser XML.

      Toma nota de tu petición; desde luego, es un tema muy interesante.

      Saludos

      1. Muchas gracias. Pondré tu blog en favoritos para estar atento. Por cierto, sería buena idea recopilar todos los capítulos en un PDF. En todo caso, yo me estoy bajando cada tema manualmente. Y luego tocará imprimirlos en papel 🙂

        Pues eso…un tema de IA le iría perfecto a tus enseñanzas. Saludos!

        1. Tengo en proyecto ir más allá aún y crear una edición completa en LaTeX de los artículos por categoría. El único problema es que tengo demasiados proyectos, me temo, y el tiempo es tan limitado. Pero esa es la idea, pdfs reformateados del contenido del blog.
          Saludos!

  3. Hola estoy buscando la manera de ordenar un conjunto de listas (todas las listas tienen la misma estructura), por el elemento n de lista..
    ejemplo:
    lista1= [1, «casa», 545]
    lista2= [1, «casa», 345]
    (o imagina que en realidad es una lista de listas: [[1, «casa», 545],[1, «casa», 345]] )

    lo que yo busco es ordenar las listas por el elemento tercero (545 y 345),
    pero no busco alterar el orden dentro de los elementos dentro de cada lista
    sino ordenar las listas entre ellas:

    la lista2 tiene un elemento tercero inferior, así que pasaría a ser la lista1, y la lista1 pasaría a ser la lista2…
    perdona, a lo mejor es una tontería, pero es que no me termino de aclarar…
    saludos

  4. bueno, me autorespondo, pues he encontrado la solución a mi pregunta:

    dada una lista de listas, ordenar las listas dentro la lista madre por un elemento de las listas:
    resumiendo, para ordenar por el elemento 2 (0,1,2), sería algo así:

    def ordena_lista(lista):
    ordenado = sorted(lista,key=lambda it: it[2])
    return ordenado

    lista = [[3, «avion», 545],[1, «casa», 345]]

    nueva_lista = ordena_lista(lista)

    # el resultado de nueva_lista es: [[1, «casa», 345],[3, «avion», 545]]

    ya me gustaría a mí saber hacer la función sin lambda, pues todavía no lo pillo, y me gustan las funciones que yo pueda entender, pero eso es harina de otro costal…

    todo se andará, aunque se admite ayuda.jeje

    1. ¡Qué tal!
      Disculpa que no te haya escrito antes, pero hasta hoy no me he dado cuenta de la cantidad de mensajes que tenía por responder. Usualmente me llegan al correo, pero no sé qué ha pasado que lleva mucho sin avisarme.
      Prepararé un artículo que te hará tener más claros estos conceptos.
      Saludos

  5. hola buenas tardes,,,
    disculpen una pregunta…
    como hago para ordenas una lista con numero romanos…?
    alguien sabe..
    por favor alguien que me ayude…
    ejemplo:
    L = [X, V, VII, I, III, IX]
    me tendria que ordenar y dejar asi
    L = [I, III, V, VII, IX, X]

    nota: usando la funcion sort()
    les agradeceria por su ayuda muchas gracias…

    1. Pistas:
      Puedes crearte una función que convierta en primer lugar de romano a decimal y otra que haga lo contrario. Si no quieres buscar el algoritmo y los datos están acotados en un rango manejable, siempre puedes recurrir a una estructura de búsqueda que te facilite la conversión, como un diccionario con pares decimal / romano o, mucho más sencillo una lista en la que cada miembro es la representación en romano del índice de la lista, como [0, I, II, III, IV,…].
      De modo que primero conviertes a decimal, después ordenas y, finalmente, vuelves a convertir a romano.
      Suerte

  6. Todo genial con sort, pero me ordena las palabras (todas en mayúscula) de una lista así:
    Zubiría, Álvarez – o sea, la Á con tilde después de la Z
    Mujica, Méndez – cuando, según el orden alfabético debería ser: Méndez, Mujica
    El asunto es que excel y *.csv ordenan como debe ser.
    ¿Alguna idea de cómo hacer para que sort ordene alfabéticamente según el español y no según algún orden de ascii o vaya a saber de qué.
    Gracias.
    Marcela

    1. Qué tal, Marcela:

      Hay varias maneras de acometer esto. Tal vez la más intuitiva sea, primero, crearte una función de transposición, que luego emplearemos como criterio de ordenación especificado en el argumento key del método sort o la función sorted.

      La función deberá tomar como parámetro una palabra y devolverá la misma palabra pero sin caracteres acentuados. ¿Te atreves con ella?

      Imagínate que has llamado a esa función sinAcentos. Entonces, la solución a tu problema es invocar al método sort del modo siguiente:
      lista.sort(key = sinAcentos)
      y ya está, tendrás la lista ordenada correctamente.
      Recuerda que si no quieres modificar la lista original, puedes usar la función sorted() en lugar de sort:
      listaOrdenada = sorted(lista, key = sinAcentos)
      Saludos

  7. hola
    intento que al leer una lista me diga cuantas veces se puede formar la palabra “hola” pero cuando pongo “ohla” igual sale que 1 vez en vez de 0
    el enunciado dice:

    «Haga un programa en Python que lea una frase, y deberá decir cuántas veces aparece en esa frase la palabra «hola»,como subsecuencia formada a partir de sus letras en orden.

    Por ejemplo, si recibe la frase Hhoola la respuesta sería 4, porque se podría tomar la primera H con la primera O (y con la L y con la A), la primera H con la segunda O, la segunda H con la primera O, o bien la segunda H con la segunda O. Si recibes hobla, la respuesta sería 1. Si recibes ohla, la respuesta sería 0, porque tras la H no hay ninguna O que permita completar la secuencia en orden. Las minúsculas y mayúsculas cuentan como igual.»
    **mi código **

    <

    def contador2 (palabra):
    palabra1 = list (palabra)
    if isinstance (palabra1, list):
    return contador2_aux (palabra1,0,0,0,0)
    else:
    return "error"

    def contador2_aux (palabra1,contH, contO, contL, contA):
    if palabra1 == []:
    return contH * contO * contL * contA
    if palabra1[0] == "h" or palabra1[0] == "H":
    return contador2_aux (palabra1[1:], contH + 1, contO, contL, contA)
    elif palabra1[0] == "o" or palabra1[0] == "O":
    return contador2_aux (palabra1[1:], contH, contO + 1, contL, contA)
    elif palabra1[0] == "l" or palabra1[0] == "L":
    return contador2_aux (palabra1[1:], contH, contO, contL + 1, contA)
    elif palabra1[0] == "a" or palabra1[0] == "A":
    return contador2_aux (palabra1[1:], contH, contO, contL, contA + 1)
    else:
    return contador2_aux (palabra1[1:], contH, contO, contL, contA)

    espero puedan ayudarme

  8. Cordial saludo. Me parece excelente el blog. Deseo ordenar una lista de listas, por ejemplo si tengo la siguiente lista:
    a=[[‘hola’, 123], [‘Mundo’, 456], [‘Azul’, 789], [‘gato’, 987]]

    Al intentar ordenar alfabéticamente por el primer elemento, con sorted(a) o con a.sort() obtengo lo siguiente:
    [[‘Azul’, 789], [‘Mundo’, 456], [‘gato’, 987], [‘hola’, 123]]

    Cuando mi salida deseada es:
    [[‘Azul’, 789], [‘gato’, 987], [‘hola’, 123], [‘Mundo’, 456]]

    Se que Python lo organiza de esa manera porque los caracteres ASCII en mayúscula están antes de los caracteres ASCII en minúscula. Si alguien tiene alguien alguna ayuda para que organice de manera correcta sin importar mayúsculas o minúsculas, les agradecería.

    Saludos.

Deja un comentario

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies