Python: Biblioteca Padrão


Vimos que um usuário pode gravar suas classes em módulos para depois importá-las para dentro de seu código. Nas instalações de Python um conjunto de módulos vem instalado por padrão, outros podem ser baixados e instalados pelo usuário.

A biblioteca padrão do Python é extensa e inclui recursos diversos voltados para muitas áreas de aplicação. Alguns desses módulos são escritos em C para garantir velocidade de execução, outros em Python. Uma lista completa desses módulos pode ser encontrada em Python Docs: Standard Library. Muitos outros módulos podem ser encontrados em Pypi: The Python Package Index. Esse últimos devem ser baixados e instalados antes de sua utilização.

Lista de alguns módulos da biblioteca padrão:

Módulo Descrição (clique no ícone 📖 para ler a seção)
argparse processamento de argumentos em comando de linhas,
datetime 📖 classes para manipulação de datas e horas,
decimal tipo de dados decimais para aritmética de ponto flutuante,
doctest ferramenta para validar testes embutidos em docstrings de um módulo,
glob 📖 manipulação de listas de arquivos e pastas usando coringas,
io 📖 gerenciamento de IO (input/output),
math 📖 matemática de ponto flutuante,
os 📖 funções para a interação com o sistema operacional,
pprint habilidade pretty print para estruturas de dados,
random 📖 geração de números e escolhas aleatórios ,
re 📖 processamento de expressões regulares,
reprlib versão de repr() personalizada para exibições de contêineres grandes,
shutil 📖 interface com arquivos e coleções de arquivos,
statistics 📖 funções estatísticas (média, mediana, variância, etc.),
string 📖 métodos para formatação de strings,
textwrap formata parágrafos de texto para determinada largura de tela,
threading executa tarefas em segundo plano,
time 📖 acesso ao clock do computador,
timeit 📖 medidas de intervalo de tempo,
unittest conjunto de testes que podem ser mantidos em arquivo separado,
zlib, gzip, bz2, lzma, zipfile, tarfile suporte a formatos de compactação e arquivamento de dados.

Vamos exibir alguns dos métodos disponibilizados por esses módulos.

Módulo os

O módulo os foi tratado na seção Arquivos e pastas dessas notas.

Módulo io

Um buffer de dados (ou apenas buffer) é uma região de um armazenamento da memória física usado para armazenar dados temporariamente, antes de serem processados e gravados em memória permanente. Geralmente buffers são implementados pelo software na memória RAM e são normalmente usados ​​quando os dados são recebidos em taxa superior àquela em que podem ser processados. É o que ocorre em um spooler de impressora ou em streaming de vídeo online.

Os métodos read() e write(), que permitem a abertura de arquivos foram vistos em Arquivos e Pastas. No entanto os métodos em io são mais poderosos e flexíveis.

Arquivos binários e de texto

Arquivos podem ser abertos para escrita, para leitura e para acrescentar dados. O Python permite especificar dois tipos de arquivos: binário e texto. O modo binário é usado para lidar com todos os tipos de dados não textuais, como arquivos de imagem e arquivos executáveis. Qualquer arquivo é armazenado em forma binária. Mas, quando um arquivo de texto é aberto (e o comando de abertura informa que é um texto) esses bytes são decodificados para retornar objetos string. Já os binários retornam o conteúdo como sequências de bytes, sem decodificação.

Por exemplo, podemos abrir um arquivo binário e gravar nele a sequência algunsBytes = b'\xC3\xA9'. O prefixo b indica que essa é uma sequência binária.

# ilustrar gravação de arquivo binário e texto
» algunsBytes = b'\xC3\xA9'
  
» # abra um arquivo no modo 'wb' para escrever, no modo binário
» with open('./dados/arquivo.txt', 'wb') as arquivoBinario:
»     # escreve os bytes no arquivo
»     arquivoBinario.write(algunsBytes)
    
» # lendo esse arquivo no modo binário
» with open('./dados/arquivo.txt', 'rb') as f:
»     print(f.read())
↳ b'\xc3\xa9'

» # lendo esse arquivo no modo texto  (o código corresponde à letra "é")
» with open('./dados/arquivo.txt', 'r') as f:
»     print(f.read())
↳ é

Abrindo o arquivo no modo binário encontramos nossa sequência b’\xC3\xA9′. Quando verificamos esse arquivo no modo texto, usando o mode r ou abrindo em um editor de texto verificamos que está gravada a letra é (e com acento agudo). Fisicamente esse caracter fica gravado em disco (ou armazenado em RAM) como a sequência binária 11000011 10101001.

Outro exemplo curto consiste na leitura de um arquivo de imagem ./dados/Anaconda.png no modo binário, de leitura. O arquivo Anaconda.png deve estar na pasta indicada. Esses dados são depois escritos em ./dados/Novo.png, aberto no mode binário, para escrita. O resultado é a criação de Novo.png, uma imagem idêntica à original.

» with open('./dados/Anaconda.png', 'rb') as f:
»     with open('./dados/Novo.png', 'wb') as novo:
»         novo.write(f.read())

O módulo io é usado para gerenciar streams (fluxo de dados, I/O ou E/S em português). Esses fluxos podem ser, basicamente, de três tipos: dados de textos, binários ou RAW. Concretamente esses fluxos transferem objetos denominados arquivos.

