Python: Arquivos e pastas


Quase sempre um programa de computador precisa recuperar dados ou informações previamente existentes, processá-los de algum modo e retornar um resultado que deve ficar armazenado para uso futuro. Esses dados podem ser lidos em papel impresso ou digitados pelo usuário. O resultado final também pode ser impresso ou exibido na tela. Mas, como maior frequência a informação inicial e final é lida e gravada em disco, em arquivos de texto ou binários, planilhas e bancos de dados (entre outros muitos formatos).

O objeto arquivo (file)

Abrir e ler um arquivo:

No Python existem diversas funções no módulo básico para abrir, manipular e escrever em arquivos. Um arquivo aberto é um objeto que pode ser atribuído à uma variável e, por meio dessa variável, as propriedades e métodos do objeto podem ser acessadas. A função open() estabelece uma conexão com o arquivo no disco.

obj = open('caminho_para_arquivo', modo)

Os modos são listados abaixo. Suponha, por exemplo, que existe na pasta de trabalho um arquivo com nome alunos.txt contendo informações sobre alunos de um curso. Seu conteúdo pode ser exibido da seguinte forma:

» objeto_arquivo = open('alunos.txt', 'r')
» texto = objeto_arquivo.read()
» objeto_arquivo.close()
» print(texto)

↳ id, nome     , cidade         , idade , nota
↳ 10, Pedro    , São Paulo      , 34    , 83.0
↳ 11, Maria    , São Paulo      , 23    , 59.0
↳ 12, Janaina  , Rio de Janeiro , 32    , 86.0
↳ 13, Wong     , Brasília       , 43    , 89.0
↳ 14, Roberto  , Salvador       , 38    , 98.0
↳ 15, Marco    , Curitiba       , 31    , 61.0
↳ 16, Paula    , Belo Horizonte , 34    , 44.0

O que é um arquivo no Python?Um arquivo é um conjunto de informações ou dados gravados no dispositivos de armazenamento do computador, como o disco rígido ou um ssd. Ele também pode estar armazenado em memória rom, pronto para a manipulação. O nome do arquivo é uma referência a uma tabela que informa onde estão estes dados.A função interna do Python open() é usada para abrir um arquivo. Ela retorna um objeto arquivo, também chamado de identificador ou handle, usado para ler ou modificar o arquivo. O método read() lê o arquivo e retorna para a variável texto.

Após o uso o arquivo deve ser fechado com o método close(). O fechamento do objeto libera recursos da máquina, permite que o arquivo seja acessado por outro bloco de código. Em alguns casos alterações feitas ao arquivo no programa podem não ser gravadas em disco até que eles seja fechado. Um problema técnico, como a falta de energia, pode ocasionar em perda de dados.

Estritamente dizendo o mais adequado para lidar com arquivos seria um bloco de código do seguinte tipo.

» try:
»     f = open("data.txt", "w")
»     # código com as operações necessárias usando o arquivo f
» finally:
»     f.close()

Isso evita a possibilidade de que algum erro faça com que o comando de fechamento seja pulado. Uma forma mais compacta de escrever isso, e que evita o problema citado, usa o comando with. No Python a declaração with é usada para manipular exceções e tornar o código mais enxuto e de fácil leitura.

# exemplo de código usando with
» with open('data.txt', 'r') as f:
»     # codigo usando o arquivo f
»     # (pronto!)

O fechamento nesse caso é automático. O comando with facilita a escrita do código que envolve recursos que devem ser finalizados. Essa finalização significa liberação de recursos de memória e evita que erros desnecessários sejam introduzidos.

Ler linhas de um arquivo:

Também é possível ler o arquivo com readlines() que retorna uma lista de linhas que podem ser percorridas depois.

# todas as linhas do arquivo são impressas dessa forma    
» with open('alunos.txt', 'r') as f:
»     linhas = f.readlines()
» for linha in linhas:
»     print(linha, end ='')
# as linhas acima são impressas (mas foram aqui omitidas)

# como readlines() retorna uma lista de linhas ele pode
# ser usado para contar quantas linhas existem no arquivo
» with open('alunos.txt', 'r') as f:
»     print(len(f.readlines()))
↳ 8    

Uma forma alternativa consiste em ler o arquivo uma linha de cada vez, processando as linhas na medida em que são lidas.

» with open('alunos.txt', 'r') as f:
»     print('1-', f.readline(), end='')
»     print('2-', f.readline(), end='')

↳ 1- id, nome     , cidade         , idade , nota
↳ 2- 10, Pedro    , São Paulo      , 34    , 83.0

Uma linha é o texto que termina a cada um sinal de quebra linha.

Quebras de linhas (end of line, EOL) são caracteres especiais inseridos em arquivos para indicar o final de uma linha e o início de outra. Esses caracteres, em geral, não são vistos nos editores de texto mas marcam os finais de cada linha. Alimentação de linha (line feed, LF) e retorno de carro (carriage return, CR) são usados, dependendo do sistema operacional. LF é representado por \n, 0x0A em hexadecimal ou 10 decimal. CR é representado por \r, 0x0D em hexadecimal ou 13 decimal. O Linux (e outros sistemas derivados do UNIX) usa \n. O Windows usa \r\n e o OSX usa \r.

A cada uso do método .readline() uma nova linha é recuperada, até o fim do arquivo. A operação pode ser repetida até que uma string nula seja retornada. Ao final o iterador fica vazio e deve ser lido novamente caso outra iteração seja necessária. Com essa forma de leitura as linhas são lidas uma de cada vez e a requisição de memória é menor.

» with open('alunos.txt', 'r') as f:
»     while True:
»         linha = f.readline()
»         if not linha:
»             break
»         print(linha, end='')

# Observe que readline() é o método default do objeto arquivo.
# Isso significa que o mesmo resultado pode ser obtido com as linhas

» with open('alunos.txt', 'r') as f:
»     for linha in f:
»         print(linha, end='')


Em ambos os casos o arquivo dos alunos é exibido. O teste de final de arquivo pode ser excluído porque o iterador se esgota ao final e o laço for é abandonado.

Observe que a linha if not linha: tem o mesmo efeito que if linha == '':. Ao final da iteração uma string vazia é retornada e strings vazias são avaliadas como False em testes booleanos.

Gravar em um arquivo:

No código abaixo um arquivo é aberto para gravação.

» texto = (
»     'Texto a ser gravado em disco\n'
»     'pode conter quantas linhas se desejar\n'
»     'novas linhas são inseridas com \'newline\''
» )
» arquivo = open('teste_gravar.txt', 'w')
» arquivo.write(texto)
» arquivo.close()

# a mesma operação usando with
» with open('teste_gravar.txt', 'w') as arquivo:
»     arquivo.write(texto)

Após a execução dessas linhas um arquivo teste_gravar.txt será encontrado na pasta de trabalho. No texto a ser inserido novas linhas são criadas após o sinal de newline (\n).

Gravação incremental em um arquivo:

Para acrescentar linhas ao arquivo, sem apagar as já existentes, usamos o parâmetro ‘a’ (de append):

# para acrescentar um novo aluno ao arquivo alunos.txt:
» novo_aluno = '17, Ana      , Belo Horizonte , 21    , 78.5\n'
» with open('alunos.txt', 'a') as f:
»     f.write(novo_aluno)
# a nova aluna, Ana e seus dados, fica acrescentada ao final do texto.

Observe que write() não insere quebras de linha automaticamente. Por isso terminamos a linha com o sinal de nova linha \n. Dessa forma novo texto inserido posteriormente já se inicia em linha própria.

Outra forma de iterar sobre as linhas de um arquivo consiste em tratar o objeto de arquivo o como um iterador em um laço for. O ex. mostra que readlines() retorna um iterável que pode ser percorrido dentro de laço for:

» with open('alunos.txt', 'r') as f:
»     for linha in f.readlines():
»         print(linha)
# o arquivo inteiro é exibido (mas omitido aqui)

Todo o texto lido em um arquivo dessa forma é uma string, mesmo que composto de dígitos. Se inteiros e números de ponto flutuante estão no texto e precisam ser usados como números, por ex. para um cálculo, eles devem ser convertidos usando-se as conversões int() e float(), respectivamente.

Um exemplo disso segue abaixo. Usamos o arquivo dos alunos para ler as idades e as notas, que são convertidas para inteiro e flutuante, respectivamente.

» with open('alunos.txt', 'r') as f:
»     maior, nota_media, n = 0, 0, 0
»     f.readline()
»     for linha in f:
»         palavras = linha.split(',')
»         n +=1
»         nota_media += float(palavras[4])
»         idade = int(palavras[3])
»         if idade > maior:
»             maior = idade
»             nome = palavras[1]
» print('%s é o aluno mais velho, com %d anos.' % (nome.strip(), maior))
» print('A nota média entre %d alunos é %2.1f' % (n, nota_media/n))

↳ Wong é o aluno mais velho, com 43 anos.
↳ A nota média entre 7 alunos é 74.3

O método split(',') quebra a linhas em palavras, partindo cada uma nas vígulas e retornando uma lista. palavra[3] é a idade, palavra[4] a nota. A maior idade e o nome do aluno mais velho são armazenados dentro do teste final.

Propriedades de arquivos e leitura incremental:

Para os exemplos abaixo um novo arquivo com 3 linhas é gravado.

» lista = ['Esta é a linha 1\n',
»          'Esta é a linha 2\n',
»          'Esta é a linha 3'
»         ]
» with open('novo.txt', 'w') as f:
»     f.writelines(lista)
» with open('novo.txt', 'r') as f:
»     print(f.read())

↳ Esta é a linha 1
↳ Esta é a linha 2
↳ Esta é a linha 3

Um objeto de arquivo tem diversas propriedades, entre elas file.name, o nome do arquivo, file.closed que é True se o arquivo está fechado (embora a variável continue apontando para o objeto), e file.mode que contém o modo em que ele foi aberto.

» with open('novo.txt', 'r') as f:
»     info = 'O arquivo "'+ f.name + '" está ' + ('fechado' if f.closed else 'aberto')
»     info += ' no modo %s' % f.mode
»     print(info)
» print('Agora o arquivo está ' + ('fechado' if f.closed else 'aberto'))

↳ O arquivo "novo.txt" está aberto no modo r
↳ Agora o arquivo está fechado

Leitura incremental de um arquivo:


A abertura de um arquivo com muitas linhas, ou uma linha muito longa sem quebras pode esgotar os recursos de memória de seu computador. Para evitar esse problema tanto readline ou readlines aceitam um argumento opcional estabelecendo quanto dado deve ser lido de cada vez. É possível ler um arquivo de modo incremental com arquivo.read(n) onde n é o número de bytes lidos (que resulta em um caracter simples). Como a primeira linha contém 16 caracteres (‘Esta é a linha 1’) ela é esgotada em duas iterações.

» with open('novo.txt', 'r') as f:
»     print(f.read(8))
»     print(f.read(8))

↳ Esta é a
↳  linha 1

Após as 2 leituras no código anterior o ponteiro (cursor) está posicionado sobre o byte 17. Essa posição pode ser lida e alterada, usando-se os métodos tell() e seek(). Nas linhas abaixo 17 bytes são lidos e impressos na linha 1. A linha 2 mostra a posição do ponteiro, 18. seek()

» with open('linhas.txt', 'r') as f:
»     print('1 : ', f.read(17), end='')
»     print('2 : ', f.tell())
»     f.seek(0, 0)
»     print('3 : ', f.read(17), end='')
»     f.seek(36, 0)
»     print('4 : ',  f.read(17), end='')

↳ 1 :  Esta é a linha 1
↳ 2 :  18
↳ 3 :  Esta é a linha 1
↳ 4 :  Esta é a linha 3

No exemplo seguinte a primeira linha é lida inteira e apenas 10 caracteres da segunda linha.

» with open('alunos.txt', 'r') as f:
»     txt1 = f.readline()
»     txt2 = f.readline(10)
» print(txt1, end = '')
» print(txt2)

↳ id, nome     , cidade         , idade , nota
↳ 10, Pedro    , São Paulo

Método open()

Método Descrição
open(arquivo, mode) Abre o arquivo (descrição dos parâmetros abaixo)

Os seguintes parâmetros são usados com open():

Parâmetro Descrição
arquivo caminho completo e nome do arquivo
mode Uma string que define em que modo o arquivo será aberto

O parâmetro mode pode ser:

Parâmetro Descrição
r para leitura – O ponteiro é colocado no início do arquivo. Default.
r+ para leitura e gravação. O ponteiro fica no início do arquivo.
w apenas para gravação. Substitui arquivo existente, cria novo se o arquivo não existir.
w+ escrita e leitura. Substitui arquivo existente, cria novo se arquivo não existir.
rb para leitura em formato binário. O ponteiro fica no início do arquivo.
rb+ para leitura e escrita em formato binário.
wb+ para escrita e leitura em formato binário. Substitui o arquivo existente. Cria novo se não existir.
a para anexar. Conteúdo existente fica inalterado e o ponteiro fica no final do arquivo. Cria novo se não existir.
ab um arquivo para anexar em formato binário. O ponteiro fica no final do arquivo. Cria novo se não existir.
a+ para anexar e ler. O ponteiro fica no final do arquivo, se arquivo existir. Cria novo se não existir.
ab+ anexar e ler em formato binário. O ponteiro no final do arquivo. Cria novo se não existir.
x cria novo arquivo lançando erro se já existir.

São métodos do objeto file:

