Strings de Python

Python tiene una clase de string integrada llamada "str" con muchas funciones útiles (hay un módulo más antiguo llamado "string", que no debes usar). Los literales de string se pueden delimitar con comillas dobles o simples, aunque las comillas simples se usan con mayor frecuencia. Los escapes de barra inversa funcionan de la manera habitual en los literales con comillas simples y dobles, p.ej., \n \' \". Un literal de string entre comillas dobles puede contener comillas simples (p.ej., "No lo hice") y, de la misma manera, una cadena entre comillas simples puede contener comillas dobles. Un literal de string puede abarcar varias líneas, pero debe haber una barra inversa \ al final de cada línea para escapar la línea nueva. Los literales de string entre comillas triples, """ o "'', pueden abarcar varias líneas de texto.

Las cadenas de Python son "inmutables", lo que significa que no se pueden cambiar después de crearlas (las cadenas de Java también usan este estilo inmutable). Dado que las cadenas no se pueden cambiar, construimos las cadenas *nuevas* a medida que vamos a representar los valores calculados. Entonces, por ejemplo, la expresión ('hello' + 'there') toma las 2 strings 'hello' y 'there', y crea una nueva cadena 'hellothere'.

Se puede acceder a los caracteres de una cadena con la sintaxis estándar [ ] y, al igual que Java y C++, Python usa indexación basada en cero, de modo que si s es “hola”, s[1] es “e”. Si el índice está fuera de los límites para la string, Python genera un error. El estilo de Python (a diferencia de Perl) consiste en detenerse si no puede decir qué hacer, en lugar de solo inventar un valor predeterminado. La práctica sintaxis "slice" (porción) también funciona para extraer cualquier subcadena de una cadena. La función len(string) muestra la longitud de una cadena. La sintaxis [ ] y la función len() en realidad funcionan en cualquier tipo de secuencia: cadenas, listas, etc. Python intenta que sus operaciones funcionen de manera coherente en diferentes tipos. Novedad para novatos en Python: no uses "len" como nombre de variable para evitar bloquear la función len(). El operador "+" puede concatenar dos cadenas. Observa en el código que aparece a continuación que las variables no están declaradas previamente. Solo asígnalas y comienza.

  s = 'hi'
  print(s[1])          ## i
  print(len(s))        ## 2
  print(s + ' there')  ## hi there

A diferencia de Java, el signo "+" no convierte automáticamente los números ni otros tipos a la forma de cadena. La función str() convierte los valores en un formato de cadena para que puedan combinarse con otras cadenas.

  pi = 3.14
  ##text = 'The value of pi is ' + pi      ## NO, does not work
  text = 'The value of pi is '  + str(pi)  ## yes

Para los números, los operadores estándar +, /, * funcionan de la forma habitual. No hay un operador ++, pero +=, -=, etc. funciona. Si deseas una división de números enteros, utiliza 2 barras; por ejemplo, 6 // 5 es 1.

La función “print” normalmente imprime uno o más elementos de Python seguidos de una línea nueva. Un literal de string "sin procesar" tiene el prefijo "r" y pasa todos los caracteres sin tratamiento especial de barras inversas, por lo que r'x\nx' se evalúa como la string 'x\nx' de longitud-4. "print" puede tomar varios argumentos para cambiar la forma en que imprime las cosas (consulta la definición de la función de impresión de python.org), como configurar "end" como "" para dejar de imprimir una línea nueva cuando termina de imprimir todos los elementos.

  raw = r'this\t\n and that'

  # this\t\n and that
  print(raw)

  multi = """It was the best of times.
  It was the worst of times."""

  # It was the best of times.
  #   It was the worst of times.
  print(multi)

Métodos de cadena

Estos son algunos de los métodos de cadena más comunes. Un método es como una función, pero se ejecuta "en" un objeto. Si la variable s es una cadena, el código s.lower() ejecuta el métodolower() en ese objeto de string y muestra el resultado (esta idea de un método que se ejecuta en un objeto es una de las ideas básicas que conforman la programación orientada a objetos, OOP). Estos son algunos de los métodos de cadena más comunes:

  • s.lower(), s.upper(): muestra la versión de la string en minúsculas o mayúsculas.
  • s.strip(): Muestra una string sin espacios en blanco del principio y del final.
  • s.isalpha()/s.isdigit()/s.isspace()... prueba si todos los caracteres de cadena se encuentran en las diferentes clases de caracteres.
  • s.startswith('other'), s.endswith('other') -- Prueba si la cadena comienza o termina con la otra cadena
  • s.find('other'): busca la otra cadena especificada (no una expresión regular) entre s y muestra el primer índice donde comienza o -1 si no se encuentra
  • s.replace('old', 'new'): muestra una cadena en la que todas las apariciones de 'old' se reemplazaron por 'new'.
  • s.split('delim'): Muestra una lista de substrings separadas por el delimitador especificado. El delimitador no es una expresión regular, es solo texto. 'aaa,bbb,ccc'.split(',') -> ['aaa', 'bbb', 'ccc']. Como caso especial práctico, s.split() (sin argumentos) divide todos los caracteres de espacio en blanco.
  • s.join(list): opuesto a split(), une los elementos en la lista dada usando la cadena como delimitador. P.ej., '---'.join(['aaa', 'bbb', 'ccc']) -> aaa---bbb---ccc

