domingo, 9 de maio de 2021

Strings em python 3

❝Estou dizendo isso porque você é um dos meus amigos.
Meu alfabeto começa onde seu alfabeto termina!❞
- Dr. Seuss, On Beyond Zebra!

Algumas coisas chatas que você precisa entender antes de mergulhar

Poucas pessoas pensam nisso, mas o texto é incrivelmente complicado. Comece com o alfabeto. O povo de Bougainville possui o menor alfabeto do mundo; seu alfabeto Rotokas é composto de apenas 12 letras: A, E, G, I, K, O, P, R, S, T, U e V. Na outra extremidade do espectro, idiomas como chinês, japonês e O coreano tem milhares de caracteres. O inglês, é claro, tem 26 letras - 52 se você contar maiúsculas e minúsculas separadamente - mais um punhado de ! @ # $% & Sinais de pontuação.

Quando você fala sobre “texto”, provavelmente está pensando em “caracteres e símbolos na tela do meu computador”. Mas os computadores não lidam com caracteres e símbolos; eles lidam com bits e bytes. Cada pedaço de texto que você já viu na tela do computador é, na verdade, armazenado em uma codificação de caracteres específica. A grosso modo, a codificação de caracteres fornece um mapeamento entre o que você vê na tela e o que seu computador realmente armazena na memória e no disco. Existem muitas codificações de caracteres diferentes, algumas otimizadas para idiomas específicos, como russo, chinês ou inglês, e outras que podem ser usadas para vários idiomas.

Na verdade, é mais complicado do que isso. Muitos caracteres são comuns a várias codificações, mas cada codificação pode usar uma sequência diferente de bytes para realmente armazenar esses caracteres na memória ou no disco. Portanto, você pode pensar na codificação de caracteres como uma espécie de chave de descriptografia. Sempre que alguém lhe dá uma sequência de bytes - um arquivo, uma página da web, qualquer coisa - e afirma que é um “texto”, você precisa saber qual codificação de caracteres eles usaram para poder decodificar os bytes em caracteres. Se eles derem a chave errada ou nenhuma chave, você terá a tarefa nada invejável de decifrar o código sozinho. Provavelmente, você errará e o resultado será um jargão.

Tudo o que você pensava que sabia sobre strings está errado.

Certamente você já viu páginas da web como esta, com estranhos caracteres de ponto de interrogação onde deveriam estar apóstrofos. Isso geralmente significa que o autor da página não declarou sua codificação de caracteres corretamente, seu navegador ficou adivinhando e o resultado foi uma mistura de caracteres esperados e inesperados. Em inglês, é simplesmente irritante; em outros idiomas, o resultado pode ser completamente ilegível.