Método Descrição
close() fecha o arquivo; sem efeito se arq. já está fechado
detach() retorna o fluxo bruto separado do buffer
fileno() retorna número inteiro diferente para cada arquivo aberto
flush() descarrega no disco as alterações no buffer
isatty() retorna True se o fluxo de arquivo é interativo
next(arquivo) itera sobre arquivo, lançando erro no final
read() retorna o conteúdo do arquivo
readable() retorna True se o fluxo do arquivo pode ser lido
readline() retorna uma única linha do arquivo
readlines() retorna lista com todas as linhas do arquivo
search() localiza item no arquivo e retorna sua posição
searchable() retorna True se o arquivo é pesquisável, por ex. com seek()
tell() retorna a posição atual do arquivo
truncate([tamanho]) redimensiona (truncando) o arquivo para um tamanho especificado
writable() retorna True se o arquivo pode receber gravações
write() grava a string especificada no arquivo
writelines() escreve a sequencia no arquivo. Qualquer objeto iterável composto por strings pode ser usado

Módulo os


Vimos que existem diversas funções e outros objetos no módulo básico que são carregados juntamente com o próprio Python. No entanto muitos outros módulos podem sem anexados no código, inclusive módulos na chamada biblioteca padrão. Módulos podem ser escritos pelo usuário e importados em seu código ou compartilhados. Além disso, outros módulos podem ser instalados, inclusive desenvolvido por terceiros.

De particular interesse para a manipulação de arquivos é o módulo os que contém as funcões básicas de interação com o sistema operacional. Para habilitar o uso de módulos instalados usamos import nome_do_modulo.

Listar pastas e arquivos:

Por exemplo, após a importação de os podemos usar o método os.listdir(pasta) que retorna uma lista com o conteúdo da pasta especificada.

» import os
» pasta ='/home/guilherme/Projetos/Python'
» lista_arquivos = os.listdir(pasta)
» print(lista_arquivos)
↳ ['arquivos', 'wallpaper.py', 'turtle.py', 'numpy_02.py', 'NLP', 'output', 'osDir.py',
↳  'lerShelfFile.py', 'guessNumber.py', ...]

Embora esse método ainda esteja disponível, no Python 3.5 foi inserido uma nova forma de obter esse resultado por meio de os.scandir() que retorna um iterável. Os elementos desse iterável possuem diversas propriedades, entre elas name, usada para recuperar o nome do arquivo ou pasta.

» arquivos = os.scandir(pasta)
» for arquivo in arquivos:
»     print(arquivo.name)
↳ wallpaper.py
↳ turtle.py
↳ numpy_02.py
↳ NLP
↳ output
↳ osDir.py
↳ ...

Observação (Windows): Como o caracter \ tem significado especial (escape) no Python é necessário escrever caminhos no Windows de uma forma especial, de uma das duas formas:

» caminho = r'C:\Windows\Temp'
# ou
» caminho = 'C:\\Windows\\Temp'

No código abaixo usamos os.path.join(local, t) que faz a concatenação correta do caminho com o nome do arquivo, agindo de forma diferente de acordo com o sistema operacional. O método os.path.split age de forma inversa, retornando uma tupla que contém a caminho e o nome do arquivo. Depois de extraído o nome do arquivo podemos obter uma tupla com nome separado da extensão usando os.path.splitext.

# como funciona os.path.join (no linux ou OSX)
» os.path.join('caminho_completo', 'nome_do_arquivo')
↳ 'caminho_completo/nome_do_arquivo'

# separando caminho do arquivo
» os.path.split('/home/usuario/Documentos/Artigo.txt')
↳ ('/home/usuario/Documentos', 'Artigo.txt')

# separando a extensão
» os.path.splitext('image.jpeg')
↳ ('image', '.jpeg')

# no windows uma string diferente seria retornada com join
» os.path.join('caminho_completo', 'nome_do_arquivo')
↳ 'caminho_completo\\nome_do_arquivo'

Filtrar pastas e arquivos:

Para investigar se o elemento retornado por listdir é um arquivo podemos usar os.path.isfile(), e os.path.isdir() para pastas (diretórios).

# para filtrar os arquivos
» local = '.'
» for t in os.listdir(local):
»     if os.path.isfile(os.path.join(local, t)):
»         print(t)
↳ arquivo001.txt
↳ arquivo002.txt
↳ jupyter001.ipynb
↳ teste.csv

# para filtrar as pastas
» for t in os.listdir(local):
»     if os.path.isdir(os.path.join(local, t)):
»         print(t)
↳ .ipynb_checkpoints
↳ dados

# para listar arquivos dentro da pasta 'dados'
» local = './dados'
» for t in os.listdir(local):
»     if os.path.isfile(os.path.join(local, t)):
»         print(t)
↳ nums.csv
↳ alunos.pkl

A mesma operação feita usando os.scandir()

# usando scandir
» local = '.'
» with os.scandir(local) as arqs:
»     for arq in arqs:
»         if arq.is_file():
»             print('arquivo:', arq.name)
»         elif arq.is_dir():
»             print('pasta:  ', arq.name)
↳ arquivo: pandas001.ipynb
↳ arquivo: python001.ipynb
↳ arquivo: teste_gravar
↳ arquivo: alunos.txt
↳ arquivo: teste_novo.csv
↳ pasta:   .ipynb_checkpoints
↳ arquivo: teste.py
↳ arquivo: alunos.csv
↳ pasta:   dados
↳ ...            

Claro que essa mesma lista pode ser obtida através de uma compreensão de lista, o que ilustra mais uma vez o poder de concisão dessa construção.

# para arquivos
» lst_arquivos = [t.name for t in os.scandir(local) if t.is_file()]
» lst_arquivos
↳ [pandas001.ipynb, python001.ipynb, teste_gravar, alunos.txt,
↳  teste_novo.csv, teste.py, alunos.csv]

# para as pastas
» [t.name for t in os.scandir(local) if t.is_dir()]
↳ ['.ipynb_checkpoints', 'dados']

A recuperação dos nomes dos arquivos, associada a testes de string com esses nomes, permite uma filtragem mais específica de arquivos retornados. No exemplo abaixo usamos .endswith para verificar se os arquivos possuem uma extensão determinada (no caso ‘.csv’). No segundo exemplo usamos compreensão de lista e testamos a substring após o último ponto.

# usando .endswith()
» print('Arquivos com extensão csv:')
» with os.scandir(local) as arqs:
»     for arq in arqs:
»         if arq.is_file() and  arq.name.endswith('.csv'):
»             print(arq.name)
Arquivos com extensão csv:
↳ teste_novo.csv
↳ alunos.csv

# alternativamente, usando compreensão de lista e .split('.')
» [arq.name for arq in os.scandir(local) if arq.is_file() if arq.name.split('.')[-1]=='txt']
↳ ['alunos.txt']

O método os.scandir() não recupera apenas os nomes dos arquivos. Cada objeto do ScandirIterator possue o método .stat() que retorna informações sobre o arquivo ou pasta ao qual se refere, como tamanho do arquivo. O atributo st_mtime, por ex., contém a hora da última alteração.

No exemplo seguinte fazemos uma iteração sobre os arquivos e pastas em local e imprimimos a data de sua última alteração.

# atributo de arquivos: tempo desde última alteração
» with os.scandir(local) as arquivos:
»     for arq in arquivos:
»         info = arq.stat()
»         print(info.st_mtime)
↳ 1612654770.8688447
↳ 1615833776.4532673
↳ 1611327215.6848917
↳ ...

O retorno é dado em segundos desde a época, uma medida de tempo também chamada de Unix time que consiste no número de segundos decorridos desde Zero Horas do dia 1 de Janeiro de 1970 (UTC, longitude 0°) e pode ser convertido em uma data mais legível através de diversas funções de tratamento de datas e horas.

No código seguinte lemos os arquivos na pasta local e retornamos seus nomes e datas de última modificação, convertidos em forma mais legível usando uma função do módulo datetime. Veremos mais tarde outras funcionalidades desse módulo.

» from datetime import datetime

» def converter_data(timestamp):
»     d = datetime.utcfromtimestamp(timestamp)
»     return d.strftime('%d/%m/%Y')

» def ler_arquivos():
»     arquivos = os.scandir(local)
»     for arq in arquivos:
»         if arq.is_file():
»             info = arq.stat()
»             print(f'{arq.name}\t\t Modificado em: {converter_data(info.st_mtime)}')

↳ Pandas001.ipynb      Modificado em: 06/02/2021
↳ Python001.ipynb      Modificado em: 15/03/2021
↳ teste_gravar         Modificado em: 15/03/2021
↳ alunos.txt           Modificado em: 16/03/2021
↳ teste_novo.csv       Modificado em: 16/11/2020
↳ ...

Os argumentos passados para strftime() são: %d o dia do mês, %m o número do mês e %Y o ano, com 4 dígitos. Várias outras formatações são possíveis.

O módulo os contém os métodos os.getcwd() para ler a pasta ativa no momento, e os.chdir(‘nova_pasta’) para trocar para uma nova pasta.

» import os
» os.getcwd()
↳ '/home/guilherme/Projetos/Phylos.net'

» os.chdir('/home/guilherme/Music')
» os.getcwd()
↳ '/home/guilherme/Music'

Também podemos criar novas pastas. Para isso usamos os.mkdir() (cria uma pasta) e os.makedirs() (cria várias pastas). Podemos criar, dentro da pasta atual, a pasta exemplo e exemplo/textos.

» import os
» os.mkdir('exemplo')
» os.mkdir('exemplo/textos')

# a tentativa de criar uma pasta existente resulta em erro
» os.mkdir('exemplo')
↳ FileExistsError: [Errno 17] File exists: 'exemplo'
Figura 1

Várias pastas e subpastas podem ser criadas simultaneamente. O comando abaixo cria três pastas.

» os.makedirs('2021/03/22')

A estrutura de pastas criada na pasta atual é ilustrada na figura 1:

Os seguintes comandos ilustram operações feitas com métodos de os:

# armazena pasta de trabalho atual, antes da modificação
» pasta_trabalho = os.getcwd()
# altera pasta atual para outra
» os.chdir('/home/guilherme/Music')
# cria subpastas na pasta atual (resultado semelhante ao da figura 1)
» os.makedirs('subpasta10/subpasta11/subpasta12')

# retorna para a pasta de trabalho
» os.chdir(pasta_trabalho)

# cria sub pasta
» os.mkdir('teste')

# muda pasta atual ('.' é atalho para pasta atual)
» os.chdir('./teste')
# verifica qual é a pasta atual
» os.getcwd()
↳ '/home/guilherme/Projetos/Artigos/teste'

# volta para pasta no nível acima ('..' é atalho para pasta 'mãe')
» os.chdir('..')

# renomeia pasta
» os.rename('teste', 'novo')

# os.remove só apaga arquivos
» os.remove('novo') # só pode apagar arquivo
↳ IsADirectoryError: [Errno 21] Is a directory: 'novo'

» os.remove('aluno.txt') # só pode apagar arquivo
# o arquivo 'aluno.txt' foi apagado (se existe)

# para apagar uma pasta use rmdir
» os.rmdir('novo')
# a pasta 'novo' foi apagada

O método os.remove(arquivo) lança um erro se o arquivo não existe ou se é uma pasta. Para evitar essa possibilidade usamos um try ou testamos previamente o arquivo. Para apagar pastas e subpastas podemos percorrer cada uma delas (veja método abaixo) ou usar o módulo shutil.

» arquivo_apagar = 'home/data.txt'
# testa a existência da arquivo
» if os.path.isfile(arquivo_apagar):
»     os.remove(arquivo_apagar)
» else:
»     print(f'O arquivo: {data_file} não existe ou é uma pasta')

Para apagar uma pasta podemos usar os.rmdir() ou shutil.rmtree().

» apagar_pasta = 'documentos/pasta'
» try:
»     os.rmdir(apagar_pasta)
» except OSError as e:
»     print(f'Error: {apagar_pasta} : {e.strerror}')

# apagar pastas e subpastas, mesmo que não estejam vazias
» import shutil
» apagar_pasta = 'documentos/pasta'
» try:
»     shutil.rmtree(apagar_pasta)
» except OSError as e:
»     print(f'Error: {apagar_pasta} : {e.strerror}')  

Percorrendo a árvore de pastas:

Não é rara a necessidade de percorrer as pastas e subpastas em uma estrutura de diretórios, eventualmente executando uma tarefa nos arquivos no disco ou fazendo buscas. O método os.walk() pode auxiliar nessa tarefa, percorrendo a árvores de pastas tanto da pasta raiz para as subpastas (de cima para baixo, top down) quanto no sentido inverso (bottom up). Por default os.walk(pasta) faz várias iterações em pasta e subpastas, até esgotar a árvore, de mãe para filhos. Em cada iteração retorna:

Figura 2
  • uma string com o nome da pasta atual
  • uma lista de suas subpastas
  • uma lista dos arquivos da pasta atual

Depois a iteração passa para uma subpasta (no modo de cima para baixo) ou para a pasta mãe. Para percorrer a árvore no sentido de baixo para cima usamos o parâmetro os.walk(pasta, topdown=False). No código que se segue percorremos a árvore (lembrando que ‘.’ simboliza pasta ativa atual). O teste if not sub_pastas resulta True se a lista está vazia. O resultado exibido supõe uma estrura de pastas como na figura 2.

# usando os.walk() para listas arquivos e pastas
» for pasta, sub_pastas, arquivos in os.walk('.'):
»     if not sub_pastas:
»         print('A pasta:', pasta, 'não possui subpastas')
»     else:    
»         print('A pasta:', pasta, 'possui as subpastas:')
»         for sub in sub_pastas:
»             print('\t\t',sub)
»     if not arquivos:
»         print('\t não possui arquivos')
»     else:    
»         print('\t e os arquivos:')    
»         for nome_arquivo in arquivos:
»             print('\t\t', nome_arquivo)