Si buscas "python str" en Google, deberías llegar a los métodos de cadena oficiales de python.org, que incluyen todos los métodos str.

Python no tiene un tipo de carácter separado. En cambio, una expresión como s[8] muestra una string-length-1 que contiene el carácter. Con esa string-length-1, los operadores ==, <=, ... todos funcionan como se espera, por lo que, en general, no necesitas saber que Python no tiene un tipo escalar independiente de "char".

Porciones de cadenas

La sintaxis "slice" es una forma práctica de hacer referencia a las subpartes de las secuencias, que generalmente son cadenas y listas. La porción s[start:end] es los elementos que comienzan en el inicio y se extienden hasta el final, pero sin incluirlo. Supongamos que tenemos s = "Hola"

la string &quot;hello&quot; con los índices de letras 0 1 2 3 4

  • s[1:4] es 'ell': los caracteres comienzan en el índice 1 y se extienden hasta el índice 4, pero sin incluirlo.
  • s[1:] es 'ello': si se omite cualquiera de los índices, se establece de forma predeterminada el inicio o el final de la cadena.
  • s[:] es "Hola": si se omiten ambos, siempre se obtiene una copia completa (esta es la forma en Python de copiar una secuencia, como una cadena o una lista).
  • s[1:100] es "ello", un índice demasiado grande se trunca a la longitud de la cadena

Los números del índice estándar basados en cero brindan un fácil acceso a los caracteres que se encuentran cerca del comienzo de la string. Como alternativa, Python usa números negativos para facilitar el acceso a los caracteres al final de la cadena: s[-1] es el último carácter “o”, s[-2] es “l” el último carácter al último, y así sucesivamente. Los números de índice negativos cuentan desde el final de la string:

  • s[-1] es "o": el último carácter (el primero desde el final)
  • s[-4] es 'e' -- 4° desde el final
  • s[:-3] es 'Él', pero no incluye los últimos 3 caracteres.
  • s[-3:] es 'llo': comienza con el tercer carácter desde el final y se extiende hasta el final de la cadena.

Es una simple verdad de porciones que, para cualquier índice n, es s[:n] + s[n:] == s. Esto funciona incluso para n elementos negativos o fuera de los límites. En otras palabras, s[:n] y s[n:] siempre particionan la cadena en dos partes de cadena, conservando todos los caracteres. Como veremos más adelante en la sección de lista, los segmentos también funcionan con listas.

Formato de cadenas

Una de las tareas que puede realizar Python es convertir automáticamente los objetos en una cadena apta para impresión. Dos formas integradas de hacerlo son los literales de string con formato, también llamados "f-strings", y la invocación de str.format().

Literales de cadena con formato

A menudo, verás literales de cadena con formato usados en situaciones como las siguientes:

  value = 2.791514
  print(f'approximate value = {value:.2f}')  # approximate value = 2.79

  car = {'tires':4, 'doors':2}
  print(f'car = {car}') # car = {'tires': 4, 'doors': 2}

Una string literal con formato tiene el prefijo “f” (como el prefijo “r” que se usa para las strings sin procesar). Todo el texto que no esté entre llaves '{}' se imprime directamente. Las expresiones contenidas en "{}" se imprimen con la especificación de formato descrita en las especificaciones de formato. Existen muchas acciones útiles con el formato, como el truncamiento y la conversión a notación científica, y la alineación izquierda/derecha/central.

Las cadenas f son muy útiles cuando quieres imprimir una tabla de objetos y quieres que las columnas que representan diferentes atributos de objetos se alineen como

  address_book = [{'name':'N.X.', 'addr':'15 Jones St', 'bonus': 70},
      {'name':'J.P.', 'addr':'1005 5th St', 'bonus': 400},
      {'name':'A.A.', 'addr':'200001 Bdwy', 'bonus': 5},]

  for person in address_book:
    print(f'{person["name"]:8} || {person["addr"]:20} || {person["bonus"]:>5}')

  # N.X.     || 15 Jones St          ||    70
  # J.P.     || 1005 5th St          ||   400
  # A.A.     || 200001 Bdwy          ||     5

Porcentaje de cadenas