Existem codificações de caracteres para cada idioma principal do mundo. Como cada idioma é diferente e a memória e o espaço em disco são historicamente caros, cada codificação de caractere é otimizada para um idioma específico. Com isso, quero dizer que cada codificação usa os mesmos números (0–255) para representar os caracteres desse idioma. Por exemplo, você provavelmente está familiarizado com a codificação ASCII, que armazena caracteres em inglês como números que variam de 0 a 127. (65 é "A" maiúsculo, 97 é "a" minúsculo. O inglês tem um alfabeto muito simples, para que possa ser totalmente expresso em menos de 128 números. Para aqueles que podem contar na base 2, são 7 dos 8 bits em um byte.

Os idiomas da Europa Ocidental, como francês, espanhol e alemão, têm mais letras do que o inglês. Ou, mais precisamente, eles têm letras combinadas com vários sinais diacríticos, como o caractere ñ em espanhol. A codificação mais comum para esses idiomas é CP-1252, também chamada de “windows-1252” porque é amplamente usada no Microsoft Windows. A codificação CP-1252 compartilha caracteres com ASCII no intervalo 0-127, mas depois se estende para o intervalo 128-255 para caracteres como n-com-um-til sobre ele (241), u-com-dois-pontos sobre ele (252). No entanto, ainda é uma codificação de byte único; o maior número possível, 255, ainda cabe em um byte.

Depois, há idiomas como chinês, japonês e coreano, que têm tantos caracteres que exigem conjuntos de caracteres de bytes múltiplos. Ou seja, cada “caractere” é representado por um número de dois bytes de 0–65535. Mas diferentes codificações multibyte ainda compartilham o mesmo problema que diferentes codificações de um único byte, ou seja, que cada uma usa os mesmos números para significar coisas diferentes. Acontece que a gama de números é mais ampla, porque há muito mais caracteres para representar.

Isso era normal em um mundo sem rede, onde “texto” era algo que você digitava e ocasionalmente imprimia. Não havia muito “texto simples”. O código-fonte foi ASCII, e todos os outros utilizados processadores de texto, que definiram seus próprios formatos (não-texto) que seguiram informações codificação de caracteres, juntamente com um estilo rico. As pessoas liam esses documentos com o mesmo processador de texto do autor original, então tudo funcionava, mais ou menos.

Agora pense no surgimento de redes globais como e-mail e web. Muito “texto simples” voando ao redor do globo, sendo escrito em um computador, transmitido por um segundo computador e recebido e exibido por um terceiro computador. Os computadores só podem ver números, mas os números podem significar coisas diferentes. Ah não! O que fazer? Bem, os sistemas tiveram que ser projetados para transportar informações de codificação junto com cada pedaço de "texto simples". Lembre-se de que é a chave de descriptografia que mapeia números legíveis por computador em caracteres legíveis por humanos. Uma chave de descriptografia ausente significa texto truncado, jargão ou pior.

Agora pense em tentar armazenar vários trechos de texto no mesmo lugar, como na mesma tabela de banco de dados que contém todos os e-mails que você já recebeu. Você ainda precisa armazenar a codificação de caracteres ao lado de cada pedaço de texto para que possa exibi-lo corretamente. Acha que é difícil? Tente pesquisar em seu banco de dados de e-mail, o que significa converter entre várias codificações instantaneamente. Não parece divertido?

Agora pense na possibilidade de documentos multilíngues, onde caracteres de vários idiomas estão próximos uns dos outros no mesmo documento. (Dica: os programas que tentavam fazer isso normalmente usavam códigos de escape para alternar os "modos". Puf, você está no modo koi8-r russo, então 241 significa Я; puf, agora você está no modo grego Mac, então 241 significa ώ.) E, claro, você também desejará pesquisar esses documentos.

Agora chore muito, porque tudo que você pensava que sabia sobre strings está errado, e não existe "texto simples".

Unicode

Digite Unicode.

Unicode é um sistema projetado para representar todos os caracteres de todos os idiomas. Unicode representa cada letra, caractere ou ideograma como um número de 4 bytes. Cada número representa um caractere único usado em pelo menos um dos idiomas do mundo. (Nem todos os números são usados, mas mais de 65535 deles, portanto, 2 bytes não seriam suficientes.) Os caracteres usados em vários idiomas geralmente têm o mesmo número, a menos que haja uma boa razão etimológica para não o fazer. Independentemente disso, há exatamente 1 número por caractere e exatamente 1 caractere por número. Cada número sempre significa apenas uma coisa; não há “modos” para acompanhar. U+0041 é sempre 'A', mesmo que seu idioma não contenha um 'A'.

Diante disso, parece uma ótima ideia. Uma codificação para governar todos eles. Vários idiomas por documento. Não há mais "troca de modo" para alternar entre as codificações no meio do fluxo. Mas, de imediato, a pergunta óbvia deve saltar para você. Quatro bytes? Para cada caractere Isso parece muito desperdício, especialmente para idiomas como Inglês e Espanhol, que precisam de menos de um byte (256 números) para expressar cada caractere possível. Na verdade, é um desperdício até mesmo para idiomas baseados em ideogramas (como o chinês), que nunca precisam de mais de dois bytes por caractere.

Existe uma codificação Unicode que usa quatro bytes por caractere. É chamado de UTF-32, porque 32 bits = 4 bytes. UTF-32 é uma codificação direta; ele pega cada caractere Unicode (um número de 4 bytes) e representa o caractere com esse mesmo número. Isso tem algumas vantagens, a mais importante é que você pode encontrar o enésimo caractere de uma string em tempo constante, porque o enésimo caractere começa no 4 × enésimo byte. Ele também tem várias desvantagens, sendo a mais óbvia que são necessários quatro bytes para armazenar cada caractere estranho.

Mesmo que haja muitos caracteres Unicode, acontece que a maioria das pessoas nunca usará nada além do primeiro 65535. Portanto, existe outra codificação Unicode, chamada UTF-16 (porque 16 bits = 2 bytes). UTF-16 codifica cada caractere de 0-65535 como dois bytes, então usa alguns truques sujos se você realmente precisar representar os caracteres Unicode do "plano astral" raramente usados ​​além de 65535. Vantagem mais óbvia: UTF-16 é duas vezes mais espaço- eficiente como UTF-32, porque cada caractere requer apenas dois bytes para armazenar em vez de quatro bytes (exceto para aqueles que não o fazem). E você ainda pode encontrar facilmente o enésimo caractere de uma string em tempo constante, se assumir que a string não inclui nenhum caractere do plano astral, o que é uma boa suposição até o momento em que não é.

Mas também há desvantagens não óbvias para UTF-32 e UTF-16. Diferentes sistemas de computador armazenam bytes individuais de maneiras diferentes. Isso significa que o caractere U+4E2D pode ser armazenado em UTF-16 como 4E 2D ou 2D 4E, dependendo se o sistema é big-endian ou little-endian. (Para UTF-32, há ainda mais ordens de bytes possíveis). Contanto que seus documentos nunca saiam do computador, você está seguro - aplicativos diferentes no mesmo computador usarão a mesma ordem de bytes. Mas no minuto em que você quiser transferir documentos entre sistemas, talvez em uma rede mundial de computadores de algum tipo, precisará de uma maneira de indicar em qual ordem seus bytes são armazenados. Caso contrário, o sistema receptor não tem como saber se a sequência de dois bytes 4E 2D significa U+4E2D ou U+2D4E.

Para resolver este problema, as codificações Unicode multibyte definem uma "Marca de Ordem de Byte", que é um caractere especial não imprimível que você pode incluir no início do seu documento para indicar em que ordem seus bytes estão. Para UTF-16, a Marca de Ordem de Byte é U+FEFF. Se você receber um documento UTF-16 que começa com os bytes FF FE, saberá que a ordem dos bytes é uma maneira; se começar com FE FF, você sabe que a ordem dos bytes está invertida.

Ainda assim, o UTF-16 não é exatamente ideal, especialmente se você estiver lidando com muitos caracteres ASCII. Se você pensar bem, até mesmo uma página da web chinesa conterá muitos caracteres ASCII - todos os elementos e atributos que cercam os caracteres chineses imprimíveis. Ser capaz de encontrar o enésimo caractere em tempo constante é bom, mas ainda há o problema irritante desses caracteres do plano astral, o que significa que você não pode garantir que cada caractere tenha exatamente dois bytes, então você não pode realmente encontrar o enésimo caractere em tempo constante, a menos que você mantenha um índice separado. E cara, com certeza há muito texto ASCII no mundo...

Outras pessoas ponderaram essas questões e encontraram uma solução:

UTF-8

UTF-8 é um sistema de codificação de comprimento variável para Unicode. Ou seja, caracteres diferentes ocupam um número diferente de bytes. Para ASCII caracteres (AZ). UTF-8 usa apenas um byte por caractere. Na verdade, ele usa exatamente os mesmos bytes; os primeiros 128 caracteres (0–127) em UTF-8 são indistinguíveis de ASCII. Os caracteres “latinos estendidos” como ñ e ö acabam ocupando dois bytes. (Os bytes não são simplesmente o ponto de código Unicode como seriam em UTF-16; há algumas mudanças sérias de bits envolvidas). Caracteres chineses como 中 acabam ocupando três bytes. Os caracteres raramente usados do “plano astral” ocupam quatro bytes.

Desvantagens: como cada caractere pode ter um número diferente de bytes, encontrar o enésimo caractere é uma operação O(N) - ou seja, quanto mais longa a string, mais tempo leva para encontrar um caractere específico. Além disso, há um ajuste de bits envolvido para codificar caracteres em bytes e decodificar bytes em caracteres.

Vantagens: codificação supereficiente de caracteres ASCII comuns. Não é pior do que UTF-16 para caracteres latinos estendidos. Melhor do que UTF-32 para caracteres chineses. Além disso (e você terá que confiar em mim nisso, porque não vou mostrar a matemática), devido à natureza exata da manipulação de bits, não há problemas de ordenação de bytes. Um documento codificado em UTF-8 usa exatamente o mesmo fluxo de bytes em qualquer computador.

Mergulho

No Python 3, todas as strings são sequências de caracteres Unicode. Não existe uma string Python codificada em UTF-8, ou uma string Python codificada como CP-1252. “Esta string é UTF-8?” é uma pergunta inválida. UTF-8 é uma forma de codificar caracteres como uma sequência de bytes. Se você quiser pegar uma string e transformá-la em uma sequência de bytes em uma codificação de caracteres específica, o Python 3 pode ajudá-lo com isso. Se você quiser pegar uma sequência de bytes e transformá-la em uma string, o Python 3 pode ajudá-lo com isso também. Bytes não são caracteres; bytes são bytes. Caracteres são uma abstração. Uma string é uma sequência dessas abstrações.

>>> s = '深入 Python'    ①
>>> len(s)               ②
9
>>> s[0]                 ③
'深'
>>> s + ' 3'             ④
'深入 Python 3'
  1. Para criar uma string, coloque-a entre aspas. As strings Python podem ser definidas com aspas simples (') ou aspas duplas (").
  2. A função len() interna retorna o comprimento da string, ou seja, o número de caracteres. Esta é a mesma função que você usa para encontrar o comprimento de uma lista, tupla, conjunto ou dicionário. Uma string é como uma tupla de caracteres.
  3. Assim como obter itens individuais de uma lista, você pode obter caracteres individuais de uma string usando a notação de índice.
  4. Assim como nas listas, você pode concatenar strings usando o operador +.

Formatando Strings

As strings podem ser definidas com aspas simples ou duplas.

Vamos dar outra olhada em humansize.py:

SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],         ①
        1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.                          ②

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

    '''                                                                     ③
    if size < 0:
        raise ValueError('number must be non-negative')                     ④

    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)                       ⑤

    raise ValueError('number too large')
  1. 'KB', 'MB', 'GB'... esses são cada strings.
  2. As docstrings de função são strings. Esta docstring abrange várias linhas, portanto, ela usa três aspas em uma linha para iniciar e terminar a string.
  3. Essas três aspas em uma linha encerram a docstring.
  4. Há outra string, sendo passada para a exceção como uma mensagem de erro legível.
  5. Há um... uau, o que diabos é isso?

Python 3 oferece suporte à formatação de valores em strings. Embora isso possa incluir expressões muito complicadas, o uso mais básico é inserir um valor em uma string com um único espaço reservado.

>>> username = 'mark'
>>> password = 'PapayaWhip'                             ①
>>> "{0}'s password is {1}".format(username, password)  ②
"mark's password is PapayaWhip"
  1. Não, minha senha não é realmente PapayaWhip.
  2. Há muita coisa acontecendo aqui. Primeiro, essa é uma chamada de método em um literal de string. Strings são objetos e objetos têm métodos. Em segundo lugar, toda a expressão é avaliada como uma string. Terceiro, {0} e {1} são campos de substituição, que são substituídos pelos argumentos passados ​​para o método format().

Nomes de campos compostos

O exemplo anterior mostra o caso mais simples, onde os campos de substituição são simplesmente inteiros. Os campos de substituição de inteiros são tratados como índices posicionais na lista de argumentos do método format(). Isso significa que {0} é substituído pelo primeiro argumento (username, neste caso), {1} é substituído pelo segundo argumento (password). Você pode ter tantos índices posicionais quantos argumentos e quantos argumentos quiser. Mas os campos de substituição são muito mais poderosos do que isso.

>>> import humansize
>>> si_suffixes = humansize.SUFFIXES[1000]      ①
>>> si_suffixes
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
>>> '1000{0[0]} = 1{0[1]}'.format(si_suffixes)  ②
'1000KB = 1MB'
  1. Em vez de chamar qualquer função no módulo humansize, você está apenas pegando uma das estruturas de dados que ela define: a lista de sufixos “SI” (potências de 1000).
  2. Parece complicado, mas não é. {0} faria referência ao primeiro argumento passado ao método format(), si_suffixes. Mas si_suffixes é uma lista. Assim, {0[0]} refere-se ao primeiro item da lista que é o primeiro argumento passado para o método format(): 'KB'. Enquanto isso, {0[1]} refere-se ao segundo item da mesma lista: 'MB'. Tudo fora das chaves - incluindo 1000, o sinal de igual e os espaços - permanece intocado. O resultado final é a string '1000KB = 1MB'.

{0} é substituído pelo primeiro argumento de format(). {1} é substituído pelo 2º.

O que este exemplo mostra é que os especificadores de formato podem acessar itens e propriedades de estruturas de dados usando (quase) a sintaxe Python . Isso é chamado de nomes de campos compostos . Os seguintes nomes de campos compostos “simplesmente funcionam”:

  • Passar uma lista e acessar um item da lista por índice (como no exemplo anterior).
  • Passando um dicionário e acessando um valor do dicionário por chave.
  • Passar um módulo e acessar suas variáveis e funções por nome.
  • Passar uma instância de classe e acessar suas propriedades e métodos por nome.
  • Qualquer combinação das opções acima

Só para te impressionar, aqui está um exemplo que combina todas as opções acima:

>>> import humansize
>>> import sys
>>> '1MB = 1000{0.modules[humansize].SUFFIXES[1000][0]}'.format(sys)
'1MB = 1000KB'

Funciona assim:

  • O módulo sys contém informações sobre a instância do Python em execução no momento. Como você acabou de importá-lo, pode passar o próprio módulo sys como um argumento para o método format(). Portanto, o campo de substituição {0} refere-se ao módulo sys.
  • sys.modules é um dicionário de todos os módulos que foram importados nesta instância Python. As chaves são os nomes dos módulos como strings; os valores são os próprios objetos do módulo. Portanto, o campo de substituição {0.modules} refere-se ao dicionário de módulos importados.
  • sys.modules['humansize'] é o módulo humansize que você acabou de importar. O campo de substituição {0.modules[humansize]} refere-se ao módulo humansize. Observe a ligeira diferença de sintaxe aqui. No código Python real, as chaves do dicionário sys.modules são strings; para se referir a eles, você precisa colocar aspas ao redor do nome do módulo (por exemplo 'humansize'). Mas dentro de um campo de substituição, você pula as aspas em torno do nome da chave do dicionário (por exemplo humansize). Para citar o PEP 3101: Advanced String Formatting, “As regras para analisar uma chave de item são muito simples. Se começar com um dígito, é tratado como um número, caso contrário, é usado como uma string”.
  • sys.modules['humansize'].SUFFIXES é o dicionário definido na parte superior do módulo humansize. O campo de substituição {0.modules[humansize].SUFFIXES} refere-se a esse dicionário.
  • sys.modules['humansize'].SUFFIXES[1000] é uma lista de sufixos SI: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']. Portanto, o campo de substituição {0.modules[humansize].SUFFIXES[1000]} se refere a essa lista.
  • sys.modules['humansize'].SUFFIXES[1000][0] é o primeiro item da lista de sufixos SI: 'KB'. Portanto, o campo de substituição completo {0.modules[humansize].SUFFIXES[1000][0]} é substituído pela sequência de dois caracteres KB.

Especificadores de formato

Mas espere! Tem mais! Vamos dar uma outra olhada nessa estranha linha de código de humansize.py:

if size < multiple:
    return '{0:.1f} {1}'.format(size, suffix)

{1} é substituído pelo segundo argumento passado ao método format(), que é o suffix. Mas o que é {0:.1f}? São duas coisas: {0} que você reconhece e :.1f que não. A segunda metade (incluindo e após os dois pontos) define o especificador de formato, que refina ainda mais como a variável substituída deve ser formatada.

Observação

Os especificadores de formato permitem que você misture o texto de substituição de várias maneiras úteis, como a função printf() em C. Você pode adicionar zero ou espaçamento, alinhar strings, controlar a precisão decimal e até mesmo converter números em hexadecimais.

Em um campo de substituição, dois pontos (:) marcam o início do especificador de formato. O especificador de formato “.1” significa “arredondar para o décimo mais próximo” (ou seja, exibir apenas um dígito após a vírgula decimal). O especificador de formato “f” significa “número de ponto fixo” (em oposição à notação exponencial ou alguma outra representação decimal). Assim, dado um size de 698.24 e suffix de 'GB', a string formatada seria '698.2 GB', porque 698.24 é arredondado para uma casa decimal, então o sufixo é anexado após o número.

>>> '{0:.1f} {1}'.format(698.24, 'GB')
'698.2 GB'

Para todos os detalhes sangrentos sobre especificadores de formato, consulte a Minilinguagem de Especificação de Formato na documentação oficial do Python.

Outros métodos de string comuns

Além da formatação, as strings podem fazer vários outros truques úteis.

>>> s = '''Finished files are the re-  ①
... sult of years of scientif-
... ic study combined with the
... experience of years.'''
>>> s.splitlines()                     ②
['Finished files are the re-',
 'sult of years of scientif-',
 'ic study combined with the',
 'experience of years.']
>>> print(s.lower())                   ③
finished files are the re-
sult of years of scientif-
ic study combined with the
experience of years.
>>> s.lower().count('f')               ④
6
  1. Você pode inserir strings de várias linhas no shell interativo do Python. Depois de iniciar uma string de várias linhas com aspas triplas, apenas pressione ENTER e o shell interativo solicitará que você continue a string. Digitar as aspas triplas de fechamento encerra a string e o próximo ENTER executará o comando (neste caso, atribuindo a string a s).
  2. O método splitlines() pega uma string de várias linhas e retorna uma lista de strings, uma para cada linha do original. Observe que os retornos de carro no final de cada linha não estão incluídos.
  3. O método lower() converte toda a string em minúsculas. (Da mesma forma, o método upper() converte uma string em maiúsculas).
  4. O método count() conta o número de ocorrências de uma substring. Sim, realmente existem seis “f”s nessa frase!

Aqui está outro caso comum. Digamos que você tenha uma lista de pares de valores-chave no formato key1=value1&key2=value2 e queira dividi-los e fazer um dicionário no formato {key1: value1, key2: value2}.

>>> query = 'user=pilgrim&database=master&password=PapayaWhip'
>>> a_list = query.split('&')                                        ①
>>> a_list
['user=pilgrim', 'database=master', 'password=PapayaWhip']
>>> a_list_of_lists = [v.split('=', 1) for v in a_list if '=' in v]  ②
>>> a_list_of_lists
[['user', 'pilgrim'], ['database', 'master'], ['password', 'PapayaWhip']]
>>> a_dict = dict(a_list_of_lists)                                   ③
>>> a_dict
{'password': 'PapayaWhip', 'user': 'pilgrim', 'database': 'master'}
  1. O método split() da string tem um argumento obrigatório, um delimitador. O método divide uma string em uma lista de strings com base no delimitador. Aqui, o delimitador é um caractere e comercial, mas pode ser qualquer coisa.
  2. Agora temos uma lista de strings, cada uma com uma chave, seguida por um sinal de igual, seguida por um valor. Podemos usar uma compreensão de lista para iterar por toda a lista e dividir cada string em duas strings com base no primeiro sinal de igual. O segundo argumento opcional para o método split() é o número de vezes que você deseja dividir. 1 significa “dividir apenas uma vez”, portanto, o método split() retornará uma lista de dois itens. (Em teoria, um valor também pode conter um sinal de igual. Se você acabou de usar 'key=value=foo'.split('='), acabará com uma lista de três itens ['key', 'value', 'foo']).
  3. Por fim, o Python pode transformar essa lista de listas em um dicionário simplesmente passando-a para a função dict().

Observação

O exemplo anterior se parece muito com a análise de parâmetros de consulta numa URL, mas a análise de URL na vida real é, na verdade, mais complicada do que isso. Se você estiver lidando com parâmetros de consulta de URL, é melhor usar a função urllib.parse.parse_qs(), que lida com alguns casos extremos não óbvios.

Cortando uma string

Depois de definir uma string, você pode obter qualquer parte dela como uma nova string. Isso é chamado de fatiar uma string. Fatiar (slicing) strings funciona exatamente da mesma forma que fatiar listas, o que faz sentido, porque strings são apenas sequências de caracteres.

>>> a_string = 'My alphabet starts where your alphabet ends.'
>>> a_string[3:11]           ①
'alphabet'
>>> a_string[3:-3]           ②
'alphabet starts where your alphabet en'
>>> a_string[0:2]            ③
'My'
>>> a_string[:18]            ④
'My alphabet starts'
>>> a_string[18:]            ⑤
' where your alphabet ends.'
  1. Você pode obter uma parte de uma string, chamada de “fatia”, especificando dois índices. O valor de retorno é uma nova string contendo todos os caracteres da string, em ordem, começando com o índice da primeira fatia.
  2. Como listas de fatiamento, você pode usar índices negativos para fatiar strings.
  3. As strings são baseadas em zero, portanto, a_string[0:2] retorna os dois primeiros itens da string, começando em a_string[0], até, mas não incluindo a_string[2].
  4. Se o índice da fatia esquerda for 0, você pode deixá-lo de fora e 0 está implícito. Então a_string[:18] é o mesmo que a_string[0:18], porque o 0 inicial está implícito.
  5. Da mesma forma, se o índice de fatia correto for o comprimento da string, você pode deixá-lo de fora. Então a_string[18:] é o mesmo que a_string[18:44], porque esta string tem 44 caracteres. Há uma simetria agradável aqui. Nesta sequência de 44 caracteres, a_string[:18] retorna os primeiros 18 caracteres e a_string[18:] retorna tudo, exceto os primeiros 18 caracteres. Na verdade, a_string[:n] sempre retornará os primeiros n caracteres e a_string[n:] retornará o resto, independentemente do comprimento da string.

Strings vs. Bytes

Bytes são bytes; os caracteres são uma abstração. Uma sequência imutável de caracteres Unicode é chamada de string. Uma sequência imutável de números entre 0 e 255 é chamada de objeto de bytes.

>>> by = b'abcd\x65'  ①
>>> by
b'abcde'
>>> type(by)          ②
<class 'bytes'>
>>> len(by)           ③
5
>>> by += b'\xff'     ④
>>> by
b'abcde\xff'
>>> len(by)           ⑤
6
>>> by[0]             ⑥
97
>>> by[0] = 102       ⑦
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment
  1. Para definir um objecto bytes, b'' utilizar a sintaxe “byte literal”. Cada byte dentro do literal de byte pode ser um caractere ASCII ou um número hexadecimal codificado de \x00 a \xff (0-255).
  2. O tipo de objeto bytes é bytes.
  3. Assim como listas e strings, você pode obter o comprimento de um objeto bytes com a função interna len().
  4. Assim como listas e strings, você pode usar o operador + para concatenar objetos bytes. O resultado é um novo objeto bytes.
  5. Concatenar um objeto bytes de 5 bytes e um objeto de 1 byte bytesfornece um objeto bytes de 6 bytes.
  6. Assim como listas e strings, você pode usar a notação de índice para obter bytes individuais em um objeto bytes. Os itens de uma string são strings; os itens de um objeto bytes são inteiros. Especificamente, números inteiros entre 0–255.
  7. Um objeto bytes é imutável; você não pode atribuir bytes individuais. Se precisar alterar bytes individuais, você pode usar o fatiamento de string e os operadores de concatenação (que funcionam da mesma forma que as strings) ou pode converter o objeto bytes em um objeto bytearray.
>>> by = b'abcd\x65'
>>> barr = bytearray(by)  ①
>>> barr
bytearray(b'abcde')
>>> len(barr)             ②
5
>>> barr[0] = 102         ③
>>> barr
bytearray(b'fbcde')
  1. Para converter um objeto bytes em um objeto mutável bytearray, use a função interna bytearray().
  2. Todos os métodos e operações que você pode fazer em um objeto bytes, você pode fazer em um objeto bytearray também.
  3. A única diferença é que, com o objeto bytearray, você pode atribuir bytes individuais usando a notação de índice. O valor atribuído deve ser um número inteiro entre 0–255.

A única coisa que você nunca pode fazer é misturar bytes e strings.

>>> by = b'd'
>>> s = 'abcde'
>>> by + s                       ①
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str
>>> s.count(by)                  ②
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'bytes' object to str implicitly
>>> s.count(by.decode('ascii'))  ③
1
  1. Você não pode concatenar bytes e strings. Eles são dois tipos de dados diferentes.
  2. Você não pode contar as ocorrências de bytes em uma string, porque não há bytes em uma string. Uma string é uma sequência de caracteres. Talvez você quisesse dizer “contar as ocorrências da string que obteria após decodificar essa sequência de bytes em uma codificação de caracteres específica”? Bem, então você precisa dizer isso explicitamente. Python 3 não converterá implicitamente bytes em strings ou strings em bytes.
  3. Por uma coincidência incrível, esta linha de código diz "conte as ocorrências da string que você obteria após decodificar esta sequência de bytes nesta codificação de caractere específica".

E aqui está o link entre strings e bytes: os objetos bytes têm um método decode() que recebe uma codificação de caracteres e retorna uma string, e as strings têm um método encode() que recebe uma codificação de caracteres e retorna um objeto bytes. No exemplo anterior, a decodificação foi relativamente direta - convertendo uma sequência de bytes na codificação ASCII em uma string de caracteres. Mas o mesmo processo funciona com qualquer codificação que suporte os caracteres da string - mesmo codificações legadas (não Unicode).

>>> a_string = '深入 Python'         ①
>>> len(a_string)
9
>>> by = a_string.encode('utf-8')    ②
>>> by
b'\xe6\xb7\xb1\xe5\x85\xa5 Python'
>>> len(by)
13
>>> by = a_string.encode('gb18030')  ③
>>> by
b'\xc9\xee\xc8\xeb Python'
>>> len(by)
11
>>> by = a_string.encode('big5')     ④
>>> by
b'\xb2`\xa4J Python'
>>> len(by)
11
>>> roundtrip = by.decode('big5')    ⑤
>>> roundtrip
'深入 Python'
>>> a_string == roundtrip
True
  1. Isso é uma string. Possui nove caracteres.
  2. Este é um objeto bytes. Possui 13 bytes. É a sequência de bytes que você obtém quando pega a_string e a codifica em UTF-8.
  3. Este é um objeto bytes. Possui 11 bytes. É a sequência de bytes que você obtém quando pega a_string e a codifica em GB18030.
  4. Este é um objeto bytes. Possui 11 bytes. É uma sequência de bytes totalmente diferente que você obtém quando pega a_string e a codifica em Big5.
  5. Isso é uma string. Possui nove caracteres. É a sequência de caracteres que você começa quando você toma by e decodificá-lo usando a codificação algoritmo Big5. É idêntico ao string original.

PostScript: codificação de caracteres do código-fonte do Python

Python 3 assume que seu código-fonte -  ou seja, cada arquivo .py - está codificado em UTF-8.

Observação

No Python 2, a codificação padrão para arquivos .py era ASCII. No Python 3, a codificação padrão é UTF-8.

Se quiser usar uma codificação diferente em seu código Python, você pode colocar uma declaração de codificação na primeira linha de cada arquivo. Esta declaração define um arquivo .py como windows-1252:

# -*- coding: windows-1252 -*-

Tecnicamente, a substituição da codificação de caracteres também pode estar na segunda linha, se a primeira linha for um comando hash-bang semelhante ao UNIX.

#!/usr/bin/python3
# -*- coding: windows-1252 -*-

Para obter mais informações, consulte PEP 263: Definindo Python Source Code Encodings.

Leitura Adicional

Em Unicode em Python:

No Unicode em geral:

Na codificação de caracteres em outros formatos:

Em strings e formatação de strings:

Esse artigo é uma tradução de um capítulo do livro "Dive Into Python 3" escrito por Mark Pilgrim. Você pode ler o livro desde o início em português clicando aqui.

Traduzido por Acervo Lima. O original pode ser acessado aqui.

Licença

0 comentários:

Postar um comentário