↳ A pasta: . possui as subpastas:
↳        pasta_1
↳        pasta_2
↳    e os arquivos:
↳        texto.txt
↳ A pasta: ./pasta_1 não possui subpastas.
↳    e os arquivos:
↳        arquivo1.py
↳        arquivo2.py         
↳        arquivo3.py
↳ A pasta: ./pasta_2 não possui subpastas.
↳    e os arquivos:
↳        arquivo4.py
↳        arquivo5.py         
↳        arquivo6.py

Informações do sistema operacional:

O módulo os possui muitos outros métodos. Por exemplo, os.uname() retorna o sistema operacional, nome de usuário e dados da versão do sistema usado.

» for t in os.uname():
»     print(t)
↳ Linux
↳ guilherme-Lenovo
↳ 5.8.0-45-generic
↳ #51-Ubuntu SMP Fri Feb 19 13:24:51 UTC 2021
↳ x86_64

O módulo os possui algumas propriedades úteis, entre elas os.environ, que contém informação sobre o sistema operacional, e os.environ que armazena um dicionário com informações sobre o ambiente (environment).

» os.system
↳ <function posix.system(command)>
        
» os.environ
↳ environ{'QT_SCALE_FACTOR': '1',
        'LANGUAGE': 'en_US',
        'SHELL': '/bin/bash',
        'LC_NUMERIC': 'pt_BR.UTF-8',
        'LC_PAPER': 'pt_BR.UTF-8',
        'MATE_DESKTOP_SESSION_ID': 'this-is-deprecated',
        'JPY_PARENT_PID': '9099',
        'TERM': 'xterm-color',
        ... [truncado]
        'MPLBACKEND': 'module://ipykernel.pylab.backend_inline'}

» # essa propriedade é um tipo dicionário. por ex.:
» os.environ['USER']
↳ guilherme

Os seguintes testes foram realizados no prompt do python, em sessão do Anaconda.

>>> import os
>>> os.times()
↳ posix.times_result(user=0.03, system=0.0, children_user=0.0, children_system=0.0, elapsed=17180795.37)
>>> os.system('date')
↳ sáb 05 jun 2021 14:19:27 -03

# supondo a existência de um programa (ou atalho) de nome caja
# o aplicativo caja é executado (no caso um gerenciador de arquivos)
>>> os.system('caja')

O método os.walk(top, topdown=True, onerror=None, followlinks=False) retorna tuplas com pasta atual, subpastas e arquivos nelas contidos. Por default ele percorre as pastas iniciando em top e descendo para as substastas nele localizadas.

» import os
» path = '/home/guilherme/Temp'
» for (root, dirs, files) in os.walk(path):
»     print('Pasta atual: %s' % root)
»     print('Com as subpastas:\n', dirs)
»     print('Arquivos presentes:\n', files)
»     print('\n--------------------------------\n')

# saida (truncada)
↳ Pasta atual: /home/guilherme/Temp
↳ Com as subpastas:
↳  ['RevelationSpace', 'pandas']
↳ Arquivos presentes:
↳  ['Livro1', 'modal.html', 'FileZilla.xml']
↳ 
↳ Pasta atual: /home/guilherme/Temp/RevelationSpace
↳ Com as subpastas:
↳  []
↳ Arquivos presentes:
↳  ['01-RevealSpace.mp3', '02-RevealSpace.mp3', '03-RevealSpace.mp3', '04-RevealSpace.mp3', '05-RevealSpace.mp3']
↳ 
↳ Pasta atual: /home/guilherme/Temp/pandas
↳ Com as subpastas:
↳  ['Lambda', 'Pandas']
↳ Arquivos presentes:
↳  ['Lambda.html', 'Pandas.html']

Métodos de OS

Segue uma lista de alguns dos métodos do módulo os:

Método significado
chdir(“novaPasta”) muda a pasta ativa para novaPasta. Se novaPasta = “..” vai para pasta mãe
os.environ() obtenha o ambiente dos usuários,
os.getcwd() retorna pasta de trabalho atual,
os.getgid() retorna id de grupo real do processo atual,
os.getuid() retorna ID do usuário do processo atual,
os.getpid() retorna ID de processo real do processo atual,
os.umask(mascara) define o umask numérico atual e retorne o umask anterior,
os.uname() retorna informações que identificam o sistema operacional atual,
os.chroot(caminho) altera pasta raiz do processo atual para caminho,
os.listdir(caminho) retorna lista de arquivos e pastas no caminho,
os.mkdir(caminho) cria pasta caminho,
os.makedirs(sequencia) criação recursiva de pastas na sequencia,
os.remove(caminho_arquivo) remove arquivo em caminho_arquivo,
os.removedirs(caminho) remove pastas recursivamente,
os.rename(‘nomeAntigo’,’nomeNovo’) renomeia arquivo/pasta ‘nomeAntigo’ para ‘nomeNovo’dst,
os.rmdir(caminho) remove pasta em caminho,
os.system(comando) executa comando na shell do sistema operacional,
os.uname() retorna dados sobre o sistema operacional, usuário e máquina usada
os.walk(caminho) retorna tuplas com pasta, subpastas e arquivos em cada uma.
🔺Início do artigo

Bibliografia

Consulte a bibliografia no final do primeiro artigo dessa série.

Python: Compreensão de listas


Sobre tuplas e Zips

Algumas propriedades extras sobre tuplas são listadas aqui. Além disso usamos a fução zip() para unir objetos iteráveis de modo a poderem ser percorridos simultaneamante.

# Uma tupla não precisa de parenteses
» t = 1, 2, 3, 4
» t
↳ (1, 2, 3, 4)
» type(t)
↳ tuple
# podemos usar tuplas para declarações múltiplas
» x, y = 45, 78
» print(x,y, x+y)
↳ 45 78 123
# E percorrer uma tupla (ou lista) em um loop for
» t = [('a', 0, True), ('b', 1, False), ('c', 2, True)]
» for letra, num, b in t:
»     if b: print (num, letra)
↳ 0 a
↳ 2 c

# qualquer sequência pode ser usada no construtor de tuplas
» t = tuple('palavras')
» t
↳ ('p', 'a', 'l', 'a', 'v', 'r', 'a', 's')

# uma tupla de 1 elemento deve conter (,)
» t = ('a',)
» t
↳ ('a',)

» type(t)
↳ tuple

# tuplas para 'swap' de variáveis
» a = 'both'
» b = 'ambos'
print(a,b)
↳ both ambos
» a, b = b, a
» print(a,b)
↳ ambos both

Funções retornam uma variável ou None. No entanto essa variável pode ser uma tupla, um dicionário ou outro objeto qualquer, o que tem o efeito de ter uma função retornando mais de um valor. Por exemplo, divmod(a,b) retorna uma tupla com o resultado inteira da divisão e o resto. Funções do usuário podem fazer o mesmo, inclusive retornando outra função, como veremos.

» divmod(10,3)
↳ (3, 1)

» div, resto = divmod(10,3)
» print(div)
↳ 3

» print(resto)
↳ 1

# Um exemplo de uma função que retorna uma sequência
» def min_max(seq):
»     return min(seq), max(seq)
»
» seq = (23,45,23,78,1,23,0,-34)
» min_max(seq)
↳ (-34, 78)

» seq = {23,45,23,78,1,23,0,-34}
» min_max(seq)
↳ (-34, 78)

# funções com n argumentos
» def imprima_tudo(*args):
»     print(args)

» imprima_tudo(1,2, '3', True)
↳ (1, 2, '3', True)

# sem * temos um erro
» def imprima(args):
»     print(args)
» imprima(1,2)
↳
---------------------------------------------------------------------------
TypeError: imprima() takes 1 positional argument but 2 were given

Função zip()

Uma função interna interessante e útil é zip(). Ela recebe como argumentos duas ou mais sequências e as compacta em um objeto zip que é um iterador de tuplas, onde cada tupla contem um elemento de cada sequência usada como parâmetro. Ela retorna um iterador de tuplas, onde a i-ésima tupla contém o i-ésimo elemento de cada uma das sequências ou iteráveis fornecidas em seu argumento. Se as sequências não tem o mesmo comprimento o iterador retornado tem o comprimento da menor. Se nenhum argumento for fornecido ela retorna um iterador vazio. Com um único argumento iterável, ele retorna um iterador de tuplas de um elemento.

» seq1 = 'Blade'
» seq2 = 'Runner'
» zipado = zip(seq1, seq2)
» print(zipado)
↳ <zip object at 0x7f27c9f77500>

# use a função tuple() para visualizar o objeto zip
» print(tuple(zipado))
↳ (('B', 'R'), ('l', 'u'), ('a', 'n'), ('d', 'n'), ('e', 'e'))

# usando a mesma variável já definida
» for letra1, letra2 in zipado:
»     print(letra1, letra2)
↳ B R
↳ l u
↳ a n
↳ d n
↳ e e

# um objeto zip pode ser passado como argumento na construção
# de uma lista gerando uma lista de tuplas
» s1 = [1, 2, 3]
» s2 = ['a', 'b', 'c']
» list(zip(s1, s2))
↳ [(1, 'a'), (2, 'b'), (3, 'c')]

# várias sequências podem ser fornecidas
» s1 = 'Margem'
» s2 = 'direita'
» s3 = 'do rio'
» zip123 = zip(s1, s2, s3)
» for a,b,c in zip123:
»     print(a, b, c)
↳ M d d
↳ a i o
↳ r r
↳ g e r
↳ e i i
↳ m t o

No útimo exemplo a sequência s2 tem maior comprimento que as demais. A letra final ‘a’ foi ignorada.

Suponha que desejamos analisar duas sequências e verificar se elas possuem elementos iguais em uma mesma posição. No exemplo abaixo fazemos este teste usando zip() e definindo uma função que retorna True se houver essa coincidência. Observe que a função é abandonada na ocorrência da primeira coincidência.

Pode ocorrer que precisamos saber qual é a posição desse (ou de vários elementos comuns). Para isso usamos enumerate(). Ela tem a seguinte forma:

enumerate(iteravel, inicio=0)

onde iteravel é uma sequência ou qualquer objeto iterável. Ela retorna pares iteráveis contendo o elemento e sua posição, começando em início (com default 0) e o elemento da sequência.

# função que retorna True se houver coincidência de elementos (ou False se não houver)
» def mesmo_elemento(t1, t2):
» for i, j in zip(t1, t2):
»     if i == j:
»         return True
» return False

» t1 = [1,2,3,4,5,6,7,8,9]
» t2 = [9,8,7,6,5,4,3,2,1]
» mesmo_elemento(t1,t2)
↳ True

# Para informar a posição da coincidência usamos enumerate
» for index, element in enumerate('abc'):
»     print(index, element)
↳ 0 a
↳ 1 b
↳ 2 c

» type(enumerate('abdc'))
↳ enumerate

# Repetindo o exemplo anterior, queremos descobrir se duas sequências
# possuem elementos comuns na mesma posição, e que posição é essa

» def posicao_elemento(t1, t2):
»     ''' retorna dictionary com indice e elemento onde elemento de t1 é igual ao de t2 '''
»     dict = {}
»     if len(t2) < len(t1):
»         (t1, t2) = (t2, t1)
»     for index, i in enumerate(t1):
»         if i == t2[index]:
»             dict[index] = i
»     return dict

» t2 = [1,2,3,4,5,6,7,6,9]
» t1 = [9,8,7,4,3,4,5,6,7,8,9,0,23,45]
» posicao_elemento(t1,t2)
↳ {3: 4, 7: 6}

» t1 = 'josefina'
» t2 = 'gasolina cara'
» posicao_elemento(t1,t2)
↳ {2: 's', 5: 'i', 6: 'n', 7: 'a'}

A linha if len(t2) < len(t1): (t1, t2) = (t2, t1) garante que t2 tenha o maior comprimento. Caso contrário o teste i == t2[index] poderia poderia dar erro ao tentar obter elemento inexistente de t2.

Compreensão de Listas (List Comprehensions)


Suponha que queremos uma lista com os quadrados de todos os números de 0 até 10. Podemos conseguir isso usando um laço for:

» quadrados = []
» for i in range(11):
»     quadrados.append(i**2)
»
» quadrados
↳ [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Há uma forma alternativa no Python para conseguir o mesmo resultado chamada compreensão de listas (list comprehensions).
Sua sintaxe básica é

[expressão(item) for item in lista if condição].

» quads = [i**2 for i in range(1, 11)]
» quads
↳ [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Essa forma permite que se escreva um código mais compacto, legível e eficiente (portanto de mais rápida execução).
A expressão pode ser uma função do usuário. No exemplo usamos a função já definida anteriormente para o cálculo de fatoriais:

» def fatorial(n):
»     if n <= 1: return 1
»     else: return n*fatorial(n-1)
»
# exibindo os fatoriais de ímpares de 1 a 9
» fats = [fatorial(i) for i in [1, 3, 5, 7, 9]]
» fats
↳ [1, 6, 120, 5040, 362880]

# atuando sobre strings
» palavras = ['Organização', 'Nações', 'Unidas']
» letras = [l[0] for l in palavras]
» letras
↳ ['O', 'N', 'U']

» tamanho = [len(p) for p in palavras]
» tamanho
↳ [11, 6, 6]

Uma ou mais condições pode ser incluída na lista a ser iterada.

[expressao(item) for item in lista if condição1 if condição2 ...]

No exemplo abaixo usamos i%2, que é o resto da divisão de i por 2. Se esse resto for 0 o número é par.

# ex.: lista dos números pares de 0 até 10, exclusive
# i%2 == 0 significa que o número é par
» num = [i for i in range(10) if i%2==0 ]
» print(num)
↳ [0, 2, 4, 6, 8]

# um exemplo com strings: apenas as palavras com comprimento maior que 5
» palavras = ['casa', 'aleatório', 'rudimentar', 'longo']
» [p for p in palavras if len(p) > 5 ]
↳ ['aleatório', 'rudimentar']

# Várias condições podem ser impostas simultaneamente
# São retornados os múltiplos de 2 e 3 simultaneamente, i.e. os múltiplos de 6.
» print([i for i in range(50) if i%2==0 if i%3==0])
↳ [0, 6, 12, 18, 24, 30, 36, 42, 48]

# a expressão na lista pode conter 'if else':
# a abaixo expressão retorna 'abacaxi' para números pares,
# 'laranja' para múltiplos de 3 e 'caqui' para todos os demais
» fruits = ['abacaxi' if i%2==0 else 'laranja' if i%3==0 else 'caqui' for i in range(10)]
» print(fruits)
↳ ['abacaxi', 'caqui', 'abacaxi', 'laranja', 'abacaxi', 'caqui', 'abacaxi', 'caqui', 'abacaxi', 'laranja']

# As listas podem ser aninhadas. Abaixo a lista externa percorre letras,
# a lista interna percorre números
» matriz = [[i+j for i in 'abcd'] for j in '1234']
» matriz
↳ [['a1', 'b1', 'c1', 'd1'],
   ['a2', 'b2', 'c2', 'd2'],
   ['a3', 'b3', 'c3', 'd3'],
   ['a4', 'b4', 'c4', 'd4']]

Um teste pode ser negado com o operador lógico not (que inverte o booleano). Usamos também o método string.isalpha() que retorna True se string for formado apenas de caracteres (sem dígitos ou pontuações).

# teste not in
# isalpha() retorna True se a string é composta de caracteres alfabéticos
» vogais = {'a', 'e', 'i', 'o', 'u'}
» texto = 'Aprenda todas as regras e transgrida algumas 1234.'
» letras = set(texto.lower())
» consoantes = {letra for letra in letras if letra not in vogais if letra.isalpha()}
» consoantes
↳ {'d', 'g', 'l', 'm', 'n', 'p', 'r', 's', 't'}

# na expressão seguinte um set de tuplas é gerado
{(i, j) for i in range(3, 5) for j in range(2)}
↳ {(3, 0), (3, 1), (4, 0), (4, 1)}

» print([i+j for i in 'abcd' for j in 'efgh'])
↳ ['ae', 'af', 'ag', 'ah', 'be', 'bf', 'bg', 'bh', 'ce', 'cf', 'cg', 'ch', 'de', 'df', 'dg', 'dh']

» substantivo = ['pão', 'pé', 'carro', 'bolo', 'mato']
» adjetivo = ['pequeno', 'bonito', 'bom', 'caro']
» ['%s %s' % (s, a) for s in substantivo for a in adjetivo if s[0] == a[0]]
↳ ['pão pequeno', 'pé pequeno', 'carro caro', 'bolo bonito', 'bolo bom']

# Podemos calcular o produto escalar de 2 vetores
» u = [1,3,4,5,6]
» v = [-1,0,9,-3,2]
» sum([a+b for a,b in zip(u,v)])
↳ 26

No cálculo do produto escalar zip(u,v) contém 5 pares que são somados formando uma lista de 5 elementos. A função sum(lista) soma esses 5 elementos.

Compreensão com dicionários

Compreensão de listas podem ser aplicadas a dicionários, alterando tanto suas chaves como valores. Lembrando dic.items() retorna uma tupla chave:valor do dicionário.

# uma compreensão para duplicar os valores em um dicionário
» dic1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
» duplo_dic1 = {k:v*2 for (k,v) in dic1.items()}
» duplo_dic1
↳ {'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}

# alterar o texto nas chaves, acrescentando prefixo R
» R_dic1 = {'R'+k : v*2 for (k,v) in dic1.items()}
» R_dic1
↳ {'Ra': 2, 'Rb': 4, 'Rc': 6, 'Rd': 8, 'Re': 10}

» s_dic = {l.upper(): l*3 for l in 'dna'}
» print (s_dic)
↳ {'D': 'ddd', 'N': 'nnn', 'A': 'aaa'}

No exemplo seguinte queremos construir um dicionário onde a chave é um par entre 1 e 10, inclusive, e o valor é seu quadrado. Fazemos isso sem compreensão de lista e depois com.

# Obs. percorrer i = 1, ..., 5 e tomar seu dobro garante que usamos apenas os pares de 1 a 10
» dic_2 = {}
» for i in range(1,6):
»     dic_2[i*2] = (i*2)**2
» dic_2
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# a mesma construção, usando compreensão
» dic_3 = {n:n**2 for n in range(1,11) if n%2 == 0}
» dic_3
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# o mesmo resultado será obtido com
» dic_4 = {2*n:(2*n)**2 for n in range(1,6)}
» dic_4
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# Podemos usar compreensões para filtrar elementos de um dicionário
» dic_4a = {k:v for (k,v) in dic_4.items() if v>36}
» dic_4a
↳ {8: 64, 10: 100}

» # selecionando apenas valores em um intervalo
» dic_4b = {k:v for (k,v) in dic_4.items() if v > 16 if v < 100}
» dic_4b
↳ {6: 36, 8: 64}

# duas condições podem ser usadas
» dic_4b = {k:v for (k,v) in dic_4.items() if v > 16 if v < 100}
» dic_4b
↳ {6: 36, 8: 64}

# duas condições, sobre chave e valor
» dic_4c = {k:v for (k,v) in dic_4.items() if k > 5 if v < 90}
» dic_4c
↳ {6: 36, 8: 64}

# o valor é (im)par para chave (im)par
» dic_par_impar = {i:('par' if i%2==0 else 'impar') for i in range(5)}
» dic_par_impar
↳ {0: 'par', 1: 'impar', 2: 'par', 3: 'impar', 4: 'par'}

Algumas vezes pode ser útil usar zip() para construir dicionários através de uma compreensão.

# Lists to represent keys and values
» keys = [101, 201, 301, 401, 501]
» values = ['inglês', 'francês', 'alemão', 'russo', 'espanhol']

# keys e values são colocados em um único iterável (um objeto zip)
» dic_5 = { k:v for (k,v) in zip(keys, values)}
» print(dic_5)
↳ {101: 'inglês', 201: 'francês', 301: 'alemão', 401: 'russo', 501: 'espanhol'}

# observe que o iterável poderia ser dado diretamente como parâmetro do construtor
» dic_6 = dict(zip(keys, values))
» print(dic_6)
↳ {101: 'inglês', 201: 'francês', 301: 'alemão', 401: 'russo', 501: 'espanhol'}

# dicionários aninhados
» dicionario = {i: {j: i*j for j in range(1, 6)} for i in range(2, 5)}
» print(dicionario)
↳ {2: {1: 2, 2: 4, 3: 6, 4: 8, 5: 10}, 3: {1: 3, 2: 6, 3: 9, 4: 12, 5: 15}, 4: {1: 4, 2: 8, 3: 12, 4: 16, 5: 20}}

Nesse dicionário as chaves vão de 2 até 4 e seus valores são outros dicionários.

Compreensões de listas e de dicionários são formas elegantes e de fácil leitura. No entanto, se forem muito complexas elas podem tornar o código difícil de ler e de debugar (encontrar eventuais erros). Devemos nos lembrar que outras pessoas podem necessitar ler o nosso código, ou você mesmo, daqui a um tempo, quando dificilmente se lembrará do raciocínio que te levou à construção de bloco sofisticados e elegantes.

Função filter()


A função tem a forma de

filter(funcao, sequencia)

e constroi um iterador usando uma função booleana (que retorna True ou False) e uma sequência ou um iterável qualquer. Ela testa cada elemento da sequência usando a função e retorna apenas os elementos avaliados como True.

» idades = [5, 12, 17, 18, 24, 32]
» def maior(x):
»     if x < 18:
»         return False
»     else:
»         return True

» adultos = filter(maior, idades)
» for x in adultos:
»     print(x)
↳ 18
↳ 24
↳ 32

# observe que a função acima poderia ser escrita simplesmente como
» def maior(x):
»     return x ≥ 18

No próximo exemplo temos uma lista de dicionários. Cada entrada dessa lista é um dicionário contendo dados sobre um animal. O código define uma função pesquisa que recebe essa lista e procura quais de suas entradas contém algum valor com o substring busca.

» bicharada = [
»   {'nome': 'Asdrubal', 'especie': 'tubarão', 'peso': '290', 'habitat': 'oceano'},
»   {'nome': 'Ana Lee', 'especie': 'caranquejo', 'peso': '1.2', 'habitat': 'oceano'},
»   {'nome': 'Raul', 'especie': 'urso', 'peso': '180', 'habitat': 'floresta'},
»   {'nome': 'Joana', 'especie': 'raposa', 'peso': '21', 'habitat': 'floresta'},
»   {'nome': 'Omar Lee', 'especie': 'golfinho', 'peso': '120', 'habitat': 'oceano'},
»   {'nome': 'Tulio', 'especie': 'rato', 'peso': '1.4', 'habitat': 'doméstico'}
» ]

» def pesquisa(lista, busca):
»     def meu_iterador(x):
»         for v in x.values():
»             if busca in v:
»                 return True
»         return False
»     return filter(meu_iterador, lista)

» lista_filtrada = pesquisa(bicharada, 'tuba')
» for t in lista_filtrada:
»     print(t)
↳ {'nome': 'Asdrubal', 'especie': 'tubarão', 'peso': '290', 'habitat': 'oceano'}

» lista_filtrada = pesquisa(bicharada, 'Lee')
» for t in lista_filtrada:
»     print(t)
↳ {'nome': 'Ana Lee', 'especie': 'caranquejo', 'peso': '1.2', 'habitat': 'oceano'}
↳ {'nome': 'Omar Lee', 'especie': 'golfinho', 'peso': '120', 'habitat': 'oceano'}

Sugestão: altere o código acima para encontrar parte de um valor dentro de um campo especificado. Encontre o animal cujo habitat contém a substring ‘ocea’.

Iteradores (iterators)

No Python um iterador é um objeto que contém um número contável de elementos que podem ser iterados, ou seja, que podem ser acessados e lidos um de cada vez. Além dos laços for e das compreensões de listas e sequências no Python podemos utilizar iteradores para percorrer sequências. Eles pode ser construídos com a função

nome_iterador = iter(sequencia, marcador).

O parâmetro marcador é opcional e …..

O iterador tem o método _next()_ que fornece o próximo elemento. Uma exceção StopIteration é lançada no final, quando todos os elementos foram percorridos. O iterador é esgotado e, portanto, só pode ser percorrido uma vez.

Exemplos de uso estão no código abaixo. Lista são convertidas em iteradores, que são percorridos com next().
Se a iteração não é interrompida antes do fim um erro é lançado. Um erro é gerado no final e sua captura é usada para interromper o laço.

# Uma lista é convertida em um iterador
lista = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
it = iter(lista)
for k in range(len(lista)):
    print(next(it), end=' ')
a b c d e f g h

# queremos separar números positivos e negativos de uma lista
# o erro no final da iteração é usado para interromper o laço
» original = [1,6,-8,-3,0, -2, 9, 76, -45]
» positivos = []
» negativos = []
» t = iter(original)
» try:
»     while True:
»         x = next(t)
»         if x >= 0:
»             positivos.append(x)
»         else:
»             negativos.append(x)
» except StopIteration:
»     print('São positivos:' , positivos, '\nSão negativos:' , negativos)

↳ São positivos: [1, 6, 0, 9, 76]
↳ São negativos: [-8, -3, -2, -45]

Embora a mesma funcionalidade possa ser conseguida de formas diferentes a transformação de um objeto iterável em um iterador é bastante útil para se escrever classes e métodos definidos pelo programador, como veremos.

Funções Lambda

Funções são boas formas de escrever código de fácil reutilização. No entanto, algumas vezes precisamos de definir um operador de forma compacta e que será utilizado apenas naquele ponto do código. As funções lambda (ou funções anônimas) permitem a criação de funções (que sequer recebem um nome) mas agem da forma desejada sobre seus argumentos.

Funções lambda tem uma sintaxe concisa:

lambda <arg1, ..., argn> : < expressão >

Elas só podem conter uma única expressão, nenhuma declaração, deve ser escrita em linha única e não comportam anotações. São exemplos simples de funções lambda:

# uma função bem simples é a identidade.
# a definição usual seria
» def id(x):
»     return x

# usando função lambda
» i = lambda x: x
» i(9)
↳ 9

# a função não precisa ser associada a uma variável
» (lambda x: abs(x))(-9)
↳ 9

# outros ex. Soma e multiplicação
» x = lambda a, b : a + b
» x(5,7)
↳ 12

# múltiplos argumentos não são envoltos em parênteses
» exp = lambda x,y : x**y
» exp(2,3)
↳ 8

» mix = lambda x,y,z : (x**y)/z
» mix(3,4,5)
↳ 16.2

# qualquer tipo de objeto pode ser passado como argumento
# no exemplo uma tupla é passada
» u = (4,7)
» (lambda x: x[0]*x[1])(u)
↳ 28

# uma função lamba que age sobre strings
# .title torna o primeiro caracter maiúsculo
» nome_autor = lambda nome, sobrenome: f'Autor: {nome.title()} {sobrenome.title()}'
» nome_autor('richard','dawkins')
↳ 'Autor: Richard Dawkins'

Um exemplo de uso frequente consiste no uso de lambdas para fornecer um critério de ordenamento.
Vimos que a função sorted(sequencia, key) pode ordenar uma sequência de acordo com um critério dado em key. No caso abaixo a cada tupla é associado o produto de seus pares, e a ordenação é feita nessa métrica.

» p = [(3, 3), (4, 2), (2, 2), (5, 2), (1, 7)] 
» sorted(p, key=lambda x: x[0]*x[1])
↳ [(2, 2), (1, 7), (4, 2), (3, 3), (5, 2)]

# dados vetores no espaço, ordená-los em ordem crescente de módulo
# a função lambda calcula o quadrado do módulo
» v =[(1,5,3), (1,8,2), (7,8,9), (2,4,3), (2,2,-1)]
» sorted(v, key=lambda x: x[0]**2+x[1]**2+x[2]**2)
↳ [(2, 2, -1), (2, 4, 3), (1, 5, 3), (1, 8, 2), (7, 8, 9)]

No exemplo seguinte definimos a função vezes_nque recebe o argumento n e opera sobre ele com a função lambda lambda a : a * n. Essa última, por sua vez, recebe outro argumento, no caso a. O exemplo também ilustra o fato de que uma função é um objeto e pode ser atribuída à uma variável.

# queremos uma função que multiplica seu argumento por um número especificado n
» # n pode ser visto como um parâmetro
» def vezes_n(n):
»     return lambda a : a * n

» funcao_duplicar = vezes_n(2)     # n = 2
» funcao_triplicar = vezes_n(3)    # n = 3
» funcao_x1000 = vezes_n(1000)     # n = 1000

» funcao_duplicar(23)              # n = 2, a = 23
↳ 46

» funcao_triplicar(15)             # n = 3, a = 15
↳ 45

» funcao_x1000(12.345)            # n = 1000, a = 12.345
↳ 12345.0

A minha_funcao abaixo é uma função lambda com dois argumentos sendo um deles outra função. No primeiro caso 2 + 2*10 = 22. No segundo caso 4 + (4)**3 -6 = 62.

» minha_funcao = lambda x, f: x + f(x)
» minha_funcao(2, lambda x: x*10 )     # aqui f(x) = x*10
↳ 22
» minha_funcao(4, lambda x: x**3-6)   # aqui f(x) = x**6 - 6
↳ 62

Função map()

Lambdas são muito usadas junto com a função map() que tem a seguinte assinatura:

map(funcao, iteravel1, ..., iteraveln).

Os dois argumentos são obrigatórios. A função percorre os iteráveis fornecidas usando a função especificada. Essa função deve usar um elemento de cada iterável. Ela usa um algoritmo otimizado para realizar sua transformação, o que a torna mais eficiente e rápida do que laços usuais do Python.

Ela retorna um objeto iterável map que pode ser convertido em lista com list(). Alguns exemplos:

# a operação abaixo concatena duas strings
» def concatena(a, b):
»     return (a + ' ' + b)
» iter1 = ('banana', 'laranja', 'uva')
» iter2 = ('nanica', 'bahia', 'syrah')
» x = map(concatena, iter1, iter2)

# para ver o resultado podemos percorrer a iterável gerada
# cada i no laço é uma única string (como 'banana nanica')
» for i in x:
»     print(i)
↳ banana nanica
↳ laranja bahia
↳ uva syrah

# a funcão pow(x,y) retorna x^y ou xy
# como pow age com 2 argumentos, duas sequências devem ser dadas em map
» y = map(pow, [6,7,8],[3,2,1])
» for i in y:
»     print(i, end=' - ')
↳ 216 - 49 - 8 -

# a função pode ser uma lambda
# duplicar cada elemento da lista
» lista = [10, 25, 17, 9, 30, -5]
» lista_2 = map(lambda n : n*2, lista)
# para exibir a sequência retornada podemos transformá-la em uma lista
» list(lista_2)
↳ [20, 50, 34, 18, 60, -10]

O uso de funções lambda pode tornar mais compacto o uso de map.

# para retornar uma lista de quadrados
» def quadrado(n):
»     return n**2
» numeros = [1, 2, 3, 4, 5]
» quadrados = map(quadrado, numeros)
» list(quadrados)
↳ [1, 4, 9, 16, 25]

# o mesmo resultado pode ser conseguido usando lambdas
» list(map(lambda x:x**2, range(1,6)))
↳ [1, 4, 9, 16, 25]

# abs() é função interna, que retorna valor absoluto
» nums = [-5, -3, -1, 0, 1, 3, 5]
» absolutos = list(map(abs, nums))
» absolutos
↳ [5, 3, 1, 0, 1, 3, 5]

# somar e multiplicar com 3 listas
» list(map(lambda i, j, k: i+j*k, [2, 4], [1, 3], [7, 8]))
↳ [9, 28]

# strip() remove string dados nas extremidades da string
» traco = ['---12356.876-', '-casa-da-vó---', '-mil-', '---']
» list(map(lambda s: s.strip("-"), traco))
↳ ['12356.876', 'casa-da-vó', 'mil', '']

# função recebe um número e retorna tuplas de 3 elementos
» def pot_1_2_3(x):
»     return x, x ** 2, x ** 3
» numeros = [1, 2, 3, 4]
» list(map(pot_1_2_3, numeros))
↳ [(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]

# a mesma coisa usando lambda
» list(map(lambda x: (x, x**2, x**3), numeros))
↳ [(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]

Em muitos casos uma compreensão de lista faz o mesmo que uma iteração via map(). No entanto é útil conhecer a função não apenas para poder ler e compreender código escrito por outras pessoas mas também para eventuais situações onde ela pode ser mais simples ou mais eficaz.

🔺Início do artigo

Bibliografia

Consulte a bibliografia no final do primeiro artigo dessa série.

Python: Resumos


Conteúdo

  • Métodos de Conjuntos (sets)
  • Métodos de Dicionários (dictionaries)
  • Métodos de Arquivos (files)
  • Métodos da biblioteca os
  • Exceções
  • Palavras reservadas

    Palavras reservadas (ou keywords) que são nomes que fazem parte da sintaxe da linguagem e não podem ser utilizadas para nomes de variáveis.

    Palavras reservadas do python
    and except lambda with
    as finally nonlocal while
    assert false None yield
    break for not
    class from or
    continue global pass
    def if raise
    del import return
    elif in True
    else is try

    Funções Internas

    Segue uma lista de funções internas ou pré-programadas do Python. Muitas outras podem ser acrescentadas à uma sessão através da importação de módulos externos.

    Função ação (retorna)
    abs() valor absoluto de um número
    all() True se todos os itens em um objeto iterável forem verdadeiros
    any() True se qualquer item em um objeto iterável for verdadeiro
    ascii() uma versão legível de um objeto (trocando caracteres não ascii por caracteres de escape)
    bin() versão binária de um número
    bool() valor booleano do objeto especificado
    bytearray() uma matriz de bytes
    bytes() um objeto bytes
    callable() True se o objeto especificado pode ser chamado, caso contrário, False
    chr() um caractere do código Unicode especificado.
    classmethod() converte um método em um método de classe
    compile() fonte especificada como um objeto, pronto para ser executado
    complex() um número complexo
    delattr() exclui o atributo especificado(propriedade ou método) do objeto
    dict() um dicionário(Array)
    dir() uma lista das propriedades e métodos do objeto
    divmod() quociente e resto quando o argumento1 é dividido pelo argumento2
    enumerate() pega uma coleção(por exemplo, uma tupla) e retorna como um objeto enumerado
    eval() avalia e executa uma expressão
    exec() executa o código(ou objeto) especificado
    filter() usa uma função de filtro para excluir itens em um objeto iterável
    float() um número de ponto flutuante
    format() formata um valor especificado
    frozenset() um objeto frozenset
    getattr() o valor do atributo especificado (propriedade ou método)
    globals() a tabela de símbolos global atual como um dicionário
    hasattr() True se o objeto especificado tem o atributo especificado(propriedade / método)
    hash() o valor hash de um objeto especificado
    help() exibe ajuda embutido
    hex() converte um número em seu valor hexadecimal
    id() o id de um objeto
    input() permite entrada do usuário
    int() um número inteiro
    isinstance() True se o objeto é instância de outro objeto especificado
    issubclass() True se a classe é subclasse de objeto especificado
    iter() um objeto iterador
    len() o comprimento de um objeto
    list() uma lista
    locals() um dicionário atualizado da tabela de símbolos local atual
    map() o iterador com a função especificada aplicada a cada item
    max() o maior item em um iterável
    memoryview() um objeto de visualização de memória
    min() o menor item em um iterável
    next() o próximo item em um iterável
    object() um novo objeto
    oct() converte um número em um octal
    open() abre um arquivo e retorna objeto de arquivo
    ord() converte um inteiro que representa o Unicode do caractere especificado
    pow() o valor de x à potência de y
    print() imprime no dispositivo de saída padrão
    property() obtém, define ou exclui uma propriedade
    range() uma sequência de números, começando em 0 e incrementos em 1 (por padrão)
    repr() uma representação legível de um objeto
    reversed() um iterador reverso
    round() arredonda um número
    set() um objeto de conjunto
    setattr() define um atributo (propriedade/método) de um objeto
    slice() um objeto de fatia
    sorted() uma lista ordenada
    @staticmethod() Converte um método em um método estático
    str() um objeto de string
    sum() soma os itens de um iterador
    super() um objeto que representa a classe pai
    tuple() uma tupla
    type() o tipo de um objeto
    vars() a propriedade __dict__ de um objeto
    zip() um iterador, de dois ou mais iteradores

    Métodos de Strings

    Método descrição
    capitalize() converte 1º caracter em maiúsculo
    casefold() converte string em minúsculas
    center() retorna string centralizada
    count() retorna número de ocorrências de um valor especificado na string
    endswith() retorna True se string termina com valor especificado
    find() busca por valor especificado na string e retorna the posição se encontrado
    format() Formata de acordo com valores especificados
    index() busca por valor especificado na string e retorna the posição se encontrado
    isalnum() retorna True se todos os caracteres são alfa-numéricos
    isalpha() retorna True se todos os caracteres são alfabéticos
    isdecimal() retorna True se todos os caracteres são decimais
    isdigit() retorna True se todos os caracteres são dígitos
    islower() retorna True se todos os caracteres são minúsculos
    isnumeric() retorna True se todos os caracteres são numéricos
    isspace() retorna True se todos os caracteres são espaços
    istitle() retorna True se a string segue regra de títulos
    isupper() retorna True se todos os caracteres são maiúsculos
    join() reune elementos de um iterável no final da string
    ljust() retorna a string justificada à esquerda
    lower() converte a string para minúsculas
    lstrip() retorna a string sem espaços à esquerda
    partition() retorna tuple partindo a string em 3 partes
    replace() substitui trecho da string por outro especificado
    rfind() busca trecho especificado value e retorna última posição
    rindex() busca trecho especificado value e retorna última posição
    rjust() retorna string justificada à direita
    rsplit() quebra a string no separador especificado, retornando lista
    rstrip() retorna a string sem espaços à direita
    split() quebra a string no separador especificado, retornando lista
    splitlines() quebra a string nas quebras de linha, retornando lista
    startswith() retorna True se string começa com valor especificado
    strip() retorna a string sem espaços laterais
    swapcase() inverte minúsculas e maiúsculas
    title() converte em maiúscula o 1º caracter de cada palavra
    upper() converte a string em maiúsculas
    zfill() preencha com número de zeros especificado, no início

    Métodos de Listas (lists)

    Método ação
    append() insere elementos na lista
    clear() remove todos os elementos na lista
    copy() retorna uma cópia da lista
    count() returns número de elementos com valor especificado
    extend() insere os elementos de outra lista (ou iterável) ao final da lista
    index() returna o índice do 1º elemento com valor especificado
    insert() insere elemento em posição especificada
    pop() remove elemento em posição especificada
    remove() remove elemento em posição especificada por índice
    reverse() inverte a ordem da lista
    sort() ordena a lista

    Além desses as seguintes funções são úteis para se tratar com sequências:

    Função Descrição
    cmp(x, y) compara dois valores
    len(seq) retorna o comprimento da sequência
    list(seq) converte uma sequência em lista
    max(args) retorna o valor máximo na sequência
    min(args) retorna o valor mínimo na sequência
    eversed(seq) permite a iteração sobre valores na sequência
    sorted(seq) retorna lista ordenada dos elementos na sequência
    tuple(seq) converte a sequência em uma tuple

    Métodos das Tuplas (tuples)

    Método Descrição
    count() retorna quantas vezes um valor especificado ocorre na tupla
    index() procura por valor especificado e retorna sua posição

    Métodos de Conjuntos (sets)

    Método Descrição
    add() insere elemento no set
    clear() remove todos os elementos do set
    copy() retorna cópia do set
    difference() retorna um set com a diferença entre 2 ou mais sets
    difference_update() remove elementos incluidos no segundo set
    discard() remove item especificado
    intersection() retorna o set interseção de 2 sets
    intersection_update() remove items do set não presentes no segundo set especificado
    isdisjoint() retorna True se os 2 sets são disjuntos
    issubset() retorna True se o set é subconjunto do segundo set
    issuperset() retorna True se o set contém o segundo set
    pop() remove (e retorna) um elemento arbitrário do set
    remove() remove o elemento especificado
    symmetric_difference() retorna o set com a diferença simétrica de dois sets
    symmetric_difference_update() insere a diferença simétrica desse set em outro
    union() retorna um set com a união dos sets
    update() atualiza o primeiro set com sua união com um ou mais sets

    Métodos de Dicionários (dictionaries)

    Método Descrição
    clear() remove todos os elementos from the dictionário
    copy() retorna uma cópia do dicionário
    fromchaves() retorna dicionário com chaves e valores especificados
    get() retorna o valor relativo a chave dada, ou valor default dado
    items() retorna uma lista contendo uma tupla para cada par chave:valor
    chaves() retorna lista com as chaves do dicionário
    pop() remove o elemento relativo à chave especificada
    popitem() remove o último par chave:valor inserido
    setdefault() retorna o valor relativo à chave dada. Se a chave não existe insere chave:valor
    update() Atualiza o dicionário com pares chave:valor dados
    valors() retorna uma lista com os valores do dicionário

    Métodos de Arquivos (files)

    Método Descrição
    open(arquivo, modo) Abre o arquivo (descrição dos parâmetros abaixo)

    Os seguintes valores são válidos para modo.

    Parâmetro Descrição
    r para leitura – O ponteiro é colocado no início do arquivo. Default.
    r+ para leitura e gravação. O ponteiro fica no início do arquivo.
    w apenas para gravação. Substitui arquivo existente, cria novo se o arquivo não existir.
    w+ escrita e leitura. Substitui arquivo existente, cria novo se arquivo não existir.
    rb para leitura em formato binário. O ponteiro fica no início do arquivo.
    rb+ para leitura e escrita em formato binário.
    wb+ para escrita e leitura em formato binário. Substitui o arquivo existente. Cria novo se não existir.
    a para anexar. O ponteiro fica no final do arquivo, se o arquivo existir. Cria novo se existir.
    ab um arquivo para anexar em formato binário. O ponteiro fica no final do arquivo. Cria novo se não existir.
    a+ para anexar e ler. O ponteiro fica no final do arquivo, se arquivo existir. Cria novo se não existir.
    ab+ anexar e ler em formato binário. O ponteiro no final do arquivo. Cria novo se não existir.
    x cria novo arquivo lançando erro se já existir.
    Método Descrição
    close() fecha o arquivo; sem efeito se arq. já está fechado
    detach() retorna o fluxo bruto separado do buffer
    fileno() retorna número inteiro diferente para cada arquivo aberto
    flush() descarrega no disco as alterações no buffer
    isatty() retorna True se o fluxo de arquivo é interativo
    next(arquivo) itera sobre arquivo, lançando erro no final
    read() retorna o conteúdo do arquivo
    readable() retorna True se o fluxo do arquivo pode ser lido
    readline() retorna uma única linha do arquivo
    readlines() retorna lista com todas as linhas do arquivo
    search() localiza item no arquivo e retorna sua posição
    searchable() retorna True se o arquivo é pesquisável, por ex. com seek()
    tell() retorna a posição atual do arquivo
    truncate([tamanho]) redimensiona (truncando) o arquivo para um tamanho especificado
    writable() retorna True se o arquivo pode receber gravações
    write() grava a string especificada no arquivo
    writelines() escreve a sequencia no arquivo. Qualquer objeto iterável composto por strings pode ser usado

    Métodos de OS

    Método Descrição
    chdir(“novaPasta”) mudar a pasta ativa para novaPasta. Se novaPasta = “..” vai para pasta mãe
    getcwd() ler diretório (pasta) atual
    listdir(“pasta”) lista arquivos e subpastas de “pasta”
    makedirs(sequencia) criar pastas com nomes em sequencia
    mkdir(“nomeDaPasta”) criar uma pasta
    remove(arquivo) apagar arquivo
    removedirs(caminho) apagar pastas recursivamente
    rmdir(“pastaRemover”) apagar pasta “pastaRemover”. Não pode ser pasta atual ou estar em uso por ouro processo
    rename(“nomeAntigo”,”nomeNovo”) renomear pasta “nomeAntigo” para “nomeNovo”
    uname() retorna dados sobre o sistema operacional, usuário e máquina usada

    Exceções

    Todas as exceções no Python se derivam da classe base BaseException. As exceções built-in que não provocam uma queda do sistema são todas derivadas de Exception, incluive as definidas pelo usuário. A tabela abaixo mostra uma hierarquia de classes das exceções, cada indentação representando um grupo de derivadas da classe acima. Por exemplo, a classe ZeroDivisionError tem a seguinte linha hierárquica:

    BaseException ⟶ Exception ⟶ ArithmeticError ⟶ ZeroDivisionError


    BaseException
    SystemExit
    KeyboardInterrupt
    GeneratorExit
    Exception
    StopIteration
    StopAsyncIteration
    ArithmeticError
    FloatingPointError
    OverflowError
    ZeroDivisionError
    AssertionError
    AttributeError
    BufferError
    EOFError
    ImportError
    ModuleNotFoundError
    LookupError
    IndexError
    KeyError
    MemoryError
    NameError
    UnboundLocalError
    OSError
    BlockingIOError
    ChildProcessError
    ConnectionError
    BrokenPipeError
    ConnectionAbortedError
    ConnectionRefusedError
    ConnectionResetError
    FileExistsError
    FileNotFoundError
    InterruptedError
    IsADirectoryError
    NotADirectoryError
    PermissionError
    ProcessLookupError
    TimeoutError
    ReferenceError
    RuntimeError
    NotImplementedError
    RecursionError
    SyntaxError
    IndentationError
    TabError
    SystemError
    TypeError
    ValueError
    UnicodeError
    UnicodeDecodeError
    UnicodeEncodeError
    UnicodeTranslateError
    Warning
    DeprecationWarning
    PendingDeprecationWarning
    RuntimeWarning
    SyntaxWarning
    UserWarning
    FutureWarning
    ImportWarning
    UnicodeWarning
    BytesWarning
    ResourceWarning
    🔺Início do artigo

    Bibliografia

    Consulte a bibliografia no final do primeiro artigo dessa série.

    Python: Sequências e Coleções


    Introdução

    No Python sequências são conjuntos de elementos ordenados que podem ser acessados em sua ordem (o que chamamos de iteráveis. Já vimos que strings são sequências. Por ex., print('Casa da Mãe Joana'[3]) resulta em 'a'.

    range(), na verdade não é uma função mas uma classe geradora de um iterador, como veremos mais tarde. Ela fornece objetos que produzem os elementos sob demanda, sem precisar guardar todos na memória.

    Outra sequência de uso comum é range, que é o tipo de objeto retornado pela “função” range(). Ela tem a seguinte sintaxe ou assinatura:

    range(inicio, fim, passo)

    onde inicio e passo são opcionais. Ela gera uma sequência de inicio até fim (exclusivo) com intervalo de passo.

    » faixa = range(2,20, 3)
    » print('o objeto é', faixa)
    ↳ o objeto é range(2, 20, 3)
    » print('o tipo do objeto é', type(faixa))
    ↳ o tipo do objeto é <class 'range'>
    » print('o segundo elemento é', faixa[1])
    ↳ o segundo elemento é 5
    
    # podemos iterar pelos itens de faixa
    » for t in faixa:
    »     print(t, end=' ')
    ↳ 2 5 8 11 14 17 
    
    Conteineres” são estruturas de dados que organizam e agrupam dados de acordo com seu tipo.

    Coleções são conteineres de dados. No módulo básico do Python existem quatro tipos de dados em coleções:

    • Lista (list): ordenada e mutável, pode ter membros duplicados.
    • Tupla (tuple): ordenada e imutável, pode ter membros duplicados.
    • Conjunto (set): não ordenada e não indexada, sem membros duplicados.
    • Dicionário (dictionary): não ordenada, mutável, sem membros duplicados.

    Existe um módulo chamado collections com outros tipos de conteineres com estruturas de dados mais específicas como, por exemplo, uma tupla nomeada e um dicionário ordenado.

    Listas

    Além dos objetos já vistos, muitos outros são pre-programados no núcleo básico do Python. A listas (lists) são sequências ordenadas de objetos que podem ser acessados por meio de seu índice (ou index), um marcador de sua posição. Os objetos que a compõem que não precisam ser do mesmo tipo, são delimitados por colchetes e separados por vírgulas.

    lista = [valor_0, …, valor_n]

    As listas são mutáveis, podem ser encolhidas ou expandidas e ter seus elementos substituídos. Exemplos de listas são dados a seguir:

    » lista1 = ['Maria', 25]
    » lista2 = ['José', 27]
    » lista3 = [lista1, lista2]
    » print(lista1[0], lista1[1])
    ↳ Maria 25
    
    # lista3 é uma lista de listas
    » print(lista3)
    ↳ [['Maria', 25], ['José', 27]]
    
    # o 2º elemento da 2ª lista é
    » print(lista3[1][1])
    ↳ 27
    

    Da mesma forma que sequências de caracteres (nas strings), elementos em listas e tuplas podem ser acessados por meio de seus índices. Fatias (ou slices) lista[i:j] se iniciam no i-ésimo elemento, até o j-ésimo, exclusive, de forma que len(lista[i:j]) = j-i. Índices negativos contam a partir do final.

    » lista4 = [10, 23, 45, 56, 67, 78, 89, 90]
    » lista4[1:3]
    ↳ [23, 45]
    
    # omitindo o 1º índice
    » lista4[:2]
    ↳ [10, 23]
    
    # omitindo o 2º índice
    » lista4[4:]
    ↳ [67, 78, 89, 90]
    
    # o último elemento
    » lista4[-1]
    ↳ 90
    
    » lista4[-4:-2]
    ↳ [67, 78]
    
    # len fornece o número de elementos na lista
    » len(lista4[3:7])
    ↳ 4
    
    # um 3º parâmetro indica o "passo"
    » lista4[::2]
    ↳ [10, 45, 67, 89]
    
    # um passo negativo indica contagem do fim para o início
    » lista4[::-1]
    ↳ [90, 89, 78, 67, 56, 45, 23, 10]
    


    Vimos que a ausência do 1º índice assume o início, a ausência do 2º assume o final. O 3º indica para pular um número de elementos.

    Uma função que retorna uma lista (ou outra sequência qualquer) pode ser diretamente indexada. Por ex., podemos construir uma função que retorna o mês abreviado em 3 letras. O índice do mês pode ser atribuído diretamente ao retorno da função:

    » def mes_abrev():
    »     m = ['jan','fev','mar','abr','mai','jun','jul','ago','set','out','nov','dez',]
    »     return m
    
    # para pegar o segundo mês:
    » mes_abrev()[1]
    ↳ 'fev'
    
    # para pegar o 1º trimestre:
    » mes_abrev()[:3]
    ↳ ['jan', 'fev', 'mar']
    
    # para pegar o último trimestre:
    » mes_abrev()[-3:]
    ↳ ['out', 'nov', 'dez']
    

    Assim como ocorre com strings, listas podem ser somadas (concatenadas) e multiplicadas por um número (repetidas). O efeito é o mesmo.

    » lista5 = [0, 1, 2, 3, 4]
    » lista6 = [10, 11, 12, 13, 14]
    » lista5 + lista6
    ↳ [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
    
    » lista5 * 2
    ↳ [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
    

    Uma lista pode ser inicializada vazia ou com um número dado de itens.

    » lista_vazia = []      # lista vazia    
    » lista_vazia
    ↳ []
    
    # elementos podem ser inseridos com o método append
    » lista_vazia.append(12)
    » lista_vazia.append(13)
    » lista_vazia
    ↳ [12, 13]
    
    # o mesmo efeito seria obtido com concatenação
    » lista_vazia.clear()     # a lista volta a ser vazia
    » lista_vazia += [12, 13]
    » lista_vazia
    ↳ [12, 13]
    
    # lista com 5 entradas
    » lista_none = [None]*5
    » lista_none
    ↳ [None, None, None, None, None]
    

    Sendo mutáveis listas podem ser alteradas in place tendo qualquer de seus valores trocados sem a necessidade de criação de nova lista. count(item) retorna quantas vezes item aparece na lista. index(item) retorna o índice onde item aparece.

    » palavras = ['palha', 'grande', 'casa', 'dado', 'pequeno',
                       'coisa', 'gado', 'fato', 'gato', 'lá' ]
    » print('A terceira palavra é ---| %s |---' % palavras[2] )
    ↳ A terceira palavra é ---| casa |---
    
    » palavras[2] = 'house'
    » print('A terceira palavra é ---| %s |---' % palavras[2] )
    ↳ A terceira palavra é ---| house |---
    
    # inserindo mais uma 'house'
    » palavras[3] = 'house'
    » palavras.count('house')
    ↳ 2
    
    # o índice de 'coisa'
    » palavras.index('coisa')
    ↳ 5
    
    # index retorna a 1ª ocorrência, quando existem mais de 1
    » palavras.index('house')
    ↳ 2
    

    Objetos podem ser inseridos em uma lista, na n-ésima posição, com lista.insert(n, obj). Um membro do lista pode ser removido com lista.pop(n). Esse método retorna o objeto removido. lista.remove(obj) também faz a remoção, sem retornar o objeto.

    # inserir um objeto na posição 3
    » palavras.insert(3, 'estrela')
    » print(palavras)
    ↳ ['pequeno', 'palha', 'lá', 'estrela', 'house', 'house', 'grande', 'gato', 'gado', 'fato', 'coisa']
    
    # pop extrai e retira elemento no índice dado
    » saiu = palavras.pop(3)
    » print(saiu)
    ↳ estrela
    
    # a lista fica sem esse elemento
    » print(palavras)
    ↳ ['pequeno', 'palha', 'lá', 'house', 'house', 'grande', 'gato', 'gado', 'fato', 'coisa']
    
    # se nenhum índice for fornecido o último elemento é removido (e retornado)
    » palavras.pop()
    ↳ coisa
    » print(palavras)
    ↳ ['pequeno', 'palha', 'lá', 'house', 'house', 'grande', 'gato', 'gado', 'fato']
    
    # remove não retorna o item removido
    » palavras.remove('house')
    » print(palavras)
    ↳ ['pequeno', 'palha', 'lá', 'house', 'grande', 'gato', 'gado', 'fato', 'coisa']
    
    # o item deve estar na lista ou exceção será lançada
    » palavras.remove('pedra')
    ↳ ValueError: list.remove(x): x not in list
    

    O método .sort() ordena itens de uma lista. Ele admite os parâmetros opcionais key, reverse que podem ser usados para fazer ordenamentos diferentes que o default. Por ex., se key = len o ordenamento se dará por comprimento das palavras, da menor para a maior. Se, além disso reverse = True o ordenamento se dará no sentido contrário. Também se pode definir uma função customizada para fazer essa ordenação.

    # ordenar
    » palavras.sort()
    » print(palavras)
    ↳ ['coisa', 'fato', 'gado', 'gato', 'grande', 'house', 'house', 'lá', 'palha', 'pequeno']
    
    # outra form de ordenar item em qualquer sequência é a função sorted()
    # que retorna a lista palavras ordenada sem alterar a lista original
    » sorted(palavras)    # nesse caso a lista já estava ordenada
    ↳ ['coisa', 'fato', 'gado', 'gato', 'grande', 'house', 'house', 'lá', 'palha', 'pequeno']
    
    # inverter a ordenação
    » palavras.reverse()
    » print(palavras)
    ↳ ['pequeno', 'palha', 'lá', 'house', 'house', 'grande', 'gato', 'gado', 'fato', 'coisa']
    
    » palavras.sort(key= len)
    » palavras
    ↳ ['lá', 'fato', 'gado', 'gato', 'coisa', 'house', 'house', 'palha', 'grande', 'pequeno']
    
    » palavras.sort(key= len, reverse=True)
    ↳ ['pequeno', 'grande', 'coisa', 'house', 'house', 'palha', 'fato', 'gado', 'gato', 'lá']
    

    Métodos das listas (lists)

    Método ação
    append() insere elementos na lista
    clear() remove todos os elementos na lista
    copy() retorna uma cópia da lista
    count() retorna o número de elementos com valor especificado
    extend() insere os elementos de outra lista (ou iterável) ao final da lista
    index() retorna o índice do 1º elemento com valor especificado
    insert() insere elemento em posição especificada
    pop() remove elemento em posição especificada por índice e retorna esse elemento
    remove() remove elemento em posição especificada por índice
    reverse() inverte a ordem da lista
    sort() ordena a lista

    Além desses as seguintes funções são úteis para se tratar com listas e outras sequências:

    Função Descrição
    cmp(x, y) compara dois valores
    len(seq) retorna o comprimento da sequência
    list(seq) converte uma sequência em lista
    max(args) retorna o valor máximo na sequência
    min(args) retorna o valor mínimo na sequência
    reversed(seq) retorna um iterador com os valores na sequência
    sorted(seq) retorna lista ordenada dos elementos na sequência
    tuple(seq) converte a sequência em uma tupla

    É possível testar se um determinado elemento é membro da lista:

    » 'gado' in palavras
    ↳ True
    
    » 'pedra' in palavras
    ↳ False
    
    » usuarios = [
    »   ['alberto', '1234'],
    »   ['mario', '6282'],
    »   ['maria', '5274'],
    »   ['joana', '9943']
    » ]
    » nome = input('Nome do usuário: ')
    » pin = input('Código PIN: ')
    » msg = 'Accesso liberado' if [nome, pin] in usuarios else 'Acesso negado'
    » print(msg)
    
    # uma entrada de dados que não corresponde a nenhuma entrada da lista
    ↳ Nome do usuário: lucas
    ↳ Código PIN: 1234
    ↳ Acesso negado
    
    # dados de usuário cadastrado
    ↳ Nome do usuário: joana
    ↳ Código PIN: 9943
    ↳ Accesso liberado
    

    Assim como existe uma função interna len() que retorna o comprimento de sequências, temos também max(), min() que retornam o maior e menor valor dentro da lista. Essas funções funcionam também com strings, considerada a ordem alfabética.

    » numeros = [100, 23, 987]
    » print(len(numeros))
    ↳ 3
    
    » print(max(numeros))
    ↳ 987
    
    » print(min(numeros))
    ↳ 23
    
    » print(max(2, 3))
    ↳ 3
    
    » print(min(9, 3, 2, 5))
    ↳ 2
    
    # min e max fazem comparações entre strings:
    » palavras
    ↳ ['pequeno', 'palha', 'lá', 'grande', 'gato', 'gado', 'fato', 'coisa']
    
    » min(palavras)
    ↳ 'coisa'
    
    » max(palavras)
    ↳ 'pequeno'
    

    Tuplas

    Tuplas (tuples) são sequências ordenadas e imutáveis de objetos, que podem ser acessados por meio de seu índice (index), um marcador de sua posição. Os objetos que a compõem não precisam ser mesmo tipo e são delimitados por parênteses e separados por vírgulas.

    lista = (valor_0, …, valor_n)

    Tuplas e listas se comportam de modo análogo, exceto em que tuplas são imutáveis (como as strings), não podem ser alteradas após sua criação. Existem razões técnicas para a existência de tuplas: operações com tuplas são mais rápidas que as com listas, e elas ocupam menos espaço na memória. Por isso não é raro que um método retorne uma tupla ou as demande como parâmetro. Daí a necessidade de conhecê-las.

    # é comum usar tuplas na formatação de strings:
    » print('Essa frase %s %d %s %s %s.' % ('lê', 4, 'palavras','na','tupla'))
    ↳ Essa frase lê 4 palavras na tupla.
    
    # elementos são acessados por índice
    » tupla = (0,1,2,3,4,'cinco')
    » tupla[5]
    ↳ 'cinco'
    
    # tuplas são imutáveis
    » tupla[5] = 5
    ↳ TypeError: 'tuple' object does not support item assignment
    
    # um erro é lançado se o índice não existe no objeto 
    » tupla[6]
    ↳ IndexError: tuple index out of range
    
    # tuplas são iteráveis
    » for t in tupla:
    »     print(t, end=', ')
    ↳ 0, 1, 2, 3, 4, cinco,
    
    # a função len funciona para tuplas
    » print(tupla[len(tupla) - 1])
    ↳ cinco
    
    # é o mesmo que
    » print(tupla[-1])
    ↳ cinco
    
    # tuplas podem conter outras tuplas como elemento
    » a = ('primeiro', 'segundo', 'terceiro')
    » b = (a, 'segundo elemento de b')
    » print('%s' % b[1])
    ↳ segundo elemento de b
    
    » print(b[0][0], b[0][1], b[0][2])
    ↳ primeiro segundo terceiro
    

    Métodos das Tuplas (tuples)

    Método Descrição
    count() retorna quantas vezes um valor especificado ocorre na tupla
    index() procura por valor especificado e retorna sua posição

    Conjuntos (sets)

    Conjuntos (sets) são coleções não ordenadas de objetos únicos e imutáveis. Conjuntos podem ser criados listando-se diretamente os elementos ou passando-se uma sequência pelo construtor set().

    conjunto = {e_1, …, e_n}

    conjunto = set(sequencia)

    Por exemplo, abaixo dois sets são criados e as operações de interseção, união e diferença são mostradas. Nenhuma dessa operações alteram os sets envolvidos.

    # dois sets são criados
    » X = {'a', 'b', 'c', 'd'}
    » Y = set('cdef')
    » print(X, Y)
    ↳ {'d', 'c', 'b', 'a'} {'f', 'c', 'd', 'e'}
    
    # a interseção é obtida com & (e comercial)
    » X & Y
    ↳ {'c', 'd'}
    
    # a união com |
    » X | Y
    ↳ {'a', 'b', 'c', 'd', 'e', 'f'}
    
    # a diferença de conjuntos
    » X-Y
    ↳ {'a', 'b'}
    
    # compreension list:
    » Z = {i**2 for i in [1,2,3,4] }
    » Z
    ↳ {1, 4, 9, 16}
    
    » type(Z)
    ↳ set
    
    # os elementos podem ser percorridos um a um
    » for i in Z:
    »     print(i, end=', ')
    ↳ 16, 1, 4, 9, 
    
    # testes para pertinência
    » 16 in Z
    ↳ True
    » 5 in Z
    ↳ False
    

    Alternativamente, as operações de interseção, união e diferença podem feitas com métodos da classe. Nesse caso o conjunto X fica alterado na operação.

    » X = {'a', 'b', 'c', 'd'}
    » Y = {'c', 'd', 'e', 'f'}
    
    # a união de dois sets (fica armazenada em X)
    » X.union(Y)
    » print(X)
    ↳ {'a', 'b', 'c', 'd', 'e', 'f'}
    
    » X = {'a', 'b', 'c', 'd'}
    » Y = {'c', 'd', 'e', 'f'}
    
    # elementos que não são comuns
    » X.symmetric_difference_update(Y)
    » print(X)
    ↳ {'a', 'b', 'e', 'f'}
    
    » X = {'a', 'b', 'c', 'd'}
    » Y = {'c', 'd', 'e', 'f'}
    # elementos em ambos os sets (interseção)
    » X.intersection_update(Y)
    » print(X)
    ↳ {'c', 'd'}
    

    Outros métodos de sets são mostrados.

    # o comprimento é o número de elementos
    » Z = {1, 4, 9, 16}
    » len(Z)
    ↳ 4
    
    # novos elementos podem ser adicionados
    » Z.add(133)
    » Z
    ↳ {1, 4, 9, 16, 133}
    
    # qualquer sequência pode ser adicionada
    » qualquer =[1, 100, 'coisa']
    » Z.update(qualquer)
    » Z
    ↳ {1, 100, 133, 16, 4, 9, 'coisa'}
    
    # um elemento pode ser removido
    » Z.remove('coisa')
    » Z
    ↳ {1, 4, 9, 16, 100, 133}
    
    # um erro é lançado no uso de remove se elemento não existe
    » Z.remove(45)
    ↳ KeyError: 45
    
    # também se pode remover com discard
    » Z.discard(133)
    » Z
    ↳ {1, 4, 9, 16, 100}
    
    # nenhum erro é lançado com discard
    » Z.discard(45)
    
    # para limpar todos os elementos do set
    » Z.clear()
    » Z
    ↳ set()
    
    # para apagar a variável
    » del Z
    » Z
    ↳ NameError: name 'Z' is not defined
    

    Suponha que temos uma lista longa de elementos muitos dos quais podem ser repetidos e queremos que essa lista não contenha repetições. Para remover repetições podemos transformar a lista em set (que não contém repetições) e depois retornando os dados para uma lista, caso isso seja necessário.

    # queremos remover as repetições de
    » lista_original = [1,2,3,4,4,3,2,1,6,6,7,8,8,8,9]
    » conjunto = set(lista_original)
    » lista_nova= list(conjunto)
    » lista_nova
    ↳ [1, 2, 3, 4, 6, 7, 8, 9]
    

    Seguem mais algumas ilustrações de uso de métodos de sets.

    # criamos um set usando compreensão
    » nSet = {i**3 for i in range(4)}
    » nSet
    ↳ {0, 1, 8, 27}
    
    # o método pop()  extrai um elemento qualquer e o retorna
    » print(nSet.pop())
    ↳ 0
    » print(nSet)
    ↳ {1, 27, 8}
    
    » print(nSet.pop())
    ↳ 1
    » print(nSet)
    ↳ {27, 8}
    
    # subset e superset
    » A = {1,2}
    » B = {1,2,3}
    » A.issubset(B)
    ↳ True
    
    » A.issuperset(B)
    ↳ False
    
    » B.issuperset(A)
    ↳ True
    

    Métodos dos conjuntos (set)

    Método Descrição
    add() insere elemento no set
    clear() remove todos os elementos do set
    copy() retorna cópia do set
    difference() retorna um set com a diferença entre 2 ou mais sets
    difference_update() remove elementos incluidos no segundo set
    discard() remove item especificado
    intersection() retorna o set interseção de 2 sets
    intersection_update() remove items do set não presentes no segundo set especificado
    isdisjoint() retorna True se os 2 sets são disjuntos
    issubset() retorna True se o set é subconjunto do segundo set
    issuperset() retorna True se o set contém o segundo set
    pop() remove (e retorna) um elemento arbitrário do set
    remove() remove o elemento especificado
    symmetric_difference() retorna o set com a diferença simétrica de dois sets
    symmetric_difference_update() insere a diferença simétrica desse set em outro
    union() retorna um set com a união dos sets
    update() atualiza o primeiro set com sua união com um ou mais sets

    Dicionários (dictionaries)

    Dicionários (dictionaries) são coleções de dados armazenados em pares chave: valor (key: value). A coleção é mutável, ordenada (a partir de Python 3.7) e não admite valores duplicados. A chave de um dicionário funciona como um índice que permite a recuperação do valor a ele associado. Eles têm a forma geral de

    dict = {key_1:value_1, …, key_n:value_n}

    As chaves são ordenadas e podem ser de diversos tipos. Valores podem ser de qualquer tipo e podem ser alterados. Por exemplo:

    # inicializando um dicionário
    » dic = {'casa':'house', 'cachorro':'dog', 'caneta':'pencil','carro':'car'}
    » dic
    ↳ {'casa': 'house', 'cachorro': 'dog', 'caneta': 'pencil', 'carro': 'car'}
    
    » print(type(dic)) 
    ↳ <class 'dict'>
    
    # acessando o valor com chave = 'caneta'
    » dic['caneta']
    ↳ 'pencil'
    
    # a função len retorna quantos pares existem no dicionário
    » len(dic)
    ↳ 4
    
    # chaves duplicadas são substituídas (a anterior é removida)
    » dic2 = {"nome" : "Pedro",
    »         "sobrenome" : "Alvarez",
    »         "idade" : 23,
    »         "idade" : 27
    »         }
    » idade = dic2['idade']
    » print(idade)
    ↳ 27
    

    Um dicionário pode ser criado recebendo uma lista de tuplas em seu construtor, como se mostra no primeiro exemplo abaixo. No exemplo seguinte tuplas são usadas como chaves.

    # t é uma lista de tuplas
    » t = [(0, 'zero'),(1, 'um'),(2, 'dois'),(3, 'tres'),(4, 'quatro')]
    » d = dict(t)
    
    » print(d)
    ↳ {0: 'zero', 1: 'um', 2: 'dois', 3: 'tres', 4: 'quatro'}
    » d[3]
    ↳ 'tres'
    
    # usando tuplas como chaves (listas não podem ser usadas)
    » tele = dict()
    » tele['Newton', 'Isaac'] = 1643
    » tele['Curie','Marie'] = 1867
    » tele['Einstein','Albert'] = 1879
    » tele['Hawking','Stephen'] = 1942
    
    » print(tele)
    ↳ {('Newton','Isaac'): 1643, ('Curie', 'Marie'): 1867, ('Einstein', 'Albert'): 1879, ('Hawking', 'Stephen'): 1942}
    # o índice é uma tupla
    » print(tele[('Curie', 'Marie')])
    ↳ 1867
    
    # o dicionário pode ser percorrido lendo-se os dois valores da tupla
    » for a, b in tele:
    »     print(b, a, tele[a,b] )
    
    ↳ Isaac Newton 1643
    ↳ Marie Curie 1867
    ↳ Albert Einstein 1879
    ↳ Stephen Hawking 1942
    
    # o dicionário também pode ser percorrido lendo-se uma tupla de cada vez
    » for t in tele:
    »     print(t, tele[t] )
    
    ↳ ('Newton', 'Isaac') 1643
    ↳ ('Curie', 'Marie') 1867
    ↳ ('Einstein', 'Albert') 1879
    ↳ ('Hawking', 'Stephen') 1942
    

    Se as chaves são strings simples elas podem ser especificadas como nomes de argumentos nomeados. No código abaixo uma lista de IDHs dos estados do sudeste é fornecida ao construtor.

    » idh = dict(
    »     SP=0.833,
    »     RJ=0.832,
    »     ES=0.802,
    »     MG=0.800
    » )
    » idh
    ↳ {'SP': 0.833, 'RJ': 0.832, 'ES': 0.802, 'MG': 0.8}
    

    Embora objetos de qualquer tipo (imutável) possam ser usados como chave, não existe um índice numérico associado aos elementos dos dicionários, além das próprias chaves. Dicionários mantém a ordem em que foram criados e sempre retornam o mesmo valor para cada chave.

    # Não índices existem associados às chaves dos dicionários
    » idh[1]
    ↳ KeyError: 1
    
    » d = {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
    » d[3]  # operação possível porque 3 é uma chave, não um índice
    ↳ 'd'
    

    Dicionários não são ordenados mas existe um objeto chamado OrderedDict que pode ser importado da classe collections que são.

    O dicionário criado abaixo, pessoa, tem uma lista associada ao valor irmaos e um dicionário associado ao valor filhos.

    # o dicionário abaixo é inicializado vazio e preenchido de modo incremental
    » pessoa = {}
    » pessoa['nome'] = 'Edvaldo'
    » pessoa['sobrenome'] = 'Santos'
    » pessoa['idade'] = 41
    » pessoa['profissao'] = 'dentista'
    » pessoa['filhos'] = {'João':3, 'Ana':7, 'Marco':10 }
    » pessoa['irmaos'] = ['Paulo', 'Eliane']
    
    » pessoa
    ↳ {'nome': 'Edvaldo',
    ↳  'sobrenome': 'Santos',
    ↳  'idade': 41,
    ↳  'profissao': 'dentista',
    ↳  'filhos': {'João': 3, 'Ana': 7, 'Marco': 10},
    ↳  'irmaos': ['Paulo', 'Eliane']}
    

    A idade do filho que se chama Marco é pessoa['filhos']['Marco'] e o primeiro irmão listado é pessoa['irmaos'][0].

    » pessoa['filhos']['Marco']
    ↳ 10
    » pessoa['irmaos'][0]
    ↳ 'Paulo'
    
    Você pode encontrar informação sobre Numpy: arrays e pandas: dataframes nesse site.

    Naturalmente que uma estrutura muito complexa de vários itens aninhados pode ser difícil de manipular, portanto o bom senso deve prevalecer na construção desses objetos. Para esse fim existem objetos pre-programados bem mais sofisticados, como os arrays do Numpy e os dataframes do pandas.

    Os valores associdas às chaves são mutáveis, podem ser alterados após a criação do dicionário. Chaves e valores podem ser percorridos separadamente ou como tuplas.

    » dic2 = {"nome" : "Pedro",
    »         "sobrenome" : "Cabral",
    »         "idade" : 27
    »         }
    
    # os valores são mutáveis
    » dic2['sobrenome'] = 'Alves'
    » dic2
    ↳ {'nome': 'Pedro', 'sobrenome': 'Alves', 'idade': 27}
    

    Podem ser usados os métodos dos dicionários: dic.keys(), que retorna as chaves, dic.values(), que retorna os valores, e dic.itens(), que retorna as chaves e valores, todos eles como objetos iteráveis.

    # as chaves podem ser lidas como um objeto iterável
    » chaves = dic2.keys()
    » chaves
    ↳ dict_keys(['nome', 'sobrenome', 'idade'])
    
    » for chave in chaves:
    »     print(dic2[chave], end=' ')
    ↳ Pedro Alves 27
    
    # os valores podem ser lidos em um objeto iterável
    » valores = dic2.values()
    » valores
    ↳ dict_values(['Pedro', 'Alves', 27])
    
    # percorrendo os valores
    » for t in valores:
    »     print(t, end=' ')
    ↳ Pedro Alves 27
    
    # percorrendo as chaves
    » for t in chaves:
    »     print(t, end = ' ')
    ↳ nome sobrenome idade
    
    # valores e chaves podem ser lidos como uma lista de tuplas
    » itn = dic2.items()
    » itn
    ↳ dict_items([('nome', 'Pedro'), ('sobrenome', 'Alves'), ('idade', 27)])
    
    # os pares podem ser percorridos
    » for (k,v) in dic2.items():
    »     print(k,v)
    ↳ nome Pedro
    ↳ sobrenome Alves
    ↳ idade 27
    

    O operador in verifica se um valor está presente entre as chaves ou valores.

    # verificando se uma chave está no dicionário
    » if 'idade' in dic2:
    »     print('idade é uma das chaves')
    ↳ idade é uma das chaves
    
    » print('peso é uma das chaves' if 'peso' in dic2 else 'valor não encontrado')
    ↳ valor não encontrado
    
    # verificando se um valor está presente no dicionário
    » print('Alves é um dos valores' if 'Alves' in dic2.values() else 'valor não encontrado')
    ↳ Alves é um dos valores
    

    Diversos métodos são pré-programados com a classe dos dicionários. Vemos abaixo o uso de pop(), popitem() e copy(). Uma lista de métodos pode ser vista no final dessa seção.

    # o método update serve para alterar o valor atribuído a uma chave
    # e/ou inserir novos pares chave:valor
    » dic2.update({'idade':19, 'sexo':'masc'})
    » dic2
    ↳ {'nome': 'Pedro', 'sobrenome': 'Alves', 'idade': 19, 'sexo': 'masc'}
    
    # um par chave valor pode ser inserido diretamente (como já fizemos acima)
    » dic2['telefone'] = '21-991111110'
    » dic2
    ↳ {'nome': 'Pedro',
    ↳  'sobrenome': 'Alves',
    ↳  'idade': 19,
    ↳  'sexo': 'masc',
    ↳  'telefone': '21-991111110'}
    
    # um par pode ser removido pela chave
    » dic2.pop('sexo')
    » dic2
    ↳ {'nome': 'Pedro',
    ↳  'sobrenome': 'Alves',
    ↳  'idade': 19,
    ↳  'telefone': '21-991111110'}
    
    # o método popitem() remove o último item inserido (após versão 3.7)
    » dic2.popitem()
    ↳ ('telefone', '21-991111110')
    
    # dic2 fica alterado
    » dic2
    ↳ {'nome': 'Pedro', 'sobrenome': 'Alves', 'idade': 19}
    
    # cópias de dicionários
    # a atribuição abaixo atribui a dic3 o mesmo objeto que dic2
    » dic3 = dic2
    » dic3['idade'] = 67
    
    # dic2 fica alterado
    » dic2
    ↳ {'nome': 'Pedro', 'sobrenome': 'Alves', 'idade': 67}
    
    # para criar cópia independente (criando um novo objeto) usamos
    » dic4 = dic2.copy()
    # ou, usando o instanciador do objeto
    » dic4 = dict(dic2)
    
    # agora dic4 pode ser alterado sem afetar dic2
    » dic4['idade'] = 3
    » dic2
    ↳ {'nome': 'Pedro', 'sobrenome': 'Alves', 'idade': 67}
    

    No código acima vemos que a atribuição dic3 = dic2 simplesmente associa o mesmo objeto à variável dict3. Para gerar um novo objeto, que pode ser alterado independentemente, usamos o método dic4 = dic2.copy().

    Dicionários podem ser aninhados ou seja, um dicionário pode conter outros dicionários como itens.

    # o construtor pode receber diretamente os dicionários
    » irmaos = {
    »          1:{'nome':'Maria' , 'nasc':1989},
    »          2:{'nome':'Marcos' , 'nasc':1991},
    »          3:{'nome':'Marla' , 'nasc':2000},
    »          }
    
    # talvez seja mais legível definir separadamente (o que é equivalente)
    » irmao1 ={'nome':'Maria' , 'nasc':1989}
    » irmao2 = {'nome':'Marcos' , 'nasc':1991}
    » irmao3 ={'nome':'Marla' , 'nasc':2000}
    » irmaos = {1:irmao1, 2:irmao2,3:irmao3}
    
    # em qualquer dos casos ficamos com o dicionário
    » irmaos
    ↳ {1: {'nome': 'Maria', 'nasc': 1989},
    ↳  2: {'nome': 'Marcos', 'nasc': 1991},
    ↳  3: {'nome': 'Marla', 'nasc': 2000}}
    
    # para acessar a data de nascimento do segundo irmão fazemos
    » irmaos[2]['nasc']
    ↳ 1991
    # lembrando que, nesse caso, 2 não é um índice mas a chave do dicionário.
    

    Vamos usar um dicionário para contar quantas letras existem em uma palavra ou frase. Para isso criamos um dicionário vazio e iteramos pelas letras da palavra, usando a letra como índice e contagem como valor. A operação palavra.lower().replace(' ', '') transforma todas as letras em minúsculas e elimina espaços. set(palavra) pega as letras da palavra sem repetições. O método string.count(sub) conta quantas vezes a substring sub aparece na string.

    # dicionário (contar quantas letras há em uma palavra)
    » def letras(palavra):
    »     ''' Recebe parametro palavra (string)
    » 
    »     Retorna dicionário {letra: contagem}
    »     onde contagem é o número de letras = letra
    »     Todas as letras são transformadas em minúscula
    »     espaços são ignorados
    »     '''
    »     palavra = palavra.lower().replace(' ', '')
    »     contagem = {}
    »     for t in set(palavra):
    »         contagem[t] = palavra.count(t)
    »     return contagem
    
    # quais e quantas letras
    » dic = letras('Oftalmotorrinolaringologista') 
    » print(dic)
    ↳ {'o': 6, 'f': 1, 't': 3, 'a': 3, 'l': 3, 'm': 1, 'r': 3, 'i': 3, 'n': 2, 'g': 2, 's': 1}
    
    # quantas letras 't'?
    » dic['t']
    ↳ 3
    
    # espaços são ignorados, pois foram removidos
    » print(letras('Rio de Janeiro'))
    ↳ {'r': 2, 'i': 2, 'o': 2, 'd': 1, 'e': 2, 'j': 1, 'a': 1, 'n': 1}
    

    O método dictionary.get(chave, default) retorna o valor relativo à chave ou o valor default, caso a chave não seja encontrada.

    » dic.get('t',0)
    ↳ 3
    # não existe a letra 'b'
    » dic.get('b',0)
    ↳ 0
    

    Compreensão de dicionários: Dicionários podem ser construídos com compreensão:

    » x = {i : i+2 for i in range(5)}
    » x
    ↳ {0: 2, 1: 3, 2: 4, 3: 5, 4: 6}
    
    # outro exemplo
    » lista = ('a', 'céu de brigadeiro', 'de', 'prateado', 'laguna', 'introvertido', 'a' )
    » dicio = {p : len(p) for p in lista}
    » dicio
    ↳ {'a': 1,
       'céu de brigadeiro': 17,
       'de': 2,
       'prateado': 8,
       'laguna': 6,
       'introvertido': 12}
    

    Métodos dos dicionários (dictionary)

    Método Descrição
    clear() remove todos os elementos do dictionário
    copy() retorna uma cópia do dicionário
    fromchaves() retorna dicionário com chaves e valores especificados
    get() retorna o valor relativo a chave dada, ou valor default dado
    items() retorna uma lista contendo uma tupla para cada par chave:valor
    keys() retorna objeto iterável com as chaves do dicionário
    pop() remove o elemento relativo à chave especificada
    popitem() remove o último par chave:valor inserido
    setdefault() retorna o valor relativo à chave dada. Se a chave não existe insere chave:valor
    update() Atualiza o dicionário com pares chave:valor dados
    values() retorna objeto iterável os valores do dicionário

    Continue lendo: Arquivos e Pastas .

    🔺Início do artigo

    Bibliografia

    Consulte a bibliografia no final do primeiro artigo dessa série.