Python también tiene una herramienta antigua similar a printf() para armar una cadena. El operador % toma una string de formato de tipo printf a la izquierda (%d int, string %s, %f/%g punto flotante) y los valores coincidentes en una tupla a la derecha (una tupla consta de valores separados por comas, generalmente agrupados entre paréntesis):

  # % operator
  text = "%d little pigs come out, or I'll %s, and I'll %s, and I'll blow your %s down." % (3, 'huff', 'puff', 'house')

La línea anterior es un poco larga: supongamos que deseas dividirla en líneas separadas. No puedes dividir la línea después del '%' como lo harías en otros lenguajes, ya que, de forma predeterminada, Python trata cada línea como una instrucción independiente (en el lado más, no es necesario escribir punto y coma en cada línea). Para solucionar este problema, encierra toda la expresión en un conjunto externo de paréntesis. Luego, la expresión puede abarcar varias líneas. Esta técnica de código entre líneas funciona con las diversas construcciones de agrupación que se detallan a continuación: ( ), [ ], { }.

  # Add parentheses to make the long line work:
  text = (
    "%d little pigs come out, or I'll %s, and I'll %s, and I'll blow your %s down."
    % (3, 'huff', 'puff', 'house'))

Eso está mejor, pero la fila sigue siendo un poco larga. Python te permite cortar una línea en fragmentos, que luego concatenará automáticamente. Entonces, para hacer que esta línea sea aún más corta, podemos hacer lo siguiente:

  # Split the line into chunks, which are concatenated automatically by Python
  text = (
    "%d little pigs come out, "
    "or I'll %s, and I'll %s, "
    "and I'll blow your %s down."
    % (3, 'huff', 'puff', 'house'))

Strings (Unicode frente a bytes)

Las cadenas regulares de Python son Unicode.

Python también admite strings compuestas por bytes sin formato (denotadas por el prefijo "b" delante de un literal de string), como se muestra a continuación:

> byte_string = b'A byte string'
> byte_string
  b'A byte string'

Una string Unicode es un tipo de objeto diferente de una string de bytes, pero varias bibliotecas, como las expresiones regulares, funcionan correctamente si se pasan cualquiera de los dos tipos de string.

Para convertir una string normal de Python en bytes, llama al método encode() en la string. En sentido inverso, el método decode() de la string de bytes convierte los bytes sin formato codificados en una string de Unicode:

> ustring = 'A unicode \u018e string \xf1'
> b = ustring.encode('utf-8')
> b
b'A unicode \xc6\x8e string \xc3\xb1'  ## bytes of utf-8 encoding. Note the b-prefix.
> t = b.decode('utf-8')                ## Convert bytes back to a unicode string
> t == ustring                         ## It's the same as the original, yay!

True

En la sección de lectura de archivos, hay un ejemplo que muestra cómo abrir un archivo de texto con codificación y leer cadenas Unicode.

Sentencia if

Python no usa { } para encerrar bloques de código para if/loops/function etcétera. En cambio, Python usa dos puntos (:) y sangría/espacio en blanco para agrupar las instrucciones. La prueba booleana para un if no necesita estar entre paréntesis (gran diferencia con C++/Java) y puede tener cláusulas *elif* y *else* (mnemotécnica: la palabra "elif" tiene la misma longitud que la palabra "else").

Cualquier valor se puede usar como una prueba "if". Todos los valores “cero” cuentan como falsos: Ninguno, 0, string vacía, lista vacía, diccionario vacío. También hay un tipo booleano con dos valores: True y False (convertidos en int, son 1 y 0). Python tiene las operaciones de comparación habituales: ==, !=, <, <=, >, >=. A diferencia de Java y C, == se sobrecarga para funcionar correctamente con cadenas. Los operadores booleanos son las palabras escritas *y*, *o*, *no* (Python no usa el estilo C && || !). Así es como podría verse el código para una app de salud que brinda recomendaciones de bebidas durante el día: observa cómo cada bloque de sentencias entonces/si no comienza con un : y las instrucciones se agrupan por su sangría:

  if time_hour >= 0 and time_hour <= 24:
    print('Suggesting a drink option...')
    if mood == 'sleepy' and time_hour < 10:
      print('coffee')
    elif mood == 'thirsty' or time_hour < 2:
      print('lemonade')
    else:
      print('water')

Creo que omitir el signo de ":" es mi error de sintaxis más común cuando escribo en el tipo de código anterior, probablemente porque es algo adicional para escribir en comparación con mis hábitos de C++/Java. Además, no escribas la prueba booleana entre paréntesis, ya que ese es un hábito de C/Java. Si el código es corto, puedes colocarlo en la misma línea después de ":", de esta manera (esto se aplica también a las funciones, los bucles, etc.), aunque algunas personas creen que es más legible separar los elementos en líneas separadas.

  if time_hour < 10: print('coffee')
  else: print('water')

Ejercicio: string1.py

Para practicar el material de esta sección, realiza el ejercicio string1.py de los Ejercicios básicos.