Todos esses três tipos de objetos possuem atributos: podem ser só de leitura, gravação ou leitura e somente gravação. Eles podem ser acessados de modo de acesso aleatório (onde buscas são feitas em qualquer sentido, procurando por partes em qualquer local; ou de acesso apenas acesso sequencial, onde passos para trás não são permitidos, dependendo do tipo de dados lido.

As duas classes de uso mais comum são:

  • StringIO, operações em fluxos de strings e
  • BytesIO operações em fluxos de bites.

Encoding

Encoding ou codificação de texto é uma forma de representação de dados que contém letras, símbolos, números e marcações especiais (como quebra de linhas e tabulações). O forma de codificação mais comum para conversão de caracteres é a American Standard Code for Information Interchange (ASCII), desenvolvida no início da computação. Essa tabela contém apenas 128 posições, o que engloba apenas caracteres da língua inglesa. Para representar outros caracteres, com acentuação, de outras línguas e generalizar os símbolos se criou a codificação Unicode, que engloba todos os caracteres usados mundialmente. O UTF-8 (8-bit Unicode Transformation Format) é um tipo de codificação compacta para o código Unicode. Ele pode representar qualquer caractere universal padrão do Unicode, sendo também compatível com o ASCII. Por esta razão, está lentamente a ser adaptado como tipo de codificação padrão para e-mail, páginas web, e outros locais onde os caracteres são armazenados.

Exemplos de UNICODE e utf-8:

UNICODE caracter utf-8
U+00E0 à c3 a0
U+00E1 á c3 a1
U+00E2 â c3 a2
U+00E3 ã c3 a3
U+00E4 ä c3 a4

Quando se usa open() (e TextIOWrapper) para ler um arquivo é usada uma codificação especificada como sistema local. Sistemas Unix e seus derivados (como o Linux) usam por default a codificação UTF-8. No entanto existem sistemas (como o Windows) que não o fazem. Por isso é importante especificar explicitamente a codificação.

import io
» with io.open(filename,'r',encoding='utf8') as f:
»     text = f.read()

Para deixar o arquivo lido com a codificação local use encoding='locale'.

Classe io.StringIO()

O construtor de io.StringIO() permite a construção de um stream de dados tipo texto.

» import io
» arq = io.ioStringIO("Visitação aberta para o site\nPhylos.net")
» type(arq), type(arq.getvalue())
↳ (_ioStringIO, str)

» print(arq.getvalue()) # ou arq.read()
↳ Visitação aberta para o site
↳ Phylos.net

» # o objeto pode ser sobreescrito
» arq.write('Não deixem de visitar o')
↳ 23

» print(arq.getvalue())
↳ Não deixem de visitar o site
↳ Phylos.net

» arq.close()
» print(arq.getvalue()) 
↳ ValueError: I/O operation on closed file

StringIO constroi um objeto similar a um arquivo, residente na memória. Esse objeto pode ser usado como input ou output para funções que recebem arquivos padrões como argumentos. io.StringIO() inicializa um objeto vazio. Em qualquer dos casos o cursor sobre o arquivo está na posição 0. Os dados podem ser sobrepostos com io.StringIO('novo texto') que será inserido na posição do cursor (0, no caso). Após a inserção o cursor ficará na posição final do texto inserido, como mostrado no exemplo.

Depois do uso o objeto deve ser fechado com ioStr.close(). O objeto tipo arquivo é iterável como um arquivo usual. O fechamento após o uso garante a liberação de recursos usados. Nenhuma operação pode ser realizada sobre um arquivo fechado.

» f = io.StringIO()
» f = io.StringIO('linha 1\nlinha 2\nlinha 3')
» while True:
»     line = f.readline()
»     if (not line): break
»     else:print(line, end='')
» f.close() 

O método seek(n) permite apontar o cursor para o n-ésimo caracter. No código lemos apenas a primeira linha. Mesmo após o esgotamento das linhas seek(0) pode retornar o cursor para o início.

» with io.StringIO('linha 1\nlinha 2\nlinha 3') as f:
»     f.seek(3)
»     print(f.readline())
↳ ha 1
    
» with io.StringIO('linha 1\nlinha 2\nlinha 3') as f:
»     while True:
»         line = f.readline()
»         if (not line):
»             f.seek(0)
»             print(f.readline())  # só a 1ª linha é impressa
»             break    
↳ linha 1

O método arquivo.tell() informa o posição atual do cursor.

» with io.StringIO('Um cavalo! Um cavalo! Meu reino por um cavalo!') as f:
»     f.seek(22)
»     print('Da posição', f.tell(), 'em diante está o texto:', f.readline())
↳ Da posição 22 em diante está o texto: Meu reino por um cavalo!

O método file.getvalue() lê o arquivo inteiro independentemente da posição do cursor, enquanto file.read() lê à partir da posição do cursor até o final. O método file.truncate(n) corta o arquivo, mantendo apenas os caracteres de 0 até n, exclusive.

» file = io.StringIO('I\'ll be back!')
» print('1', file.getvalue())
» print('2', file.read())
» print('3', file.read())
» file.seek(0)
» print('4', file.read())

↳ 1 I'll be back!
↳ 2 I'll be back!
↳ 3 
↳ 4 I'll be back!

» file.truncate(7)
» print(file.getvalue())
↳ I'll be

Na linha 1 o arquivo foi lido mas o cursor continua no início. Em 2 o conteúdo é exibido e o arquivo é esgotado (cursor no final). Em 3 não há o que exibir. Em 4 o cursor é retornado ao início e o arquivo todo exibido.

O objeto StringIO tem os seguintes métodos e propriedade booleanas:

Métodos significado
StringIO.isatty() True se o arquivo é interativo,
StringIO.readable() True se o arquivo é está em modo leitura (readable),
StringIO.writable() True se o arquivo está no modo de escrita,
StringIO.seekable() True se o arquivo está no modo de acesso aleatório (random access),
Propriedade significado
StringIO.closed True se o arquivo está fechado.

A expressão arquivo na tabela significa um objeto tipo-arquivo, que não precisa estar gravado em disco. Um objeto que não está no modo de acesso aleatório não admite reposicionamento de cursor com o método seek(n).

» import io
» file = io.StringIO('Um texto qualquer de teste!')
» print("O arquivo é interativo?", file.isatty())
» print("O arquivo é de leitura?", file.readable())
» print("O arquivo está no modo de escrita?", file.writable())
» print("O arquivo admite buscas?", file.seekable())
» print("O arquivo está fechado?", file.closed)
» file.close()
» print("O arquivo foi fechado?", file.closed)
​
↳ O arquivo é interativo? False
↳ O arquivo é de leitura? True
↳ O arquivo está no modo de escrita? True
↳ O arquivo admite buscas? True
↳ O arquivo está fechado? False
↳ O arquivo foi fechado? True

Concluindo: io.StringIO fornece um meio conveniente de trabalhar com texto na memória usando métodos de arquivos como read(), write(). Com ele se pode construir strings longa com economia de desempenho em relação a outras técnicas de strings. Buffers de fluxo na memória também são úteis para testes uma vez que suas operações são mais ágeis que a gravação em um arquivo em disco.

Classe io.BytesIO

A classe io.BytesIO permite a inicialização de objetos de stream, puramente binários. Um arquivo aberto no modo binário não fará nenhuma conversão para caracteres de texto codificados, como o fazem os editores de texto, ou os arquivos abertos com io.StringIO. Eles são úteis para a manipulação de arquivos gerais, tais como imagens ou sons.

Um string prefixado com o caracter b é um string de bytes. O argumento de BytesIO deve ser sempre texto puro, em ASCII, ou um erro é lançado.

» import io
» arq = io.BytesIO(b'ç')
↳ SyntaxError: bytes can only contain ASCII literal characters.

» # usada com um argumento válido
» arq = io.BytesIO(b'Este texto, em bytes, sobre Python: \x00\x01')

» # o conteúdo é um string de bytes
» type(arq.getvalue())
↳ bytes

» # os caracteres \x00\x01 não são decodificados
» print(arq.getvalue())
↳ b'Este texto, em bytes, sobre Python: \x00\x01'

» # um arquivo fechado não pode ser manipulado
» arq.close()
» arq.getvalue()
↳ ValueError: I/O operation on closed file.

Um objeto BytesIO pode ser criado vazio e preenchido depois. BytesIO.write(b_str) insere b_str no final do objeto (faz um append). No exemplo abaixo dois strings comuns são transformados em bytes: '中文'.encode('utf-8') e 'criação'.encode('utf-8'), ambos envolvendo caracteres fora da tabela ASCII pura.

» arq = io.BytesIO()
» type(arq)
↳ io_BytesIO

» arq.write('中文'.encode('utf-8'))
» print(arq.getvalue())
↳ b'\xe4\xb8\xad\xe6\x96\x87'

» # encode() transforma em bytes os caracteres extendidos
» # no caso '中文' = 'chinês'
» type('中文'.encode('utf-8'))
↳ bytes

» # removendo o conteudo de arq e inserindo novo b_string
» arq = io.BytesIO()
» arq.write('criação'.encode('utf-8'))
» print(arq.getvalue())
↳ b'cria\xc3\xa7\xc3\xa3o'

» # write faz o append
» arq.write(b' <---')
» print(arq.getvalue())
↳ b'cria\xc3\xa7\xc3\xa3o <---'

Operações com io.BytesIO() são similares às de uma gravação de arquivo obtidas com file.open() e file.write(). Mas, embora criando um objeto com características de um arquivo, eles não precisam ser gravados em disco (ou outro meio de armazenamento) o que torna essas operações muito mais rápidas.

Uma comparação de velocidade entre gerar e manipular uma string com conteúdo binário e um objeto io.BytesIO() é feita abaixo.

» import io
» import time

» start = time.time()
» buffer = b''
» for i in range(0,100_000):
»     buffer += b'Oi Mundo!'
» print('1:', time.time() - start)

» start = time.time()
» buffer = io.BytesIO()
» for i in range(0,100_000):
»     buffer.write(b'Oi Mundo!')
» print('2:', time.time() - start)

» # o output será
↳ 1: 6.659011363983154
↳ 2: 0.012261390686035156

Se necessário podemos passar o conteúdo de um objeto io.BytesIO() para um arquivo ordinário e gravá-lo em disco.

    
» # criamos um buffer vazio e depois o preenchemos
» buffer = io.BytesIO()
» for i in range(0,100_000):
»     buffer.write(b'Oi Mundo!')
» # gravamos no disco esse buffer
» with open('./dados/Buffer.bin', 'wb') as arquivo:
»     arquivo.write(buffer.getbuffer())
» # um arquivo com 100000 linhas de 'Oi Mundo' é gravado no disco    

io.open()

Já vimos e usamos o método open() do módulo os. O módulo io também possui um método open().

» import io
» arquivo = io.open('./dados/Anaconda.png', 'rb', buffering = 0)
» print(arquivo.read())
» # o resultado está exibido abaixo (truncado)
↳ b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x1d\x00\x00\x01\x14\x08\x06\x00\x00\x00\x16e\xe2\xb3\x00\x00\x01\x84iCCPICC profile\x00\x00(\x91}\x91=H\xc3@\x1c\xc5_[E\xd1\xfa\x01f\x10q\xc8P\x9d,\x88\x8a8j\x15\x8aP!\xd4\n\xad:\x98\\\xfa\x05M\x1a\x92\x14\x17G\xc1\xb5\xe0\xe0\xc7b\xd5\xc1\xc5YW\x07WA\x10\xfc\x00qquRt\x91\x12\xff\x97\x14Z\xc4xp\xdc\x8fw\xf7\x1ew\xef\x80`\xad\xc44\xabm\x1c\xd0t\xdbL\xc6cb:\xb3*v\xbc\xa2\x1b\xbd\x10\xd0\x8f\x88\xcc,cN\x ...

O método io.open() é preferido para operações de E/S por ser interface de alto nível. Na verdade ele é um wrapperque se utiliza de os.open() mas retorna um objeto com mais propriedades e métodos.

RAW

O modo RAW (também chamado de IO sem buffer) é usada como um bloco de construção de baixo nível para fluxos binários e de texto. Esse modo raramente é útil manipular diretamente um fluxo do código do usuário. Se necessário um fluxo bruto pode ser criado abrindo-se um arquivo no modo binário e com o buffer desativado:

» f = open('myfile.jpg', 'rb', buffering=0)

Módulo shutil

O módulo shutil contém vários métodos que operam em arquivos e pastas, usados para copiar, apagar, entre outras funcionalidades. Vamos carregar os módulos os e shutil para experimentar alguns desses métodos. No Linux o sinal ‘./’ indica a pasta atual, ‘./Temp’, é uma subpasta de nome Temp.

Método significado
os.system() Executa um comando shell,
shutil.copy(origem, destino) copia arquivos,
shutil.copy2(origem, destino) copia arquivos com metadados,
shutil.move(origem, destino) move arquivos,
shutil.copyfile(origem, destino) copia arquivos,
shutil.copytree(pastaOrigem, pastaDestino) copia pasta e seu conteúdo
shutil.rmtree(pastaDestino) paga pastas recursivamente,
shutil.disk_usage(caminho) dados de uso do disco,
shutil.which(app) caminho de um aplicativo no path.

Exemplos de uso:
shutil.copy(origem, destino)
O método shutil.copy copia o arquivo origem para o destino, retornando uma exceção se a pasta de destino não existir.

shutil.copy2(origem, destino)
O método shutil.copy2 tem o mesmo efeito que o anterior, mas tenta copiar junto com o arquivo origem os seus metadados para o destino.

shutil.move(origem, destino)
O método shutil.move move o arquivo origem para o destino, retornando uma exceção se a pasta de destino não existir. O arquivo original deixa de existir.

» import os
» import shutil

» origem = './renomear.py'
» destino = './temp/renomear.py'

» # Vamos copiar o arquivo renomar.py para a paste temp de destino
» dest = shutil.copy(origem, destino)

» # um arquivo pode ser copiado em sua própria pasta
» shutil.copy('nomeAntigo.txt','nomeNovo.txt')

» # um arquivo pode ser renomeado
» shutil.move('nomeAntigo.txt','nomeNovo.txt')

» # o método retorna a pasta e arquivo de destino
» print('Pasta de destino:', dest)
↳ Pasta de destino: ./temp/renomear.py

» shutil.move('nomeAntigo.txt', , 'nomeNovo.txt')
↳ 'nomeNovo.txt'

» # considerando que nomeNovo.txt é o único arquivo na pasta
» os.listdir('./temp')
↳ ['nomeNovo.txt']

No Windows os caminhos devem ser escritos de forma compatível com o sistema.

» import shutil
» import os

» caminho = 'C:\\temp1\\temp2'
» print('A pasta atual contém os arquivos:') 
» print(os.listdir(caminho))
» # os arquivos são listados

» destino = 'C:\\temp1\\temp3'
» shutil.move('arquivo.txt',destino + '\\temp3\\arquivo.txt')

» print('A pasta de destino passa a ter os arquivos:') 
» print(os.listdir(destino))
» # uma lista é impressa, incluindo arquivo.txt

shutil.copyfile(origem, destino)
Um arquivo pode ser copiado com shutil.copyfile, origem é o caminho relativo ou absoluto da arquivo a ser copiado, destino o caminho do arquivo copiado.

shutil.copytree(pastaOrigem, pastaDestino)
O método shutil.copytree copia uma pasta inteira juntamente com todos os seus arquivos e outras pastas, de pastaOrigem para pastaDestino. A pasta de destino é criada na cópia ou uma exceção é lançada se a pasta ou os arquivos já existirem no destino.

» # copiando um arquivo da pasta ativa para Temp do usuário
» origem = './teste.txt'
» destino = '/home/usuario/Temp/testeNovo.txt'
» shutil.copytree(origem, destino)
	
» origem = '/home/usr/Projetos/'
» destino = '/home/usr/Projetos/temp2'

» shutil.copytree(origem, destino)
» print(os.listdir(destino))
» # uma lista dos arquivos no destino é exibida

shutil.rmtree(pastaDestino)
Para apagar pastas recursivamente podemos usar shutil.rmtree. Um erro será lançado de
a pasta pastaDestino não existir ou se não for uma pasta (se for um arquivo).

» print(os.listdir('./'))
↳ ['texto1.txt', 'alunos.csv', 'temp', 'temp2']

» # apagar pasta temp2 e todos os seus arquivos
» shutil.rmtree('./temp2')

» print(os.listdir('./'))
↳ ['texto1.txt', 'alunos.csv', 'temp']	

shutil.disk_usage(caminho)
shutil.which(app)
O método shutil.disk_usage informa dados de uso do disco sendo acessado e shutil.which(app) informa o caminho completo do aplicativo app que seria executado se estiver no PATH acessável no momento. O método retorna None se o app não existir ou não estiver no PATH.

» caminho = '/home/usuario/'
» estatistica = shutil.disk_usage(caminho)
» print(estatistica)
↳  usage(total=234684264448, used=172971024384, free=49720573952)

» comando = 'python'
» local = shutil.which(comando) 
» print(local)
↳ /home/guilherme/.anaconda3/bin/python

» comando = 'firefox'
» locate = shutil.which(comando) 
» print(local)
↳ /usr/bin/firefox

Biblioteca glob

O módulo glob fornece métodos para a busca de arquivos e pastas usando padrões de construção de nomes semelhantes aos usados pelo shell do Unix.

Método Descrição
glob.glob(padrao) retorna lista de arquivos que satisfazem o padrão em padrao,
glob.iglob(padrao) retorna um iterador contendo os nomes dos arquivos individuais,
glob.escape(padrao) Usado nos casos em que os nomes de arquivos usam caracteres especiais.

Usando o módulo glob é possível pesquisar por nomes de arquivo literais ou usar caracteres especiais, similares aos das expressões regulares (mas bem mais simples).

Sinal Descrição
* Asterisco: corresponde a zero ou mais caracteres,
? Interrogação: corresponde exatamente a um caractere,
[] Colchetes: intervalo de caracteres alfanuméricos,
[!] negativa: não contém os caracteres listados.

A sintaxe de glob é:

glob.glob(caminho, recursive = False)
glob.iglob(caminho, recursive = False)
onde caminho é o nome dos arquivo ou arquivos, usando os sinais acima e caminho relativo ou absoluto. Se recursive = True a busca é realizada recursivamente dentro das subpastas. O método iglob tem o mesmo efeito mas retorna um objeto iterável. Alguns exemplos são:

» import glob
» import os

» # retorna todos os arquivos na pasta /home/usuario/Temp/
» glob.glob('/home/usuario/Temp/*')
↳ ['/home/usuario/Temp/musica02.mp4',
↳  '/home/usuario/Temp/Musica01.mp3',
↳  '/home/usuario/Temp/teste.txt',
↳  '/home/usuario/Temp/Programa.py']

» # arquivos com nomes começados com letra minúscula 
» glob.glob('/home/usuario/Temp/[a-z]*')
↳ ['/home/usuario/Temp/musica02.mp4',
↳  '/home/usuario/Temp/teste.txt']

» # arquivos com nomes começados com letra maiúscula 
» glob.glob('/home/usuario/Temp/[A-H]*')
↳ ['/home/usuario/Temp/Programa.py',
↳  '/home/usuario/Temp/Musica01.mp3']

» # arquivos com extensão mp(0 até 9)
» glob.glob('/home/usuario/Temp/*.mp[0-9]')
↳ ['/home/usuario/Temp/Musica01.mp3',
↳   '/home/usuario/Temp/musica02.mp4']

» # podemos trocar a pasta ativa
» os.chdir('/home/usuario/Temp/')
» glob.glob('*')
» # retorna todos os arquivos na pasta

» # iglob retorna um iterável
» it = glob.iglob('*')
» for t in it:
»     print(t)
↳ /home/usuario/Temp/musica02.mp4
↳ /home/usuario/Temp/Musica01.mp3
↳ /home/usuario/Temp/teste.txt
↳ /home/usuario/Temp/Programa.py

» # usando a negativa
» # arquivos que não começam com m, M ou t.
» glob.glob('[!mMt]*')
↳ ['/home/usuario/Temp/Programa.py']

glob.escape(caminho, recursive = False)
O método escape permite o uso de caracateres especiais como _, #, $ no nome dos arquivos. O caracter especial é inserido por meio de glob.escape(char).

» caminho = '*' + glob.escape('#') + '*.jpeg'
» glob.glob(caminho)
↳ ['Foto#001.jpeg']

» caminho = 'A*' + glob.escape('_') + '*.gif'
» glob.glob(caminho)
↳ ['Abcd_001.gif']

Um exemplo de uso pode ser dado no código que procura e apaga arquivos temporários, com a extensão .tmp

» import glob
» import os

» # apague arquivos temporários, tipo *.tmp
» for t in (glob.glob('*.tmp')):
»     print('Apagando ', t)
»     os.remove(t)

Módulo math

O módulo math do Python contém diversas funções matemáticas, especialmente úteis em projetos científicos, de engenharia ou financeiros. Eles são um acréscimo aos operadores matemáticos básicos do módulo básico, como os operadores matemáticos integrados, como adição (+), subtração (-), divisão (/) e multiplicação (*).

As seguintes funções são encontradas no módulo:

Método Descrição
Funções trigonométricas e geométricas
math.sin() seno, ângulo em radianos,
math.cos() cosseno,
math.tan() tangente,
math.asin() arco seno,
math.acos() arco cosseno,
math.atan() arco tangente em radianos,
math.atan2() arco tangente de y/x em radianos,
math.sinh() seno hiperbólico,
math.cosh() cosseno hiperbólico,
math.tanh() tangente hiperbólica,
math.asinh() inverso do seno hiperbólico,
math.acosh() inverso do cosseno hiperbólico,
math.atanh() inversa da tangente hiperbólica,
math.degrees() ângulo convertido de radianos para graus,
math.radians() ângulo convertido de grau para radianos,
math.dist() distância euclidiana entre 2 pontos p e q,
math.hypot() norma euclidiana, distância da origem,
Exponencial e logarítmica
math.pow() potência, xy,
math.erf() função de erro de um número,
math.erfc() a função de erro complementar de um número,
math.exp() exponencial de x, ex,
math.expm1() exponencial de x menos 1, ex-1,
math.frexp() mantissa e expoente de número especificado,
math.log() logaritmo natural, ln(x),
math.log10() logaritmo de base 10, log10(x),
math.log1p() logaritmo natural de 1 + x, , ln(1+x),
math.log2() logaritmo de base 2, log2(x),
math.ldexp() inverso de math.frexp() que é x×2i,
math.gamma() função gama,
math.lgamma() gama do log de x,
Arredondamentos e truncamentos
math.ceil() número arredondado para o maior inteiro mais próximo,
math.floor() número arredondado para o menor inteiro mais próximo,
math.copysign() float, valor do primeiro parâmetro com o sinal do segundo parâmetro,
math.trunc() número truncado, parte inteira,
math.fabs() valor absoluto,
Análise combinatória
math.comb() quantas formas de k itens de n sem repetição e ordem,
math.perm() permutações, maneiras de escolher k itens de n com ordem, sem repetição,
math.factorial() fatorial,
Funções sobre iteráveis
math.prod() produto dos elementos de um iterável,
math.fsum() soma dos elementos de um iterável,
Outras funções
math.fmod() resto da divisão x/y,
math.remainder() resto da divisão x/y,
math.gcd() mdc, maior divisor comum de inteiros,
math.lcm() mdc, mínimo múltiplo comum de inteiros,
math.sqrt() raiz quadrada,
math.isqrt() raiz quadrada arredondada para o menor inteiro mais próximo,
Testes Lógicos
math.isclose() booleano, se dois valores estão próximos,
math.isfinite() booleano, se um número é finito,
math.isinf() booleano, se um número é infinito,
math.isnan() booleano, se um valor é NaN.

As seguintes constantes são carregadas:

Constante valor
math.e número de Euler (2.7182…),
math.inf infinito positivo de ponto flutuante,
math.nan NaN positivo de ponto flutuante,
math.pi π = 3.1415…,
math.tau τ = 2 π.

Alguns exemplos de operações:

» import math
» math.cos(0)        # 1.0
» math.sin(0)        # 0.0
» math.ceil(1.17)    # 2
» math.floor(1.75)   # 1
» math.fabs(-1.75)   # 1.75
» math.comb(7,3)     # 35,   comb(n,k) = n! / (k! * (n - k)!) se k <= n, 0 se  k > n
» math.copysign(3.7,-4.5) # -3.7
» math.fmod(125,3)   # 2.0
» math.fsum([1.2, 2.3, 3.4, 4.5]) # 11.4  (qualquer iterável no argumento)
» math.gcd(12, 8)    # 4 mdc (máximo divisor comum)
» math.pow(2,15)     # 32768.0
» math.sqrt(135)     # 11.61895003862225
» p, q = [1,2,3], [3,2,1]
» math.dist(p,q)     # 2.8284271247461903
» math.hypot(1,2,3)  # 3.741657386773941

Módulos cmath e Numpy

As funções do módulo math são todos definidas para parâmetros “reais” (números de ponto flutuante, pra ser mais preciso). Elas não podem receber números complexos como parâmetros. Para operações com complexos existe o módulo cmath que implementa muitas das mesmas funções. Mais informações sobre esse módulo em Python Docs.


Para operações matemáticas mais sofisticadas existem várias bibliotecas de uso específico. Uma das mais usadas é Numerical Python ou NumPy, usado principalmente na computação científica e no tratamento de dados. NumPy não faz parte da biblioteca padrão e deve ser instalado separadamente.

Além de diversas funções semelhantes às do módulo math, NumPy é capaz de realizar operações alto desempenho com matrizes n-dimensionais. Frequentemente NumPy é utilizado junto com o pandas (veja artigo) para a análise de dados, treinamento de máquina e inteligência artificial.

Módulos statistics e random

O módulo statistics contém diversos métodos para o cálculo de funções estatísticas sobre um conjunto de dados numéricos. A maioria dessas funções podem receber como parâmetros dados int, float, Decimal e Fraction.

Método descrição
Médias e medidas de localização central
mean() Média aritmética dos dados,
fmean() Média aritmética rápida, de ponto flutuante,
geometric_mean() Média geométrica dos dados,
harmonic_mean() Média harmônica dos dados,
median() Mediana,
median_low() Mediana baixa,
median_high() Mediana alta,
median_grouped() Mediana ou 50-ésimo percentil de dados agrupados,
mode() Moda (o valor mais comum) de dados discretos,
multimode() Lista de modas (valores mais comuns) de dados discretos,
quantiles() Divide dados em intervalos de igual probabilidade,
Medidas de espalhamento
stdev() Desvio padrão,
pstdev() Desvio padrão,
variance() Variância da amostra,
pvariance() Variância de toda a população.

Alguns exemplos simples de cálculos estatísticos:

» from statistics import *
» mean([1.2, 2.3, 3.4, 4.5, 5.6])
↳ 3.4

» mode([1.2,2.1,2.1,3.8,4.6])
↳ 2.1

» # mode pode ser usada com dados nominais
» mode(['carro', 'casa', 'casa', 'carro', 'avião', 'helicóptero', 'casa'])
↳ 'casa'

» # multimode retorna lista com valores mais frequentes, na ordem em que aparecem 
» multimode('aaaabbbccddddeeffffgg')
↳ ['a', 'd', 'f']

» multimode([1,1,1,2,3,3,3,4,5,5,6,6,6])
↳ [1, 3, 6]

» dados = [1.5, 2.5, 2.5, 2.75, 3.25, 4.75]
» pstdev(dados)
↳ 0.986893273527251

» # statistics.pvariance(data, mu=None): variação populacional de dados
» # se mu (ponto central da média) não for fornecido ele é calculado como a média
» # se for conhecido pode ser passado como parâmetro

» mu = mean(dados)
» pstdev(dados, mu)
↳ 0.986893273527251

» from fractions import Fraction as F
» dados = [F(1, 4), F(5, 4), F(1, 2)]
» pvariance(dados)
↳ Fraction(13, 72)

» pvariance([1/4, 5/4, 1/2])   # o mesmo resultado seria obtido com floats:  13/72 =
↳ 0.18055555555555555

» # desvio padrão
» dados = [1.25, 3.12, 2.15, 5.68, 7.23, 3.01]
» stdev(dados)
↳ 2.2622643523691037	

Muitos outras bibliotecas existem para cálculo estatístico no Python, como NumPy e SciPy, alémm de outros aplicativos como Minitab, SAS, Matlab e R (nesse site).

Módulo random

Números aleatórios ou randômicos gerados em computadores são números produzidos à partir de uma semente ( ou seed) por meio de algoritmos que visam produzir valores o mais espalhados e imprevisíveis quanto possível. Como os algoritmos são todos determinísticos a distribuição não será de fato aleatória, a menos que o computador possa estar conectado a um evento externo realmente randômico (como o decaimento de uma substância radioativa).

Para fins práticos, tais como uso em jogos, teste e geração de chaves criptográficas, a pseudo aleatoriedade pode ser suficiente.

O módulo random contém diversos métodos para geração desses números e de escolhas entre listas.

Método descrição
seed() inicilaliza o gerador de números aleatórios,
getstate() retorna o estado atual do gerador,
setstate() restaura o estado do gerador,
getrandbits() retorna um número representando bits aleatórios,
randrange() retorna um número aleatório dentro do intervalo,
randint() retorna um inteiro aleatório dentro do intervalo,
choice() retorna um elemento aleatório da sequência dada,
choices() retorna uma lista de elementos aleatórios da sequência dada,
shuffle() embaralha os elementos de lista, retorna None,
sample() retorna uma amostra da sequência,
random() retorna número de ponto flutuante entre 0 e 1,
uniform() retorna float no intervalo,
triangular() retorna float no intervalo, com possibilidade de determinar o ponto central.

Os métodos abaixo retornam um número de ponto flutuante (float) entre 0 e 1 baseado na distribuição mencionada:

Método distribuição
betavariate() Beta,
expovariate() Exponential,
gammavariate() Gamma,
gauss() Gaussiana,
lognormvariate() log-normal,
normalvariate() normal,
vonmisesvariate() de von Mises,
paretovariate() de Pareto,
weibullvariate() de Weibull.

O método random() retorna um número randômico entre 0 e 1 (no intervalo [0, 1)).

seed(semente) é usado para inicializar o gerador. A partir de uma determinada inicialização os números gerados sempre se repetem, o que é útil para testes, em execuções repetidas do código. Se nenhuma semente for fornecida a semente default é a hora atual do sistema.

» # número entre 0 (inclusive) e 1 (exclusive)
» random.random()
↳ 0.15084917392450192

» # a semente inicializa o gerador
» # o bloco seguinte sempre gera os mesmos números
» random.seed(10)
» for t in range(5):
»     print(random.random(), end=' ')
↳ 0.5714025946899135 0.4288890546751146 0.5780913011344704 0.20609823213950174 0.81332125135732     	

» # inteiros entre 1 e 10 (inclusive)
» for t in range(10):
»     print(int(random.random()*10+1), end=' ')
↳ 3 9 8 9 10 3 9 8 5 4

» # o mesmo se pode conseguir com randint
» for t in range(10):
»     print(random.randint(1,10), end=' ')
↳ 4 1 6 1 5 3 5 2 7 10 

O método choice(lista) retorna um ítem escolhido aleatoriamente entre membros de uma lista, tupla ou string. sample(lista, k) escolhe k elementos da lista.

» import random
» lista = [123, 234, 345, 456, 567, 678,789]
» print(random.choice(lista))
↳ 789

» lista = ['banana', 'laranja', 'abacaxi', 'uva', 'pera']
» print(random.choice(lista))
↳ abacaxi

» # escolhe um elemento de uma string
» texto = 'Phylos.net'
» print(random.choice(texto)	
↳ h

» # podemos simular o experimento de 1000 dados jogados
» milDados = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0}
» for t in range(1000):
»     milDados[random.choice([1,2,3,4,5,6])] += 1
» for t in range(1,7,2):
»     print('Lado {}: {} \t Lado {}: {}'.format(t, milDados[t], t+1, milDados[t+1]))
↳ Lado 1: 178 	 Lado 2: 171
↳ Lado 3: 160 	 Lado 4: 166
↳ Lado 5: 158 	 Lado 6: 167

» # o método sample(lista, n) escolhe n elementos da lista
» lista = [i for i in range(10,100,5)]
» print(lista)
↳ [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
» print(random.sample(lista, 4))
↳ [20, 90, 95, 55]

» lista = ['banana', 'laranja', 'abacaxi', 'uva', 'pera']
» random.sample(lista, 2)
↳ ['abacaxi', 'banana']

randrange(inicio, fim, passo) retorna um aleatório entre inicio (inclusive) e fim (exclusive) com passos de passo, um parâmetro opcional com default de passo = 1. Todos os parâmetros são inteiros.

uniform(inicio, fim) retorna um float aleatório entre inicio e fim (ambos inclusive). Os parâmetros podem ser floats.

» # randrange(5, 10, 2) só pode retornar 5, 7 ou 9
» print(randrange(5, 10, 2))
↳ 9

» for t in range(5):
»     print(random.randrange(0, 4), end=' ')
↳ 0 3 3 1 2     

» # uniform lida com floats
» for t in range (5):
»     print(random.uniform(3.14, 3.16), end=' ')
↳ 3.1561382140695495 3.1525027738382234 3.152682503779535 3.1532559087053946 3.1411872204770708     

Aviso importante: Os pseudo geradores desse módulo não devem ser usados para gerar números (pseudo) aleatórios para fins de segurança, tais como a geração de chaves de criptografia, gerenciamento de segurança de passwords e autenticação e tokens de segurança. Para essa finalidade use o módulo secrets.

Datas e calendários

Para trabalhar com datas podemos importar o módulo datetime.

Uma das classes definidas nesse módulo é datetime que tem os métodos datetime.now(), a data e hora do momento, e e datetime.today(), a data apenas, ambas de acordo com o ajuste do computador. Um objeto de data contém o ano (year), mês (month), dia (day), hora (hour), minuto (minute), segundo (second), e microsegundo (microsecond).

O módulo datetime contém as classes date, time, datetime e timedelta.

» import datetime as dt

» # datetime.now() retorna a data e hora do momento
» d = dt.datetime.now()
» print(d)
↳ 2021-06-05 16:27:46.444763

» print('%d/%d/%d' % ( x.day, x.month, d.year))
↳ 5/6/2021

» # usando o método format de strings
» print('Ano: {}, Mẽs: {}, Dia: {}'.format(d.day, d.month, d.year))
↳ Ano: 7, Mẽs: 6, Dia: 2021

# ou
» print('{d.day}/{d.month}/{d.year}'.format(d=d))
↳ 7/6/2021

» print('minutos: %d, segundos:%d,%d' % ( d.minute, d.second, d.microsecond))
↳ minutos: 27, segundos:46,444763

» # usando o método format de strings:
» print('minutos: {}, segundos:{},{}'.format(d.minute, d.second, d.microsecond))
↳ minutos: 1, segundos:56,516050

» # ou
» print('{d.minute}min:{d.second},{d.microsecond}s'.format(d=d))
↳ 1min:56,516050s

Para inicializar uma variável com um valor qualquer usamos o construtor da classe datetime. Horas, minutos e seguntos, se não fornecidos, são ajustados para zero por default.

Uma data pode ser criada à partir um timestamp, que é o número de segundos decorridos desde 01 de janeiro de 1970 (UTC), usando o método fromtimestamp().

» # inicializando uma data
» d2 = dt.datetime(1996, 6, 14)
» print(d2)
↳ 1996-06-14 00:00:00

» # data de um timestamp
» ttp = dt.datetime.fromtimestamp(1613000000)
» print(ttp)
↳ 2021-02-10 20:33:20

Para conseguir uma formatação mais minuciosa de datas e hotas podemos usar o método strftime que converte um data em string de acordo com alguns parâmetros de valores pré-estabelecidos. Objetos das classes date, datetime e time todos possuem o método strftime(padrao) para criar uma representação de string representando seu valor.

Por outro lado o método datetime.strptime() constroi um objeto datetime partindo de uma string, representada em um padrão.

» d = dt.datetime.now()
» print(d.strftime('%d/%m/%y'))
↳ 07/06/21

» print(d.strftime('%d/%m/%Y')) 
↳ 07/06/2021

» print(d.strftime('%b'))   # sistema em inglês
↳ Jun

» print(d.strftime('%A, %d de %B, dia %j do ano de %Y')) 
↳ Monday, 07 de June, dia 158 do ano de 2021

A representação de horas segue o mesmo padrão:

» from datetime import time
» # time(hour = 0, minute = 0, second = 0) default
» a = time()
» print('a =', a)
↳ a = 00:00:00

» # time(hour, minute and second)
» b = time(8, 14, 15)
» print('b =', b)
↳ b = 08:14:15

» # time(hour, minute and second)
» c = time(hour = 8, minute = 14, second = 15)
» print('c =', c)
↳ c = 08:14:15

» # time(hour, minute, second, microsecond)
» d = time(8, 14, 15, 34567)
» print('d =', d)
↳ d = 08:14:15.034567

» print(b.strftime('Hora: %I%p, Minuto:%M, Segundo: %S '))
↳ Hora: 08AM, Minuto:14, Segundo: 15 

A diferença entre datas, calculadas com objetos datetime ou date é um objeto timedelta. O número de segundos em um intervalo é dado pelo método total_seconds().

» # diferença entre datas feitas entre objetos datetime e date 
» from datetime import datetime, date

» t1 = date(year = 2021, month = 6, day = 7)
» t2 = date(year = 1957, month = 6, day = 28)
» t3 = t1 - t2
» print('Diferença entre t1 e t2 =', t1 - t2)
↳ Diferença entre t1 e t2 = 23355 days, 0:00:00

» t4 = datetime(year = 2018, month = 7, day = 12, hour = 3, minute = 19, second = 3)
» t5 = datetime(year = 2021, month = 2, day = 12, hour = 1, minute = 15, second = 1)
» print('Diferença entre t4 e t5 =', t4 - t5)
↳ Diferença entre t4 e t5 = -946 days, 2:04:02

» print("type of t3 =", type(t3))
↳ type of t3 = <class 'datetime.timedelta'>
» print("type of t6 =", type(t6))
↳ type of t3 = <class 'datetime.timedelta'>

A diferença entre datas e horas pode ser calculada diretamente entre objetos timedelta.

» from datetime import timedelta
» t1 = timedelta(weeks = 2, days = 5, hours = 1, seconds = 33)
» t2 = timedelta(days = 4, hours = 11, minutes = 4, seconds = 54)
» deltaT = t1 - t2
» print("Diferença: ", deltaT)
↳ Diferença:  14 days, 13:55:39

» print("Segundos decorridos =", deltaT.total_seconds())

O método datetime.strptime(formato, data) tem o efeito oposto. Dada uma string contendo uma data escrita de acordo com formato ele retorna a data correspondente.

» from datetime import datetime
» data_string = '21 jun, 18'
» data1= datetime.strptime(data_string, '%d %b, %y')
» print('data =', data1)
↳ data = 2018-06-21 00:00:00

» data_string = '21 June, 2018'
» data2 = datetime.strptime(data_string, '%d %B, %Y')
» print('data =', data2)        
↳ data = 2018-06-21 00:00:00

Abaixo uma lista com os valores de parâmetros de formatação de strftime e strptime. Os exemplos são para a data datetime.datetime(2013, 9, 30, 7, 6, 5).

%Znome do fuso horário.,%jdia do ano, número preenchido com zero.273,
%-jdia do ano, número.(p) 273,%Unúmero da semana no ano (domingo é dia 1), número preenchido com zero.39,%Wnúmero da semana do ano (segunda-feira é dia 1), número.39,%crepresentação completa de data e hora (l).Seg, 30 de setembro 07:06:05 2013,%xrepresentação completa de data apenas (l).30/09/13,%Xrepresentação completa de tempo (l).07:06:05,%% o caracter literal ‘%‘.

Código Significado Exemplo
%a dia da semana abreviado (l). seg,
%A dia da semana completo (l). Segunda-feira,
%w dia da semana, numerado: 0 é domingo e 6 é sábado. 1,
%d dia do mês preenchido com zero. 30,
%-d dia do mês como um número decimal. (p) 30,
%b nome do mês abreviado (l). Set,
%B nome do mês, completo (l). setembro,
%m número do mês preenchido com zero. 09,
%-m número do mês. (p) 9,
%y ano sem século como um número decimal preenchido com zero. 13,
%Y ano com século como um número decimal. 2013,
%H hora (em 24 horas) como um número decimal preenchido com zero. 07,
%-Hv hora (em 24 horas) como um número decimal. (p) 7,
%I hora (em 12 horas) como um número decimal preenchido com zero. 07,
%-I hora (em 12 horas) como um número decimal. (p) 7,
%p AM ou PM (l). AM,
%M minuto, número preenchido com zero. 06,
%-M minuto, número. (p) 6,
%S segundos, número preenchido com zero. 05,
%-S segundo como um número decimal. (p) 5,
%f microssegundo, número preenchido com zeros à esquerda. 000000,
%z deslocamento UTC no formato + HHMM ou -HHMM.

Códigos marcados com (l) tem resultado dependente do ajuste local para datas. Os marcados com (p) são variáveis de acordo com a a plataforma.

Módulo timeit

O módulo timeit contém métodos úteis para medir o tempo decorrido na execução de um bloco de código. Diferentes modos de implementação de um código podem ser executados em tempos muito diferentes e medir esse tempo, em execuções mais longas, pode ser uma ótima forma de decidir por um otimização.

O método principal é
timeit.timeit(stmt=’codigo1′, setup=’codigo2′, timer=<timer>, number=1000000, globals=None)
onde codigo1 é uma string com as linhas de cógigo cujo tempo de execução se quer medir, codigo2 é string com o cógigo a ser executado previamente, <timer> é o timer a ser usado, number é o número de repetições da execução, e globals especifica o namespace onde será rodado o código.

No exemplo abaixo timeit.timeit('123456 + 654321', number=100_000) mede o tempo de execução da soma 123456 + 654321, repetida 100_000 vezes. O resultado da medida é retornado em segundos, no caso de t = 8.7 x 10-9 seg.

» import timeit
» timeit.timeit('123456 + 654321', number = 100_000)
↳ 0.000865324996993877
» timeit.timeit('123456 + 654321', number = 1_000_000)
↳ 0.008846134001942119

» # o código é realmente executado (evite outputs extensos)
» timeit.timeit('print("-", end=" ")', number = 10)
↳ - - - - - - - - - - 
↳ 0.0006212080006662291

Lembrando, podemos usar o underline como separador de milhares, apenas para facilitar a leitura de números longos. Vemos que realizar a mesma soma 10 vezes mais aumento o tempo de execução.

Suponha que queremos comparar o tempo de construção de uma tupla e uma lista usando o mesmo procedimento de listas de compreensão.

» # lista e tupla com inteiros de 0 a 1000, apenas múltiplos de 5
» lista = [i for i in range(1000) if i%5==0]
» tupla = (i for i in range(1000) if i%5==0)
» # qual deles é mais rápido?

» codigo1 = '''
» lista = [i for i in range(1000) if i%5==0]
» '''
» codigo2 = '''
» tupla = (i for i in range(1000) if i%5==0)
» '''
» # medimos o tempo de construção desses 2 objetos
» t1 = timeit.timeit(codigo1, number = 1000)
» print(t1)
↳ 0.07810732900179573

» t2 = timeit.timeit(codigo2, number = 1000)
» print(t2)
↳ 0.000657964003039524

» # comparando
» print('t1 = {} t2'.format(t1/t2))
» t1 = 118.71064167792143 t2

Vemos pelo último teste que a construção da tupla é mais de 100 vezes mais rápida que a da lista.

O tempo medido varia entre experimentos diferentes pois depende de uma série de fatores tais como quais os processos o computador já tem em execução e qual tempo está disponivel para o processamento do Python. É claro que varia também para máquinas diversas com velocidades de processador e memória disponível diferentes.

Qualquer bloco de código pode ser inserido para parâmetro de timeit, transformado em uma string. Também podemos passar funções como parâmetro.

» timeit.timeit('''
» import math
» listaFat = []
» def fatoriais(n):
»     for t in range(n):
»         listaFat.append(math.factorial(t))
»     print(listaFat)
» ''', number = 1_000_000)
↳ 0.28315910099991015

» # passando funções como parâmetros
» # soma dos 1000 primeiros numeros

» def soma1():
»     soma=0
»     cont=0
»     while cont ≤ 1000:
»         soma +=cont
»         cont +=1

» def soma2():
»     soma=0
»     for i in range(1001):
»         soma +=i

» timeit.timeit(soma1, number = 10000)
↳ 0.9234309990024485

» timeit.timeit(soma2, number = 10000)
↳ 0.6420390400016913

» import math
» def soma3():
»     soma = math.fsum(range(1001))    

» timeit.timeit(soma3, number = 10000)
↳ 0.2940528209983313

Vemos que usar range() para gerar uma sequência é mais rápido do que somar um contador um a um. O método math.fsum() é otimizado para velocidade e roda mais rápido de que as operações anteriores.

No exemplo abaixo um bloco de código é executado antes do código cuja execução é medida. Ele é usado para inicializar variáveis e preparar um cabeçalho para a exibição do resultado e seu tempo de execução não é incluído na medida.

» pre='a=10;conta=0;print("Conta |Valor de a");print("----------------")'
» cod='''
» for t in range(100):
»     conta +=1
»     a+=t
» print("{}   |  {}".format(conta, a))
» '''
» t = timeit.timeit(stmt = cod, setup = pre, number=5)
» print('O tempo de execução do código foi de \nt = {} seg'.format(t))
↳ Conta |Valor de a
↳ ----------------
↳ 100   |  4960
↳ 200   |  9910
↳ 300   |  14860
↳ 400   |  19810
↳ 500   |  24760
↳ O tempo de execução do código foi de 
↳ t = 0.0005647910002153367 seg

timeit no Jupyter Notebook


No Jupyter Notebook existe um método mágico para aplicar timeit a um bloco de código sem necessidade de se importar o módulo. Usamos %timeit comando para medir o tempo de execução do comando, e %%timeit célula para medir o tempo de execução em toda a célula.

No modo de linha várias comandos podem ser separados por ponto e vírgula, (;). Assim como na execução com timeit importado, como várias rodadas de execução do código são medidas, o código é efetivamente executado e, por isso, se deve evitar inserir partes com outputs extensos.

» %timeit r = range(10)
↳ 199 ns ± 9.94 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

» %timeit r = range(1000)
↳ 270 ns ± 18.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

» # o código é avaliado em namespace separado
» print(r)
↳ NameError: name 'r' is not defined

» # comandos alinhados com ;
» %timeit import math; n=10; m=100; math.pow(n,m)
↳ 303 ns ± 16.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

» # exemplo de exibição longa:
» %timeit for t in range(100): print(t)

» # 81125 linhas são exibidas

Para medida do tempo de execução de uma célula inteira usamos %%timeit.

» def fatorial(n):
»     if n <= 1: return 1
»     else: return n*fatorial(n-1)

-----------------------------------(início da célula)-------
» %%timeit
» n = 10
» fatorial(n)
-----------------------------------(  fim da célula )-------
↳ 1.54 µs ± 77 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

-----------------------------------(início da célula)-------
» %%timeit
» import math
» n=10
» math.factorial(n)
-----------------------------------(  fim da célula )-------
↳ 250 ns ± 8.43 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

» # em linha
» %timeit math.factorial(10)
↳ 110 ns ± 2.85 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)    	

Para medir o tempo de execução de um bloco de código também podemos usar o método timeit.default_timer() para obter um marco de tempo antes e após sua execução.

» import timeit
» import math

» def funcao_1():
»     soma = 0
»     t = 0
»     while t < 1_000_000:
»         soma += t
»         t += 1
»     return soma

» def funcao_2():
»     nums = [i for i in range(1_000_000)]
»     return math.fsum(nums)
    
» inicio = timeit.default_timer()
» funcao_1()
» intervalo_1 = timeit.default_timer() - inicio

» inicio = timeit.default_timer()
» funcao_2()
» intervalo_2 = timeit.default_timer() - inicio

» print('A função 1 demorou {} seg'.format(intervalo_1))
» print('A função 2 demorou {} seg'.format(intervalo_2))
↳ A função 1 demorou 0.12041138499989756 seg
↳ A função 2 demorou 0.07828983699982928 seg

O método timeit admite as seguintes opções:
Em modo de linha:
%timeit [-n N -r R [-t|-c] -q -p P -o]
Em modo de célula:
%%timeit [-n N -r R [-t|-c] -q -p P -o]
onde as opções são:

opção descrição
-n n executa o bloco n vezes em cada loop. Se não fornecido um valor de ajuste é escolhido,
-r r repete o loop r vezes e toma o melhor resultado. Default: r = 3,
-t usa time.time para medir o tempo, que é o default no Unix,
-c usa time.clock para medir o tempo, que é o default no Windows,
-p p usa precisão de p dígitos para exibir o resultado. Default: p = 3,
-q modo silencioso, não imprime o resultado,
-o retorna TimeitResult que pode ser atribuído a uma variável.

O módulo timeit é considerado mais preciso que time, descrito a seguir. Ele executa, por default, o bloco de código 1 milhão de vezes, retornando o menor tempo de execução. Esse número pode ser alterado com o parâmetro number.

Módulo time

O módulo time possui o método time.time() que retorna o momento de sua execução como um timestamp do Unix. The ctime() transforma esse timestamp em um formato padrão de ano, mês, dia e hora.

» import time
» t = time.time()
» print('Tempo decorrido desde a "época" {} segundos'.format(t))
↳ Tempo decorrido desde a "época" 1625697356.5482924 segundos

» horaLocal = time.ctime(t)
» print("Hora local: {}".format(horaLocal))
↳ Hora local: Wed Jul  7 19:35:56 2021

O método time.sleep(n) pausa a execução do código por n segundos, onde n pode ser um inteiro ou float.

» import time
» ti = time.time()
» time.sleep(3.5)
» tf = time.time()

» print('Pausa de {delta:.2f} segundos'.format(delta=tf - ti))
↳ Pausa de 3.50 segundos

Também existe, no Jupiter Notebook, o método mágico %time que mede o tempo decorrido na execução de uma função, similar ao comando time do Unix. Diferentemente de %timeit esse método também exibe o resultado do cálculo.

-----------------------------------(início da célula)-------	
» %time sum(range(100000))
-----------------------------------(  fim da célula )-------
↳ CPU times: user 2.1 ms, sys: 0 ns, total: 2.1 ms
↳ Wall time: 2.1 ms
↳ 4999950000

-----------------------------------(início da célula)-------
» %%time
» soma = 0
» for t in range(1000):
»     soma+=t
» soma
-----------------------------------(  fim  da célula)-------
↳ CPU times: user 150 µs, sys: 7 µs, total: 157 µs
↳ Wall time: 160 µs
↳ 499500 

» # w está disponível após a operação
» %time w = [u for u in range(1000)]
↳ CPU times: user 36 µs, sys: 8 µs, total: 44 µs
↳ Wall time: 47 µs

» print(w)
↳ [0,1,2,...,999]

Módulo string

O Python é notoriamente bom para o gerenciamento de texto. Uma das extensões dessas funcionalidades está no módulo string que contém uma única função e duas classes. A função é capwords(s, sep=None) que parte a string usando str.split() no separador sep; em seguida ela torna o primeiro caracter de cada parte em maiúsculo, usando str.capitalize(); finalmente junta as partes com str.join(). O separador default é sep=’ ‘ (espaço).

» # usando separador default    
» frase = 'nem tudo que reluz é ouro'
» maiuscula = string.capwords(frase)
» print(maiuscula)
↳ Nem Tudo Que Reluz É Ouro

# usando separador '\n' (quebra de linha)
» frase = 'mais vale um pássaro na mão\nque dois voando'
» maiuscula = string.capwords(frase, sep='\n')
» print(maiuscula)
↳ Mais vale um pássaro na mão
↳ Que dois voando    

Um número de constantes são carregadas com o módulo. Elas são especialmente úteis no tratamento de texto, por exemplo quando se deseja remover toda a pontuação de um texto base.

» # string module constants
» print('1.', string.ascii_letters)
» print('2.', string.ascii_lowercase)
» print('3.', string.ascii_uppercase)
» print('4.', string.digits)
» print('5.', string.hexdigits)
» print('6.', string.whitespace)  # ' \t\n\r\x0b\x0c'
» print('7.', string.punctuation)
↳ 1. abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
↳ 2. abcdefghijklmnopqrstuvwxyz
↳ 3. ABCDEFGHIJKLMNOPQRSTUVWXYZ
↳ 4. 0123456789
↳ 5. 0123456789abcdefABCDEF
↳ 6. 
↳ 
↳ 7. !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

Um exemplo simples de remoção de pontuação de um texto.

» # removendo pontuação de um texto
» txtBasico = 'Partimos de um texto com pontuação, queremos remover essas marcações!'
» pontuacao = string.punctuation
» filtrado =''
» for t in txtBasico:
»     if not t in pontuacao:
»         filtrado += t
» print(filtrado)
↳ Partimos de um texto com pontuação queremos remover essas marcações

O módulo string possui duas classes: Formatter e Template.

Formatter funciona da mesma forma que a função str.format(). A utilidade dessa classe está na possibilidade de derivar dela subclasses para a definição customizada de formatações.

» from string import Formatter
» formatador = Formatter()

» print(formatador.format('{} {}.{}', 'Recomendo o site','phylos', 'net'))
↳ Recomendo o site phylos.net

» print(formatador.format('{site}', site='phylos.net'))
↳ phylos.net

» print(formatador.format('{} {site}', 'Visite o site', site='phylos.net'))
↳ Visite o site phylos.net

» # A função format() tem o mesmo comportamento
» print('{} {website}'.format('Visite o site', website='phylos.net'))
↳ Visite o site phylos.net

A classe Templater é usada para criar templates para substituições em strings. Essa funcionalidade é útil, por exemplo, na criação de aplicativos internacionalizados (contendo mais de uma língua como opção na interface).

» from string import Template
» t = Template('$txt $sobrenome, $nome $sobrenome!')
» ing = t.substitute(txt='My name is', nome='James', sobrenome='Bond')
» pt = t.substitute(txt='Meu nome é', nome='Quim', sobrenome='Joah')

» print(ing)
↳ My name is Bond, James Bond!

» print(pt)
↳ Meu nome é Quim, Joah Quim!

São constantes de Strings:

Constante Significado
string.ascii_letters Concatenação de ascii_lowercase e ascii_uppercase descritos abaixo,
string.ascii_lowercase Caracteres minúsculos ‘abcdefghijklmnopqrstuvwxyz’,
string.ascii_uppercase Caracteres maiúsculos ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’,
string.digits Dígitos: a string ‘0123456789’,
string.hexdigits Dígitos hexadecimais: a string ‘0123456789abcdefABCDEF’,
string.octdigits Dígitos octais: string ‘01234567’,
string.punctuation Caracteres ASCII considerados pontuação local: !”#$%&'()*+,-./:;<=>?@[\]^_`{|}~,
string.printable Caracteres ASCII imprimíveis: digits, ascii_letters, punctuation, e whitespace,
string.whitespace String com todos os caracteres ASCII impressos como espaços em branco (space, tab, linefeed, return, formfeed, vertical tab).
🔺Início do artigo

Bibliografia

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

Leave a Reply

Your email address will not be published. Required fields are marked *