Você está na página 1de 256

SEJA BEM VINDO AO APRENDENDO PYTHON

TENHA BONS ESTUDOS

ASS: HACHER DEEX

------------------------------------------------------------------------------------------------------------------------------

Vamos estudar?

Explorando dados usando Python 3


Para quem é este e-book!
Python é a linguagem de ensino introdutória mais popular nas universidades de ciências nos
EUA, portanto, se você é novo no desenvolvimento de software ou se tem pouca experiência e
gostaria de começar com o pé direito, então esse idioma e este livro é o que você precisa. Seu
design e portabilidade irão ajudá-lo torne-se produtivo, independentemente do ambiente que
você escolher para trabalhar.

Se você já trabalhou com Python ou qualquer outra linguagem, este livro ainda pode ser útil
para você como uma referência aos fundamentos do Python e para fornecer uma ampla gama
de considerações e sugestões.

Conteúdos
1 Por que você deve aprender a escrever programas?

1.1 Criatividade e motivação

1.2 Arquitetura de hardware do computador

1.3 Entendendo a programação

1.4 Palavras e frases

1.5 Conversando com Python

1.6 Terminologia: interpretador e compilador

1.7 Escrevendo um programa

1.8 O que é um programa?

1.9 Os blocos de construção dos programas

1.10 O que poderia dar errado?

1.11 A jornada de aprendizagem

1.12 Glossário

1.13 Exercícios
2 Variáveis, expressões e instruções

2.1 Valores e tipos

2.2 Variáveis

2.3 Nomes de variáveis e palavras-chave

2.4 Declarações

2.5 Operadores e operandos

2.6 Expressões

2.7 Ordem das operações

2.8 Operador de módulo

2.9 Operações com strings

2.10 Solicitando entrada do usuário

2.11 Comentários

2.12 Escolhendo nomes de variáveis mnemônicas

2.13 Depuração

2.14 Glossário

2.15 Exercícios

3 Execução condicional

3.1 Expressões booleanas

3.2 Operadores lógicos

3.3 Execução condicional

3.4 Execução alternativa

3.5 Condicionais encadeados

3.6 Condicionais aninhados

3.7 Capturando exceções usando try e except

3.8 Avaliação de curto-circuito de expressões lógicas

3.9 Depuração

3.10 Glossário

3.11 Exercícios

4 Funções

4.1 Chamadas de função

4.2 Funções incorporadas


4.3 Funções de conversão de tipo

4.4 Números aleatórios

4.5 Funções matemáticas

4.6 Adicionando novas funções

4.7 Definições e usos

4.8 Fluxo de execução

4.9 Parâmetros e argumentos

4.10 Funções frutíferas e funções nulas

4.11 Por que funções?

4.12 Depuração

4.13 Glossário

4.14 Exercícios

5 Iteração

5.1 Atualizando variáveis

5.2 A instrução while

5.3 Loops infinitos

5.4 “Loops infinitos” e break

5.5 Finalizando iterações com continue

5.6 Loops definidos usando for

5.7 Padrões de loop

5.7.1 Loops de contagem e soma

5.7.2 Loops máximo e mínimo

5.8 Depuração

5.9 Glossário

5.10 Exercícios

6 Cordas 67

6.1 Uma string é uma sequência

6.2 Obtendo o comprimento de uma string usando len

6.3 Traversal através de uma string com um loop

6.4 Fatias de cordas

6.5 Strings são imutáveis


6.6 Looping e contagem

6.7 O operador in

6.8 Comparação de strings

6.9 métodos de string

6.10 Analisando strings

6.11 Operador de formato

6.12 Depuração

6.13 Glossário

6.14 Exercícios

7 Arquivos

7.1 Persistência

7.2 Abrindo arquivos

7.3 Arquivos de texto e linhas

7.4 Lendo arquivos

7.5 Pesquisando em um arquivo

7.6 Deixando o usuário escolher o nome do arquivo

7.7 Usando try, except e open

7.8 Escrevendo arquivos

7.9 Depuração

7.10 Glossário

7.11 Exercícios

8 Listas

8.1 Uma lista é uma sequência

8.2 As listas são mutáveis

8.3 Percorrendo uma lista

8.4 Operações de lista

8.5 Listar fatias

8.6 Listar métodos

8.7 Excluindo elementos

8.8 Listas e funções

8.9 Listas e strings


8.10 Analisando linhas

8.11 Objetos e valores

8.12 Alias

8.13 Listar argumentos

8.14 Depuração

8.15 Glossário

8.16 Exercícios

9 Dicionários

9.1 Dicionário como um conjunto de contadores

9.2 Dicionários e arquivos

9.3 Looping e dicionários

9.4 Análise de texto avançada

9.5 Depuração

9.6 Glossário

9.7 Exercícios

10 Tuplas

10.1 Tuplas são imutáveis

10.2 Comparando tuplas

10.3 Atribuição de tupla

10.4 Dicionários e tuplas

10.5 Atribuição múltipla com dicionários

10.6 As palavras mais comuns

10.7 Usando tuplas como chaves em dicionários

10.8 Sequências: strings, listas e tuplas - Nossa!

10.9 Depuração

10.10Glossário

10.11 Exercícios

11 Expressões regulares

11.1 Correspondência de caracteres em expressões regulares

11.2 Extraindo dados usando expressões regulares

11.3 Combinando pesquisa e extração de arquivos


11.4 Caractere de escape

11.5 Resumo

11.6 Seção de bônus para usuários Unix/Linux

11.7 Depuração

11.8 Glossário

11.9 Exercícios

12 programas em rede

12.1 Protocolo de Transporte de Hipertexto - HTTP

12.2 O navegador da Web mais simples do mundo

12.3 Recuperando uma imagem por HTTP

12.4 Recuperando páginas da web com urllib

12.5 Analisando HTML e copiando a web

12.6 Analisando HTML usando expressões regulares

12.7 Analisando HTML usando BeautifulSoup

12.8 Lendo arquivos binários usando urllib

12.9 Glossário

12.10 Exercícios

13 Usando Web Services

13.1 eXtensible Markup Language - XML

13.2 Analisando XML

13.3 Percorrendo os nós

13.4 Notação de objeto JavaScript - JSON

13.5 Analisando JSON

13.6 Interfaces de programação de aplicativos

13.7 Serviço web de geocodificação do Google

13.8 Segurança e uso da API

13.9 Glossário

13.10 Exercícios

14 Programação Orientada a Objetos

14.1 Gerenciando Programas Maiores

14.2 Iniciando
14.3 Usando Objetos

14.4 Iniciando com Programas

14.5 Subdividindo um Problema – Encapsulamento

14.6 Nosso primeiro objeto Python

14.7 Classes como Tipos

14.8 Ciclo de vida do objeto

14.9 Muitas Instâncias

14.10 Herança

14.11 Resumo

14.12 Glossário

15 Usando bancos de dados e SQL 185

15.1 O que é um banco de dados?

15.2 Conceitos de banco de dados

15.3 Navegador de banco de dados para SQLite

15.4 Criando uma tabela de banco de dados

15.5 Resumo da Linguagem de Consulta Estruturada

15.6 Spidering Twitter usando um banco de dados

15.7 Modelagem básica de dados

15.8 Programação com várias tabelas

15.8.1 Restrições nas tabelas do banco de dados

15.8.2 Recuperar e/ou inserir um registro

15.8.3 Armazenando o relacionamento de amigo

15.9 Três tipos de chaves

15.10Usando JOIN para recuperar dados

15.11 Resumo

15.12 Depuração

15.13Glossário

16 Visualizando dados

16.1 Construindo um mapa do Google a partir de dados geocodificados

16.2 Visualizando redes e interconexões

16.3 Visualizando dados


Capítulo 1

Por que você deve aprender a programar?


Escrever programas (ou programar) é uma atividade muito criativa e gratificante. Você pode
escrever programas por muitas razões, desde ganhar a vida até resolver um problema difícil de
análise de dados, para se diverti, para ajudar alguém a resolver um problema. Este livro
pressupõe que todos precisam saber programar e que, uma vez que você saiba programar,
descobrirá o que deseja fazer com suas novas habilidades.

Estamos cercados em nossas vidas diárias por computadores que variam de laptops a
celulares. Podemos pensar nesses computadores como nossos “assistentes pessoais” que
podem assumir e cuidar de muitas coisas em nosso nome. O hardware em nossos
computadores atuais é essencialmente construído para nos fazer continuamente a pergunta:
“O que você gostaria que eu faça a seguir?”

Os programadores adicionam um sistema operacional e um conjunto de aplicativos ao


hardware e acabamos com um Personal Digital Assistant bastante útil e capaz de nos ajudar a
fazer muitas coisas diferentes.

Nossos computadores são rápidos e têm grande quantidade de memória e podem ser muito
úteis para nós, se apenas soubéssemos a língua para falar para explicar ao computador o que
gostaria de “fazer a seguir”. Se soubéssemos esta linguagem, poderíamos dizer ao computador
para fazer tarefas em nosso nome que eram repetitivas. Curiosamente, os tipos de coisas que
computadores podem fazer melhor são muitas vezes os tipos de coisas que nós, humanos,
achamos chatos e cansativas.

Por exemplo, olhe para os três primeiros parágrafos deste capítulo e diga-me a palavra mais
comumente usada e quantas vezes a palavra é usada. contá-las é quase doloroso porque não é
o tipo de problema que as mentes humanas são projetadas para resolver.

Para um computador, o oposto é verdadeiro, ler e entender o texto de um pedaço de papel é


difícil para um computador, mas contar as palavras e dizer a você quantas vezes a palavra mais
usada foi usada, é muito fácil para o computador.

1.1 Criatividade e motivação


Embora este livro não se destine a programadores profissionais, profissionais gramming pode
ser um trabalho muito gratificante, tanto financeiramente quanto pessoalmente. Preparar
programas úteis, elegantes e inteligentes para outros usarem é uma atividade muito criativa.

Seu computador ou Personal Digital Assistant (PDA) geralmente contém muitos programas
diferentes de muitos grupos diferentes de programadores, cada um competindo por sua
atenção e interesse. Eles tentam o seu melhor para atender às suas necessidades e dar-lhe
uma ótima experiência do usuário no processo. Em algumas situações, quando você escolhe
uma peça de software, os programadores são diretamente compensados por sua escolha. Se
pensarmos em programas como a produção criativa de grupos de programadores, talvez a
figura a seguir é uma versão mais sensata do nosso PDA

Por enquanto, nossa principal motivação não é ganhar dinheiro ou agradar os usuários finais,
mas em vez disso, para sermos mais produtivos no manuseio dos dados e informações que
encontraremos em nossas vidas. Quando você começar, você será o programador e o usuário
final de seus programas. À medida que você ganha habilidade como programador e sua
gramática parecer mais criativa para você, seus pensamentos podem se voltar para o
desenvolvimento de programas para outros.

1.2 Arquitetura de hardware do computador


Antes de começarmos a aprender a língua falamos para dar instruções aos computadores para
desenvolver softwares, precisamos aprender um pouco sobre como os computadores são
construído. Se você desmontasse seu computador ou celular e olhasse bem no fundo, você
encontraria as seguintes peças:

• A Unidade Central de Processamento (ou CPU) é a parte do computador que é construído


para ser obcecado com "o que vem a seguir?" Se o seu computador estiver classificado como
3.0 Gigahertz, significa que a CPU perguntará “E agora?” três bilhões de vezes por segundo.
Você vai ter que aprender a falar rápido para acompanhar a CPU...

• A Memória Principal RAM é usada para armazenar informações que a CPU precisa. A
memória principal é quase tão rápida quanto a CPU. Mas as informações armazenadas na
memória principal desaparecem quando o computador é desligado.

• A Memória Secundária também é usada para armazenar informações, mas é muito mais
lenta que a memória principal. A vantagem da memória secundária é que ele pode
armazenar informações mesmo quando não há energia no computador. Exemplos de
memória secundária são unidades de disco ou memória flash (normalmente encontrados em
pendrives e tocadores de música portáteis).

• Os dispositivos de entrada e saída são simplesmente nossa tela, teclado, mouse,


microfone, alto-falante, touchpad, etc. Eles são todas as maneiras pelas quais interagimos
com o computador.

• Atualmente, a maioria dos computadores também possui uma conexão de rede para
recuperar informação através de uma rede. Podemos pensar na rede como uma rede muito
lenta. lugar para armazenar e recuperar dados que podem nem sempre estar “ativos”.
Então, em certo sentido, a rede é uma forma mais lenta e às vezes não confiável de memória
secundária.

Embora a maior parte dos detalhes de como esses componentes funcionam seja melhor deixar
para os construtores de computadores, isso irá ajuda a ter alguma noção para podermos falar
sobre essas diferentes partes enquanto escrevemos nossos programas.

Como programador, seu trabalho é usar e orquestrar cada um desses recursos para resolver os
problemas que você precisa resolver, e analise os dados que você obtém da solução. Como
programador, você estará “conversando” principalmente com a CPU e dizendo o que fazer a
seguir. Às vezes, você dirá à CPU para usar a memória principal, memória secundária, rede ou
dispositivos de entrada/saída.

Você precisa ser a pessoa que responde à pergunta "E agora?". Mas isso seria muito
desconfortável encolhê-lo para 5 mm de altura e inseri-lo em um computador apenas para que
você pudesse emitir um comando três bilhões de vezes por segundo. Então em vez disso, você
deve anotar suas instruções com antecedência.

1.3 Entendendo a programação


No restante deste e-book, tentarei transformá-lo em uma pessoa hábil na arte de
programação. No final, você será um programador - talvez não um profissional programador,
mas pelo menos você terá as habilidades para olhar para um dado/informação, problema de
análise e desenvolver de um programa e resolver o problema.

De certa forma, você precisa de duas habilidades para ser um programador:

• Primeiro - você precisa conhecer a linguagem de programação (Python) - você precisa


conhecer o vocabulário e a gramática. Você precisa ser capaz de soletrar as palavras neste
novo idioma corretamente e saber como construir palavras bem formadas “frases” neste novo
idioma.

• Segundo - você precisa “contar uma história”. Ao escrever uma história, você combina
palavras e frases para transmitir uma ideia ao leitor. Há habilidade e arte em construção da
história, e a habilidade na escrita da história é melhorada fazendo algumas escrever e obter
algum feedback. Na programação, nosso programa é a “história” e o problema que você está
tentando resolver é a “ideia”.

Depois de aprender uma linguagem de programação como Python, você descobrirá que é
muito mais fácil aprender uma segunda linguagem de programação, como JavaScript ou C++.

A nova linguagem de programação tem vocabulário e gramática muito diferentes, mas as


habilidades de resolução de problemas serão as mesmas em todas as linguagens de
programação. Você aprenderá o “vocabulário” e as “frases” do Python rapidamente. Ele levará
mais tempo para você ser capaz de escrever um programa coerente para resolver um novo
problema. Ensinamos programação da mesma forma que ensinamos a escrever. começamos a
ler e explicando programas, então escrevemos programas simples, e depois escrevemos
programas cada vez mais complexos ao longo do tempo. Em algum momento você “pega a
onda” e vera os padrões por conta própria e veja com mais naturalidade como lidar com um
problema e escreva um programa que resolva esse problema. E quando chegar a esse ponto, a
programação torna-se um processo muito agradável e criativo. Começamos com o vocabulário
e a estrutura dos programas Python. Seja paciente com os exemplos simples, lembre-se
quando você começou a ler e a escrever pela primeira vez.
1.4 Palavras e frases
Ao contrário das linguagens humanas, o vocabulário do Python é realmente muito pequeno.
Nós chamamos deste “vocabulário” as “palavras reservadas”. São palavras que têm um
significado muito especial para Python. Quando o Python vê essas palavras em um programa,
elas têm apenas um significado. Mais tarde, ao escrever programas, você criara suas próprias
palavras que tenham significado para você, chamadas variáveis. Você terá grande latitude na
escolha de seus nomes para suas variáveis, mas você não pode usar nenhuma das palavras
reservadas do Python como um nome para uma variável.

Quando treinamos um cachorro, usamos palavras especiais como “senta”, “fica” e “busca”.
Quando você fala com um cachorro e não usa nenhuma das palavras reservadas, eles apenas
olham para você com um olhar interrogativo no rosto até você dizer uma palavra reservada.
Por exemplo, se você diz: “Gostaria que mais pessoas caminhassem para melhorar sua saúde
geral”, o que a maioria dos cães provavelmente ouve é "blá, blá, blá, ande, blá, blá, blá, blá".
Isso é porque “andar” é uma palavra reservada na linguagem canina. Muitos podem sugerir
que a linguagem entre humanos e gatos não tem palavras reservadas.

As palavras reservadas na linguagem em que os humanos falam com o Python incluem o


seguinte:

and del global not with

as elif if or yield

assert else import pass

break except in raise

class finally is return

continue for lambda try

def from nonlocal while

E isso, ao contrário de um cachorro, o Python já está completamente treinado. Quando você


diz “try”, o Python tentará toda as vez o que você disser, sem falhar.

Aprenderemos essas palavras reservadas e como elas são usadas oportunamente, mas para
agora vamos nos concentrar no equivalente Python de “falar” (na linguagem de humano para
cachorro). O bom de dizer ao Python para falar é que podemos até dizer a ele o que dizer
dando-lhe uma mensagem entre aspas:

print('ola eu não sou um cachorro!')

E até escrevemos nossa primeira sentença Python sintaticamente correta. Nossa frase começa
com a função print seguida por uma string de texto de nossa escolha entre aspas simples.
1.5 Conversando com Python
Agora que temos uma palavra e uma frase simples que conhecemos em Python, precisamos
saber como iniciar uma conversa com Python para testar nossas novas habilidades de
linguagem.

Antes de poder conversar com Python, você deve primeiro instalar o software Python3 em seu
computador e aprenda como iniciar o Python em seu computador. Isso e muitos detalhes para
este capítulo, então sugiro que você consulte https://python.org.br/instalacao-windows/ onde
Tem as instruções detalhadas de como configurar e iniciar o Python em Sistemas Windows
(baixe de acordo com seu sistema operacional). Em algum momento, você estará em um
terminal ou janela de comando e você digitará python e o interpretador Python será iniciado
executando o modo interativo e aparecem mais ou menos como isso:

Python (versão) (códigos e datas)

[MSC v.1900 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>>

O prompt >>> é a maneira do interpretador Python perguntar a você: “O que você quer que eu
faça a seguir?”

O Python está pronto para conversar com você. Tudo o que tens que saber é como falar a
linguagem Python.

Digamos, por exemplo, que você não conhece nem a linguagem Python e as mais simples
palavras ou frases. Você pode querer usar a linha padrão que os astronautas usam quando eles
pousam em um planeta distante e tentam falar com os habitantes do planeta:

>>> Venho em paz, por favor, leve-me ao seu líder

SyntaxError: invalid syntax

>>>

Isso não está correto. A menos que você pense em algo rapidamente, os habitantes do planeta
provavelmente irão apunhalá-lo com suas armas extraterrestres, colocá-lo em um espeto, e
assá-lo sobre uma fogueira de chama verde, e usá-lo para o jantar.

Felizmente, você trouxe uma cópia deste livro em suas viagens e folheia esta página e tente
novamente:

>>> print(‘Venho em paz, por favor, leve-me ao seu líder!')

Venho em paz, por favor, leve-me ao seu líder

Agora Isso está parecendo muito melhor, e correto, então você tenta se comunicar um pouco
mais:

>>> print('Você deve ser o deus lendário dos extraterrestres')

Você deve ser o deus lendário dos extraterrestres


>>> print('Estamos procurando vocês a muito tempo')

Estamos procurando vocês a muito tempo

>>> print('Nossa lenda diz que vocês nem existiam, e que são cabeçudos’)

Nossa lenda diz que vocês nem existiam, e que são cabeçudos

>>> print ‘vamos se conhecer melhor’

SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

A conversa estava indo tão bem por um tempo e então você fez o menor erro usando a
linguagem Python e Python trouxe o jantar deles de volta.

Neste ponto, você também deve perceber que, embora o Python seja incrivelmente complexo
e poderoso e muito exigente quanto à sintaxe que você usa para se comunicar com ele, o
Python não é inteligente. Você está apenas tendo uma conversa consigo mesmo, mas usando a
sintaxe adequada.

De certo modo, quando você usa um programa escrito por outra pessoa, a conversa é entre
você e os outros programadores com Python atuando como intermediário. Python é uma
maneira de os criadores de programas expressarem como a conversa é e deveria prosseguir. E
em apenas mais alguns capítulos, você será um daqueles programadores usando Python para
conversar com os usuários do seu programa.

Antes de encerrarmos nossa primeira conversa com o interpretador Python, você


provavelmente deve saber a maneira correta de dizer “tchau” ao interagir com os habitantes
do Planeta Python:

>>> adios baby

SyntaxError: invalid syntax

>>> if you don't mind, I need to leave

SyntaxError: unterminated string literal (detected at line 1)

>>> quit()

Você notará que o erro é diferente nas duas primeiras tentativas incorretas. O segundo erro é
diferente porque if é uma palavra reservada e o Python viu o reservado da palavra e pensou
que estávamos tentando dizer algo, mas entendeu a sintaxe da frase errada.

A maneira correta de dizer “adeus” ao Python é inserir quit() no divisa >>> prompt.
Provavelmente você levaria um bom tempo para adivinhar que ter um livro em mãos
provavelmente seria útil para conversar com extraterrestres.
1.6 Terminologia: interpretador e compilador
Python é uma linguagem de alto nível destinada a ser relativamente simples para humanos.
Para o homem ler e escrever e para os computadores ler e processar. Outros de alto nível de
linguagens incluem Java, C++, PHP, Ruby, Basic, Perl, JavaScript e muito mais.

O hardware real dentro da Unidade Central de Processamento (CPU) não suporta qualquer
uma dessas linguagens de alto nível.

A CPU entende uma linguagem que chamamos de linguagem de máquina. A linguagem de


máquina é muito simples e francamente muito cansativo de escrever porque é representado
todo em zeros e uns:

001010001110100100101010000001111

11100110000011101010010101101101

...

A linguagem de máquina parece bastante simples na superfície, dado que existem apenas
zeros e uns, mas sua sintaxe é ainda mais complexa do que Python. Portanto, muito poucos
programadores escrevem linguagem de máquina. Em vez disso, construímos vários tradutores
para permitir que os programadores escrevam em linguagens de alto nível como Python ou
JavaScript, e esses tradutores convertem os programas em linguagem de máquina para
execução real pela CPU.

Como a linguagem de máquina está ligada ao hardware do computador, a linguagem de


máquina não é portátil em diferentes tipos de hardware. Programas escritos em linguagem de
alto nível os medidores podem ser movidos entre computadores diferentes usando um
interpretador diferente na nova máquina ou recompilando o código para criar uma versão em
linguagem de máquina do programa para a nova máquina.

Esses tradutores de linguagens de programação se enquadram em duas categorias gerais:

1- Interpretadores
2- Compiladores

Um interpretador lê o código-fonte do programa conforme escrito pelo programador, analisa o


código-fonte e interpreta as instruções em tempo real. Python é um interpretador e quando
estamos executando o Python interativamente, podemos digitar uma linha de Python (uma
frase) e Python a processa imediatamente e está pronto para digitarmos outra linha de
comando.

Algumas das linhas de comando dizem ao Python que você deseja que ele se lembre de algum
valor para mais tarde utilizar. Precisamos escolher um nome para esse valor ser lembrado e
podemos usar esse nome simbólico para recuperar o valor mais tarde. Usamos o termo
variável para nos referir aos rótulos que usamos para nos referir a esses dados armazenados.
>>> x = 6

>>> print(x)

>>> y = x * 7

>>> print(y)

42

>>>

Neste exemplo, pedimos ao Python para lembrar o valor seis e usar o rótulo x para podermos
recuperar o valor mais tarde. Verificamos que o Python realmente se lembrou do valor usando
print.

Então pedimos ao Python para recuperar x e multiplicá-lo por sete e colocar o novo valor
calculado em y. Em seguida, pedimos ao Python para imprimir o valor atualmente em y.

Mesmo que estejamos digitando esses comandos no Python uma linha por vez, o Python está
tratando-os como uma sequência ordenada de declarações com declarações posteriores
capazes para recuperar dados criados em instruções anteriores. Estamos escrevendo nosso
primeiro simples parágrafo com quatro frases em uma ordem lógica e significativa.

É da natureza de um intérprete ser capaz de ter uma conversa interativa como mostrado
acima. Um compilador precisa receber todo o programa em um arquivo, e, em seguida,
executa um processo para traduzir o código-fonte de alto nível em linguagem de máquina e,
em seguida, o compilador coloca a linguagem de máquina resultante em um arquivo para
posterior execução.

Se você tiver um sistema Windows, geralmente esses programas executáveis em linguagem de


máquina têm um sufixo de “.exe” ou “.dll” que significa “executável” e “link dinâmico
biblioteca” respectivamente. No Linux e no Macintosh, não há sufixo que marca um arquivo
como executável.

Se você abrisse um arquivo executável em um editor de texto, ele pareceria completamente


louco e ser ilegível:

^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@\xa0\x82

^D^H4^@^@^@\x90^]^@^@^@^@^@^@4^@ ^@^G^@(^@$^@!^@^F^@

^@^@4^@^@^@4\x80^D^H4\x80^D^H\xe0^@^@^@\xe0^@^@^@^E

^@^@^@^D^@^@^@^C^@^@^@^T^A^@^@^T\x81^D^H^T\x81^D^H^S

^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A\^D^HQVhT\x83^D^H\xe8

....
Não é fácil ler ou escrever linguagem de máquina, por isso é bom termos inter-preters e
compiladores que nos permitem escrever em linguagens de alto nível como Python ou C.

Agora, neste ponto de nossa discussão sobre compiladores e interpretadores, você deve estar
pensando um pouco sobre o próprio interpretador Python. Em que língua está escrito em?
Está escrito em uma linguagem compilada? Quando digitamos em “python”, o que exatamente
está acontecendo?

O interpretador Python é escrito em uma linguagem de alto nível chamada “C”. Você pode
olhar no código-fonte real do interpretador Python acessando www.python.org e trabalhando
seu caminho para o código-fonte. Então Python é um programa em si e é compilado em código
de máquina. Quando você instalou o Python em seu computador, você copiou uma cópia do
código de máquina do Python traduzindo o programa em seu sistema. No Windows, o código
de máquina executável para Python em si provavelmente está em um arquivo com um nome
como:

C:\Python35\python.exe

Isso é mais do que você realmente precisa saber para ser um programador Python, mas às
vezes vale a pena responder a essas pequenas perguntas incômodas logo no início.

1.7 Escrevendo um programa


Digitar comandos no interpretador Python é uma ótima maneira de experimentar os recursos
do Python, mas não é recomendado para resolver problemas mais complexos.

Quando queremos escrever um programa, usamos um editor de texto para escrever o Python
instruções em um arquivo, que é chamado de script. Por convenção, os scripts Python têm
nomes que terminam com .py.

Para executar o script, você deve informar ao interpretador Python o nome do arquivo. Em
uma janela de comando Unix ou Windows, você digitaria python hello.py como segue:

csev$ cat hello.py

print('Hello world!')

csev$ python hello.py

Hello world!

csev$

O “csev$” é o prompt do sistema operacional e o “cat hello.py” está nos mostrando que o
arquivo “hello.py” tem um programa Python de uma linha para imprimir uma string.

Chamamos o interpretador Python e dizemos para ele ler seu código-fonte do arquivo
“hello.py” em vez de nos solicitar linhas de código Python interativamente. Você notará que
não havia necessidade de ter quit () no final do Python programado no arquivo.

Quando o Python está lendo seu código-fonte de um arquivo, ele sabe parar quando chegar
ao final do arquivo.
1.8 O que é um programa?
A definição de um programa em sua forma mais básica é uma sequência de instruções Python
que foram criados para fazer algo. Mesmo nosso script hello.py simples é um programa. É um
programa de uma linha e não é particularmente útil, mas no mais estrito definição, é um
programa Python.

Pode ser mais fácil entender o que é um programa pensando em um problema que um
programa pode ser construído para resolver e, em seguida, olhando para um programa que
resolver esse problema.

Digamos que você esteja fazendo uma pesquisa de Computação Social em postagens do
Facebook e esteja interessado na palavra mais usada em uma série de postagens. Você
poderia imprimir o fluxo de postagens do Facebook e se debruçar sobre o texto procurando o
mais comum palavra, mas isso levaria muito tempo e seria muito propenso a erros. Você seria
inteligente para escrever um programa Python para lidar com a tarefa com rapidez e precisão
para que você poça passar o fim de semana fazendo algo divertido.

Por exemplo, observe o seguinte texto sobre um palhaço e um carro. olha o texto e descubra a
palavra mais comum e quantas vezes ela ocorre.

O palhaço correu atrás do carro

E o carro entrou na barraca

E a barraca caiu sobre o palhaço e o carro

Então imagine que você está fazendo esta tarefa olhando para milhões de linhas de texto.
Francamente, seria mais rápido para você aprender Python e escrever um programa Python
para contar as palavras do que escanea-las manualmente.

A notícia ainda melhor é que já criei um programa simples para encontrar a palavra mais
comum em um arquivo de texto. Escrevi, testei e agora estou dando para você usar para que
você possa economizar algum tempo.

name = input('Enter file:')

handle = open(name, 'r')

text = handle.read()

words = text.split()

counts = dict()

for word in words:

counts[word] = counts.get(word, 0) + 1

bigcount = None

bigword = None
for word, count in list(counts.items()):

if bigcount is None or count > bigcount:

bigword = word

bigcount = count

# Código: http://www.pythonlearn.com/code3/words.py

Você nem precisa saber Python para usar este programa. Você vai precisar leia o Capítulo 10
deste livro para entender completamente o incrível mundo Python e as técnicas que foram
usadas para fazer o programa. Você é o usuário final, basta usar o programa e maravilhe-se
com sua inteligência e como ele economizou seu tempo e seus esforços. Você simplesmente
digita o código em um arquivo chamado words.py e o executa ou você baixe o código-fonte
em http://www.pythonlearn.com/code3/ e execute-o.

Este é um bom exemplo de como o Python e a linguagem Python estão agindo como um
intermediário entre você (o usuário final) e eu (deex). Python é uma maneira de trocarmos
sequências de instruções úteis (ou seja, programas) em uma linguagem que pode ser usada
por qualquer pessoa que instale o Python em seu computador. Então nenhum de nós estamos
falando com Python, em vez disso, estamos nos comunicando um com o outro através do
Python.

1.9 Os blocos de construção dos programas


Nos próximos capítulos, aprenderemos mais sobre o vocabulário, e a estrutura de parágrafo e
estrutura da história do Python. Nós aprenderemos sobre os recursos poderosos do Python e
como compor esses recursos juntos para criar programas úteis. Existem alguns padrões
conceituais de baixo nível que usamos para construir programas. Essas construções não são
apenas para programas Python, elas fazem parte de todos os programas. linguagem de
programação desde a linguagem de máquina até as linguagens de alto nível.

Input - Obtenha dados do “mundo exterior”. Isso pode estar lendo dados de uma arquivo, ou
mesmo algum tipo de sensor como um microfone ou GPS. Em nossos programas iniciais, nossa
entrada virá dos dados digitados pelo usuário no teclado.

Output - Exibir os resultados do programa em uma tela ou armazená-los em um arquivo ou


talvez gravá-los em um dispositivo como um alto-falante para tocar música ou falar textos.

sequential execution - Executa instruções uma após a outra na ordem em que são
encontrados no script.

conditional execution - Verifique certas condições e, em seguida, execute ou pule uma


sequência de declarações.

repeated execution - Execute algum conjunto de declarações repetidamente, geralmente com


alguma variação.

Reuse - Escreva um conjunto de instruções uma vez e dê um nome a elas e depois reutilize-as
instruções conforme necessário ao longo de seu programa.
Parece quase simples demais para ser verdade e, claro, nunca é tão simples. É como dizemos
que andar é simplesmente “colocar um pé na frente do outro”. A arte de escrever um
programa é compor e entrelaçar esses elementos básicos muitas vezes para produzir algo que
seja útil para seus usuários. O programa de contagem de palavras acima usa diretamente
todos esses padrões, exceto um.

1.10 O que poderia dar errado?


Como vimos em nossas primeiras conversas com Python, devemos nos comunicar muito
precisamente quando escrevemos os código. O menor desvio ou erro causará que o Python
desistir de olhar para o seu programa.

Os programadores iniciantes geralmente consideram o fato de que o Python não deixa espaço
para erros como evidência de que Python é mesquinho, odioso e cruel. Enquanto Python
parece gostar de todos os outros, Python os conhecem pessoalmente e guarda rancor contra
eles.

Por causa desse rancor, o Python pega nossos programas perfeitamente escritos e rejeita
como “incapazes” apenas para nos atormentar. (Acho que exagerei um pouco, mas vamos la)

Há pouco a ganhar discutindo com Python. É apenas uma ferramenta. Não tem emoções e está
feliz e pronto para atendê-lo sempre que precisar. Seus erros nas mensagens parecem duras,
mas são apenas um pedido de ajuda do Python. Ele olhou para o que você digitou e
simplesmente não consegue entender o que você digitou.

Python é muito mais parecido com um cachorro, amando você incondicionalmente, tendo
algumas palavras-chave que ele entenda, olhando para você com um olhar doce em seu rosto
(>>>), e esperando para você dizer algo que entenda. Quando o Python diz SyntaxError:
invalid syntax

ele está simplesmente abanando o rabo e dizendo: “Parece que você disse algo mas eu
simplesmente não entendo o que você quis dizer, mas por favor continue falando comigo
(>>>).

À medida que seus programas se tornam cada vez mais sofisticados, você encontrará três
vários tipos de erros:

Syntax erros - Esses são os primeiros erros que você cometerá a ver e os mais fáceis de
corrigir. Erro de sintaxe significa que você violou as regras de “gramática” do Python. O Python
faz o possível para apontar diretamente para a linha e o caractere onde notou esta confuso. A
única parte complicada dos erros de sintaxe é que às vezes o erro que precisa ser consertado
é, na verdade, mais cedo no programa do que onde o Python percebeu que estava confuso.
Portanto, a linha e o caractere que o Python indica em um erro de sintaxe pode ser apenas um
ponto de partida para sua investigação.

Logic erros - Um erro de lógica é quando seu programa tem uma boa sintaxe, mas há um erro
na ordem das declarações ou talvez um erro na forma como a declarações se relacionam entre
si. Um bom exemplo de um erro de lógica pode ser:
“Tomar um gole na sua garrafinha de água, colocar na mochila, caminhar até a biblioteca e, em
seguida, coloque a tampa de volta na garrafa”.

Semantic erros - Um erro semântico é quando sua descrição das etapas a serem seguidas é
sintaticamente perfeito e na ordem certa, mas há simplesmente um erro no programa. O
programa está perfeitamente correto, mas não faz o que você destinou a fazê-lo. Um exemplo
simples seria se você estivesse dando a uma pessoa instruções para um restaurante e disse: “. .
. ao chegar ao cruzamento com o posto de gasolina, vire à esquerda e ande uns metros e o
restaurante é um prédio vermelho à sua esquerda." Seu amigo está muito atrasado e liga para
você para dizer que eles estão em uma fazenda e andando atrás de um celeiro, sem sinal de
restaurante. Então você diz "você virou à esquerda ou à direita no posto de gasolina?" e eles
dizem: “Eu segui suas instruções perfeitamente, eu as escrevi, diz vire à esquerda e percorrer
alguns metros do posto de gasolina. Então você diz: “Sinto muito, porque embora minhas
instruções estivessem sintaticamente corretas, infelizmente continham um pequeno erro
semântico não detectado.”.

Novamente em todos os três tipos de erros, o Python está apenas tentando ao máximo fazer
exatamente o que você perguntou.

1.11 A jornada de aprendizado


À medida que avança no restante do e-book, não tenha medo se os conceitos não
funcionarem. Parecem se encaixar bem na primeira vez. Quando você estava aprendendo a
falar, da ruim.

E tudo bem se levasse seis meses para você passar do vocabulário simples para as frases
simples e levou mais 5-6 anos para passar de frases para parágrafos, e mais alguns anos para
poder escrever um conto interessante e completo sobre você próprio.

Quero que você aprenda Python muito mais rapidamente, então ensinamos tudo ao mesmo
tempo nos próximos capítulos. Mas é como aprender um novo idioma que leva tempo para
absorver e entender antes que pareça natural. Isso leva a alguma confusão como ler e ler
novamente os tópicos para tentar fazer com que você veja o quadro geral enquanto estamos
definindo os pequenos fragmentos que compõem esse grande quadro. Enquanto o livro é
escrito linearmente, e se você estiver fazendo um curso, ele progredirá de maneira linear, não
hesite em ser muito não linear em como você aborda o material. Olhe para a frente e para trás
e leia devagar. Desnatando material mais avançado sem entender completamente os detalhes,
você pode obter uma melhor compreensão do "por que?" de programação. Revendo material
anterior e até mesmo refazendo os exercícios, você perceberá que realmente aprendeu muito
do material, mesmo que o material que você está olhando parece um pouco impenetrável.

Normalmente, quando você está aprendendo sua primeira linguagem de programação,


existem alguns maravilhosos “Ah Hahhhh!” momentos em que você sente vontade de bater o
dedinho no sofá. É normal.

Se algo parece particularmente difícil, geralmente não vale a pena ficar acordado a noite toda
olhando para ela. Faça uma pausa, tire uma soneca, faça um lanche, explique o que você está
tendo problemas com alguém (ou talvez com seu cachorro) e depois volte a ele com novos
olhos. Garanto-lhe que uma vez que você aprender os conceitos de programação nesse e-
book, você vai olhar para trás e ver que foi tudo muito fácil, elegante e simplesmente levou um
pouco de tempo para absorvê-lo. :D

1.12 Glossário
Bug - Um erro em um programa.

unidade central de processamento - O coração de qualquer computador. É o que executa o


software que escrevemos; também chamado de “CPU” ou “processador”.

Compilar - Para traduzir um programa escrito em uma linguagem de alto nível para uma
linguagem de baixo nível de uma só vez, em preparação para execução posterior.

linguagem de alto nível - Uma linguagem de programação como o Python, projetada para ser
fácil para os humanos lerem e escreverem.

modo interativo - Uma maneira de usar o interpretador Python digitando comandos e


expressões no prompt.

Interpret - Para executar um programa em uma linguagem de alto nível, traduzindo-o uma
linha de cada vez.

linguagem de baixo nível - Uma linguagem de programação projetada para ser fácil para um
computador executar; também chamado de “código de máquina” ou “linguagem de
montagem”.

Código da máquina - A linguagem de nível mais baixo para software, que é a linguagem que é
executado diretamente pela unidade central de processamento (CPU).

memória principal - Armazena programas e dados. A memória principal perde suas


informações quando a alimentação é desligada.

Analisar - Examinar um programa e analisar a estrutura sintática.

Portabilidade - Uma propriedade de um programa que pode ser executado em mais de um


tipo de computador.

função print - Uma instrução que faz com que o interpretador Python exiba um valor na tela.

Solução de problemas - O processo de formular um problema, encontrar uma solução e


expressando a solução.

Programa - Um conjunto de instruções que especifica uma computação.

Prompt - Quando um programa exibe uma mensagem e faz uma pausa para que o usuário
digite alguma entrada para o programa.

memória secundária - Armazena programas e dados e retém suas informações mesmo quando
a alimentação é desligada. Geralmente mais lento que a memória principal. Exemplos de
memória secundária incluem unidades de disco e memória flash em pendrives.

Semântica - O significado de um programa.


Erro de semântica - Um erro em um programa que faz com que ele faça algo diferente de

o que o programador pretendia.

Código fonte - Um programa em uma linguagem de alto nível.

1.13 Exercícios
Exercício 1: Qual é a função da memória secundária em um computador?

a) Executar toda a computação e lógica do programa

b) Recuperar páginas da web pela Internet

c) Armazenar informações a longo prazo, mesmo após um ciclo de energia

d) Obter informações do usuário

Exercício 2: O que é um programa?

Exercício 3: Qual é a diferença entre um compilador e um interpretador?

Exercício 4: Qual dos seguintes contém “código de máquina”?

a) O interpretador Python

b) O teclado

c) arquivo fonte Python

d) Um documento de processamento de texto

Exercício 5: O que há de errado com o seguinte código:

>>> primt 'Hello world!'

File "<stdin>", line 1

primt 'Hello world!'

SyntaxError: invalid syntax

>>>

Exercício 6: Onde no computador uma variável como “X” é armazenada após a seguinte linha
Python termina?

x = 123

a) Unidade central de processamento

b) Memória Principal
c) Memória Secundária

d) Dispositivos de Entrada

e) Dispositivos de saída

Exercício 7: O que o seguinte programa imprimirá:

x = 43

x=x+1

print(x)

a) 43

b) 44

c) x + 1

d) Erro porque x = x + 1 não é possível matematicamente

Exercício 8: Explique cada um dos itens a seguir usando um exemplo de capacidade humana:

(1) Unidade central de processamento

(2) Memória principal

(3) Memória secundária

(4) dispositivo de entrada

(5) Dispositivo de saída. Por exemplo, “Qual é o equivalente humano a uma Unidade central
de processamento"?

Exercício 9: Como corrigir um “erro de sintaxe”?


Capítulo 2
Variáveis, expressões e declarações

2.1 Valores e tipos


Um valor é uma das coisas básicas com as quais um programa trabalha, como uma letra ou um
número. Os valores que vimos até agora são 1, 2 e “Hello, World!” Esses valores pertencem a
tipos diferentes: 2 é um número inteiro e “Hello, World!” é uma string, assim chamada porque
contém uma “string” de letras. Você (e o intérprete) pode identificar strings porque elas estão
entre aspas.

A instrução print também funciona para números inteiros. Usamos o comando python para
iniciar o intérprete.

python

>>> print(4)

Se você não tiver certeza do tipo de um valor, o interpretador pode lhe dizer.

>>> type('Hello, World!')

<class 'str'>

>>> type(17)

<class 'int'>

Não surpreendentemente, strings pertencem ao tipo str e inteiros pertencem ao tipo int.
Menos obviamente, números com ponto decimal pertencem a um tipo chamado float, porque
esses números são representados em um formato chamado ponto flutuante.

>>> type(3.2)

<class 'float'>
E quanto o valores como “17” e “3,2”? Parecem números, mas estão em aspas como strings.

>>> type('17')

<class 'str'>

>>> type('3.2')

<class 'str'>

Eles são cordas.

Ao digitar um número inteiro grande, você pode ficar tentado a usar vírgulas entre grupos de
três dígitos, como em 1.000.000. Este não é um número inteiro legal em Python, mas é legal:

>>> print(1,000,000)

100

Bem, isso não é o que esperávamos! Python interpreta 1.000.000 como uma vírgula, sequência
separada de inteiros, que imprime com espaços entre eles. Este é o primeiro exemplo que
vimos de um erro semântico: o código é executado produzindo uma mensagem de erro, mas
não faz a coisa "certa".

2.2 Variáveis
Um dos recursos mais poderosos de uma linguagem de programação é a capacidade de
manipular variáveis. Uma variável é um nome que se refere a um valor.

Uma instrução de atribuição cria novas variáveis e dá a elas valores:

>>> mensagem = 'E algo completamente diferente'

>>> n = 17

>>> pi = 3.1415926535897931

Este exemplo faz três atribuições. O primeiro atribui uma string a uma nova variável
mensagem nomeada; o segundo atribui o inteiro 17 a n; o terceiro atribui o (aproximado) valor
de π pi.

Para exibir o valor de uma variável, você pode usar uma instrução print:
>>> print(n)

17

>>> print(pi)

3.141592653589793

O tipo de uma variável é o tipo do valor ao qual ela se refere.

>>> type(message)

<class 'str'>

>>> type(n)

<class 'int'>

>>> type(pi)

<class 'float'>

2.3 Nomes de variáveis e palavras-chave


Os programadores geralmente escolhem nomes para suas variáveis que sejam significativos e
documentar para que a variável é usada. Os nomes das variáveis podem ser arbitrariamente
longos. Eles podem conter letras e números, mas eles não podem começar com um número. É
legal usar letras maiúsculas, mas é uma boa ideia começar os nomes das variáveis com uma
letra minúscula (você verá o porquê mais tarde).

O caractere sublinhado (_) pode aparecer em um nome. É frequentemente usado em nomes


com várias palavras, como my_name ou airspeed_of_unladen_swallow. Variável os nomes
podem começar com um caractere de sublinhado, mas geralmente evitamos fazer isso a
menos que estejamos escrevendo código de biblioteca para outros usarem.

Se você atribuir um nome ilegal a uma variável, receberá um erro de sintaxe:

>>> 76trombones = 'big parade'

SyntaxError: invalid syntax

>>> more@ = 1000000

SyntaxError: invalid syntax

>>> class = 'Advanced Theoretical Zymurgy'

SyntaxError: invalid syntax


76trombones é ilegal porque começa com um número. more@ é ilegal porque ele contém um
caractere ilegal, @. Mas o que há de errado com o class?

Acontece que class é uma das palavras-chave do Python. O intérprete usa palavras-chave para
reconhecer a estrutura do programa, e não podem ser usados como uma variável.

Python reserva 33 palavras-chave:

and del from None True

as elif global nonlocal try

assert else if not while

break except import or with

class False in pass yield

continue finally is raise

def for lambda return

Você pode querer manter esta lista à mão. Se o intérprete reclamar de uma das suas variáveis
e você não sabe por que, veja se está nesta lista.

2.4 Declarações
Uma instrução é uma unidade de código que o interpretador Python pode executar. Nós
temos dois tipos de instruções: print sendo uma instrução de expressão e atribuição. Quando
você digita uma instrução no modo interativo, o interpretador a executa e exibe o resultado,
se houver.

Um script geralmente contém uma sequência de instruções. Se houver mais de uma instrução,
os resultados aparecem um de cada vez conforme as instruções são executadas.

Por exemplo

print(1)

x=2

print(x)
Produz a saída

A instrução de atribuição não produz nenhuma saída.

2.5 Operadores e operandos


Operadores são símbolos especiais que representam cálculos como adição e tipificação. Os
valores aos quais o operador é aplicado são chamados de operandos.

Os operadores +, -, *, / e ** realizam adição, subtração, multiplicação, divisão e


exponenciação, como nos exemplos a seguir:

20+32 hour-1 hour*60+minute minute/60 5**2 (5+9)*(15-7)

Houve uma alteração no operador de divisão entre Python 2.x e Python 3.x. No Python 3.x, o
resultado dessa divisão é um resultado de ponto flutuante:

>>> minute = 59

>>> minute/60

0.9833333333333333

O operador de divisão no Python 2.0 dividiria dois inteiros e arredondaria o resultado para um
inteiro:

>>> minute = 59

>>> minute/60

0
Para obter a mesma resposta no Python 3.0, use a divisão com ( // ). Isso irá arredondar o
resultado!

>>> minute = 59

>>> minute//60

No Python 3.0, a divisão inteira funciona muito mais do que você esperaria, se você inseriu a
expressão em uma calculadora.

2.6 Expressões
Uma expressão é uma combinação de valores, variáveis e operadores. Um valor todo por si, é
considerado uma expressão e, portanto, uma variável, então os seguintes são todos válidas
expressões (supondo que a variável x tenha um valor atribuído):

17

x + 17

Se você digitar uma expressão no modo interativo, o interpretador a avalia e exibe o resultado:

>>> 1 + 1

Mas em um script, uma expressão sozinha não faz nada! Isso é comumente fonte de confusão
para iniciantes.

Exercício 1: Digite as seguintes instruções no interpretador Python para ver o que eles fazem:

x=5

x+1
2.7 Ordem das operações
Quando mais de um operador aparece em uma expressão, a ordem de avaliação depende das
regras de precedência. Para operadores matemáticos, Python segue convenção matemática. A
sigla PEMDAS é uma maneira útil de lembrar as regras:

• Os parênteses têm a precedência mais alta e podem ser usados para forçar uma expressão
missão para avaliar na ordem que você deseja. Como as expressões entre parênteses são
avaliado primeiro, 2 * (3-1) é 4 e (1+1)**(5-2) é 8. Você também pode usar parênteses para
facilitar a leitura de uma expressão, como em (minuto * 100) / 60, mesmo que não altere o
resultado.

• A exponenciação tem a próxima precedência mais alta, então 2**1+1 é 3, não 4, e 3*1**3 é
3, não 27.

• A multiplicação e a divisão têm a mesma precedência, que é maior do que a adição e a


subtração, que também têm a mesma precedência. Então 2*3-1 é 5, não 4, e 6+4/2 é 8,0, não
5.

• Operadores com a mesma precedência são avaliados da esquerda para a direita. Então o
expressão 5-3-1 é 1, não 3, porque o 5-3 acontece primeiro e depois 1 é subtraído de 2.

Em caso de dúvida, sempre coloque parênteses em suas expressões para garantir que as
colocações estão sendo realizadas na ordem que você pretende.

2.8 Operador de módulo


O operador de módulo trabalha com números inteiros e produz o restante quando o primeiro
operando é dividido pelo segundo. Em Python, o operador de módulo é uma porcentagem
sinal (%). A sintaxe é a mesma de outros operadores:

>>> quotient = 7 // 3

>>> print(quotient)

>>> remainder = 7 % 3

>>> print(remainder)

Então 7 dividido por 3 é 2 com 1 sobrando. O operador de módulo acaba sendo


surpreendentemente útil. Por exemplo, você pode verificar se um número é divisível por
outro: se x % y é zero, então x é divisível por y.
Você também pode extrair o dígito ou dígitos mais à direita de um número. Por exemplo, x %
10 produz o dígito mais à direita de x (na base 10). Da mesma forma, x % 100 produz o dois
últimos dígitos.

2.9 Operações de string


O operador + funciona com strings, mas não é adição no sentido matemático. Em vez disso, ele
executa a concatenação, o que significa juntar as strings ligando-as de ponta a ponta. Por
exemplo:

>>> first = 10

>>> second = 15

>>> print(first+second)

25

>>> first = '100'

>>> second = '150'

>>> print(first + second)

100150

A saída deste programa é 100150.

2.10 Solicitando entrada do usuário


Às vezes, gostaríamos de obter o valor de uma variável do usuário por meio de seu teclado.
Python fornece uma função interna chamada input que obtém entrada de o teclado1. Quando
esta função é chamada, o programa para e espera pelo usuário digitar algo. Quando o usuário
pressiona Return ou Enter, o programa resume e input retorna o que o usuário digitou como
uma string.

>>> input = input()

Alguma coisa

>>> print(input)

Alguma coisa
Antes de receber a entrada do usuário, é uma boa ideia imprimir um prompt informando o
usuário o que inserir. Você pode passar uma string para entrada para ser exibida para o
usuário antes de pausar para entrada:

>>> name = input('Qual é o seu nome?\n')

Qual é o seu nome?

Deex

>>> print(name)

Deex

A sequência \n no final do prompt representa uma nova linha, que é um caractere que causa
uma quebra de linha. É por isso que a entrada do usuário aparece abaixo do prompt. Se você
espera que o usuário digite um número inteiro, pode tentar converter o valor de retorno para
int usando a função int():

>>> prompt = 'Qual...é a velocidade de uma andorinha?\n'

>>> velocidade = input(prompt)

Qual...é a velocidade de uma andorinha?

17

>>> int(velocidade)

17

>>> int(velocidade) + 5

22

Mas se o usuário digitar algo diferente de uma string de dígitos, você receberá um erro:

>>> velocidade = input(prompt)

Qual é a velocidade de uma andorinha?

uma andorinha africana ou brasileira?

>>> int(velocidade)

alueError: invalid literal for int() with base 10:

Veremos como lidar com esse tipo de erro mais adiante.


2.11 Comentários
À medida que os programas ficam maiores e mais complicados, eles ficam mais difíceis de ler.
Linguagens formais são densas e muitas vezes é difícil olhar para um pedaço de código e
descobrir o que está fazendo, ou por quê. Por esse motivo, é uma boa ideia adicionar notas
aos seus programas para explicar em linguagem natural o que o programa está fazendo. Essas
notas são chamadas de comentários, e em Python eles começam com o símbolo #:

# calcula a porcentagem da hora que passou

porcentagem = (minuto * 100) / 60

Nesse caso, o comentário aparece sozinho em uma linha. Você também pode colocar
comentários no final de uma linha:

porcentagem = (minuto * 100) / 60 # porcentagem de uma hora

Tudo do \# até o final da linha é ignorado; não tem efeito sobre o programa.

Os comentários são mais úteis quando documentam recursos não óbvios do código. É razoável
supor que o leitor possa descobrir o que o código faz; isso é muito mais útil para explicar o
porquê.

Este comentário é redundante com o código e inútil:

v = 5 # atribui 5 a v

Este comentário contém informações úteis que não estão no código:

v = 5 # velocidade em metros/segundo.

Bons nomes de variáveis podem reduzir a necessidade de comentários, mas nomes longos
podem ser expressões complexas difíceis de ler, então há uma compensação.
2.12 Escolhendo nomes de variáveis mnemônicas
Contanto que você siga as regras simples de nomenclatura de variáveis e evite palavras, você
tem muitas opções quando nomeia suas variáveis. No início, esta escolha pode ser confusa
tanto quando você lê um programa quanto quando escreve seus próprios programas. Por
exemplo, os três programas a seguir são idênticos em termos do que eles realizam, mas muito
diferentes quando você os lê e tenta compreendê-los.

a = 35.0

b = 12.50

c=a*b

print(c)

horas = 35,0

taxa = 12,50

salario = horas * taxa

print(salario)

x1q3z9ahd = 35.0

x1q3z9afd = 12.50

x1q3p9afd = x1q3z9ahd * x1q3z9afd

print(x1q3p9afd) #talvez eu tenha exagerado novamente

O interpretador Python vê todos esses três programas como exatamente iguais, mas os
humanos veem e entendem esses programas de maneira bem diferente. Os humanos irão
entender rapidamente a intenção do segundo programa porque o programador faz nomes de
variáveis escolhidos que refletem sua intenção em relação a quais dados serão armazenados
em cada variável.

Chamamos esses nomes de variáveis sabiamente escolhidos de “nomes de variáveis


mnemônicas”. A palavra mnemonic significa “auxílio à memória”. Escolhemos nomes de
variáveis mnemônicas para nos ajudar lembre-se por que criamos a variável em primeiro lugar.

Embora tudo isso pareça ótimo, é uma boa ideia usar variáveis mnemônicas, nomes de
variáveis mnemônicas podem atrapalhar o aprendizado de um programador iniciante a
capacidade de analisar e entender o código. Isso ocorre porque os programadores iniciantes
não tem ainda uma memória para as palavras reservadas (são apenas 31 delas) e às vezes as
variáveis com nomes muito descritivos começam a parecer na linguagem e não apenas em
nomes de variáveis bem escolhidos.
Dê uma olhada rápida no código de exemplo Python a seguir, que percorre alguns dados.
Abordaremos os loops em breve, mas, por enquanto, tente apenas decifrar o que isso significa:

for word in words:

print(word)

O que esta acontecendo aqui? Quais dos tokens (for, word, in, etc.) são reservados palavras e
quais são apenas nomes de variáveis? O Python entende em um fundamento nível mental a
noção de palavras? Os programadores iniciantes têm problemas para separar quais partes do
código devem ser iguais a este exemplo e quais partes do código são simplesmente escolhas
feitas pelo programador.

O código a seguir é equivalente ao código acima:

for slice in pizza:

print(slice)

É mais fácil para o programador iniciante olhar para este código e saber quais partes são
palavras reservadas definidas pelo Python e cujas partes são simplesmente nomes de variáveis
escolhido pelo programador. É bastante claro que o Python não tem fundamentos de
compreensão de pizza e fatias e o fato de que uma pizza consiste em um conjunto de uma ou
mais fatias.

Mas se nosso programa é realmente sobre ler dados e procurar palavras nos dados, pizza e
fatias são nomes de variáveis nada mnemônicos. Escolhendo-os como variável, desviariam a
atenção do significado do programa.

Após um curto período de tempo, você saberá as palavras reservadas mais comuns e você
começará a ver as palavras reservadas saltando para você:

word *in* words*:*\ *print* word

As partes do código definidas pelo Python (for, in, print e :) estão em negrito e as variáveis
escolhidas pelo programador (palavra e palavras) não estão em negrito. Muitos os editores de
texto estão cientes da sintaxe do Python e colorirão as palavras reservadas de maneira
diferente para lhe dar pistas para manter suas variáveis e palavras reservadas separadas.
Depois de um tempo você começará a ler Python e determinará rapidamente o que é uma
variável e o que é uma palavra reservada.
2.13 Depuração

Neste ponto, o erro de sintaxe que você provavelmente cometerá é uma variável com nome
ilegal, como class e yield, que são palavras-chave, ou odd~job e US$, que são caracteres ilegais.

Se você colocar um espaço em um nome de variável, o Python pensa que são dois operandos
sem um operador:

>>> bad name = 5

SyntaxError: invalid syntax

>>> month = 09

File "<stdin>", line 1

month = 09

SyntaxError: invalid token

Para erros de sintaxe, as mensagens de erro não ajudam muito. As mensagens mais comuns

sábios são SyntaxError: invalid syntax e SyntaxError: invalid token, nenhum dos quais é muito
informativo. O erro de tempo de execução que você provavelmente cometerá é “use before
def;” isto é, tentando para usar uma variável antes de atribuir um valor. Isso pode acontecer se
você soletrar um nome da variável errado:

>>> principal = 327.68

>>> interest = principle * rate

NameError: name 'principle' is not defined

Os nomes das variáveis diferenciam maiúsculas de minúsculas, então LaTeX não é o mesmo
que latex.

Neste ponto, a causa mais provável de um erro semântico é a ordem das operações.

Por exemplo, para calcular 1/2π, você pode ser tentado a escrever

>>> 1.0 / 2.0 * pi


Mas a divisão acontece primeiro, então você obteria π/2, que não é a mesma coisa! Não há
como o Python saber o que você quis escrever, então, neste caso, você não receba uma
mensagem de erro; você acabou de obter a resposta errada.

2.14 Glossário
Atribuição - Uma instrução que atribui um valor a uma variável.

Concatenar - Juntar dois operandos ponta a ponta.

Comentário - Informações em um programa destinadas a outros programadores (ou qualquer


pessoa lendo o código-fonte) e não tem efeito na execução do programa. Usasse com #

Avaliar - Para simplificar uma expressão executando as operações para produzir um único
valor.

Expressão - Uma combinação de variáveis, operadores e valores que representa um valor de


resultado único.

Ponto flutuante - Um tipo que representa números com partes fracionárias.

Inteiro - Um tipo que representa números inteiros.

Palavra-chave - Uma palavra reservada que é usada pelo compilador para analisar um
programa; você não pode usar palavras-chave como if, def e while como nomes de variáveis.

Mnemônico - Um auxiliar de memória. Frequentemente damos nomes mnemônicos às


variáveis para nos ajudar lembre-se do que está armazenado na variável.

Operador de módulo - Um operador, denotado com um sinal de porcentagem (%), que


funciona em inteiros e produz o resto quando um número é dividido por outro.

Operando - Um dos valores em que um operador opera.

Operador - Um símbolo especial que representa um cálculo simples como adição,


multiplicação ou concatenação de strings.

Regras de precedência - O conjunto de regras que regem a ordem na qual as expressões


envolvendo múltiplos operadores e operandos são avaliados.

Instrução - Uma seção de código que representa um comando ou ação. Até agora, a
declarações que vimos são atribuições a declaração de expressão de impressão.

String - Um tipo que representa sequências de caracteres.

Type - Uma categoria de valores. Os tipos que vimos até agora são inteiros (tipo int), números
de ponto flutuante (tipo float) e strings (tipo str).

Valor - Uma das unidades básicas de dados, como um número ou string, que um programa
manipula.

Variável - Um nome que se refere a um valor.


2.15 Exercícios
Exercício 2: Escreva um programa que use a entrada para solicitar ao usuário seu nome e
então os acolhe.

Digite seu nome: deex

Olá deex

Exercício 3: Escreva um programa para solicitar ao usuário as horas e a taxa por hora para
Calcular o salário bruto.

Insira as horas: 35

Taxa de entrada: 2,75

Pagamento: 96,25

Não nos preocuparemos em garantir que nosso pagamento tenha exatamente dois dígitos
após o decimal por enquanto. Se quiser, você pode jogar com a função round integrada do
Python para arredondar corretamente o pagamento resultante para duas casas decimais.

Exercício 4: Suponha que executamos as seguintes instruções de atribuição:

Largura = 17

Altura = 12,0

Para cada uma das seguintes expressões, escreva o valor da expressão e o tipo (do valor da
expressão).

1. largura//2

2. largura/2,0

3. altura/3

4. 1 + 2 \* 5

Use o interpretador Python para verificar suas respostas.

Exercício 5: Escreva um programa que solicite ao usuário uma temperatura Celsius, converta a
temperatura em Fahrenheit e imprima a temperatura convertida.
Capítulo 3
Execução condicional

3.1 Expressões booleanas


Uma expressão booleana é uma expressão que é verdadeira ou falsa. A seguir exemplos usam
o operador ==, que compara dois operandos e produz True se eles são iguais e false caso
contrário:

>>> 5 == 5

True

>>> 5 == 6

False

{}

True e False são valores especiais que pertencem à classe bool; eles não são cordas:

>>> type(True)

<class 'bool'>

>>> type(False)

<class 'bool'>

O operador == é um dos operadores de comparação; os outros são:

x != y # x não é igual a y

x>y # x é maior que y

x<y # x é menor que y

x >= y # x é maior ou igual a y

x <= y # x é menor ou igual a y

xéy # x é igual a y
x não é y # x não é o mesmo que y

Embora essas operações provavelmente sejam familiares para você, os símbolos do Python
são diferentes dos símbolos matemáticos para as mesmas operações. Um erro comum é usar
um único sinal de igual (=) em vez de um sinal de igual duplo (==). Lembrar que = é um
operador de atribuição e == é um operador de comparação. Não há algo como =< ou =>.

3.2 Operadores lógicos


Existem três operadores lógicos: and, or, e not. A semântica (significado) de esses operadores
é semelhante ao seu significado em inglês. Por exemplo

x > 0 and x < 10

É verdadeiro somente se x for maior que 0 e menor que 10.

n%2 == 0 or n%3 == 0 é verdadeiro se qualquer uma das condições for verdadeira, ou seja, se o
número é divisível por 2 ou 3.

Finalmente, o operador not nega uma expressão booleana, então not (x > y) é verdadeiro se x
> y é falso; isto é, se x for menor ou igual a y.

Estritamente falando, os operandos dos operadores lógicos devem ser expressões booleanas.
Mas o Python não é muito rígido. Qualquer número diferente de zero é interpretado como
“true”.

>>> 17 and True

True

Essa flexibilidade pode ser útil, mas há algumas sutilezas que podem ser confusas. Você pode
querer evitá-lo até ter certeza de saber o que está fazendo.

3.3 Execução condicional


Para escrever programas úteis, quase sempre precisamos da capacidade de verificar as
condições e alterar o comportamento do programa de acordo. Declarações condicionais nos
da essa habilidade. A forma mais simples é a instrução if:
if x > 0 :

print('x é positivo')

A expressão booleana após a instrução if é chamada de condição. Nós terminamos a instrução


if com dois pontos (:) e a(s) linha(s) após a instrução if são recuadas.

Se a condição lógica for verdadeira, a instrução recuada será executada. Se a condição lógica
for falsa, a instrução recuada será ignorada.

As instruções if têm a mesma estrutura que as definições de função ou para loops. A instrução
consiste em uma linha de cabeçalho que termina com o caractere de dois pontos (:) seguido
por um bloco recuado. Declarações como esta são chamadas de declarações compostas,
porque eles se estendem por mais de uma linha.

Não há limite para o número de instruções que podem aparecer no corpo, mas deve haver
pelo menos um. Ocasionalmente, é útil ter um corpo sem declarações (geralmente como um
substituto para o código que você ainda não escreveu). Naquile caso, você pode usar a
instrução pass, que não faz nada.

if x < 0 :

pass # precisa lidar com valores negativos!

Se você inserir uma instrução if no interpretador Python, o prompt mudará de três chevrons
para três pontos para indicar que você está no meio de um bloco de afirmações, conforme
abaixo:

>>> x = 3

>>> if x < 10:

... print('Small')

...

Small

>>>
3.4 Execução alternativa
Uma segunda forma da instrução if é a execução alternativa, na qual existem duas
possibilidades e a condição determina qual delas será executada. a sintaxe se parece com isso:

if x%2 == 0 :

print('x é par')

else :

print ('x é impar')

Se o resto quando x é dividido por 2 for 0, então sabemos que x é par, e o programa exibe uma
mensagem para esse efeito. Se a condição for falsa, o segundo conjunto de declarações é
executado.

Como a condição deve ser verdadeira ou falsa, exatamente uma das alternativas será
executada. As alternativas são chamadas de ramos, porque são ramos em fluxo de execução.

3.5 Condicionais encadeados


Às vezes há mais de duas possibilidades e precisamos de mais de dois galhos. Uma maneira de
expressar uma computação como essa é uma condicional encadeada:

if x < y:

print('x é menor que y')

elif x > y:

print('x é maior que y')

else:

print('x e y são iguais)

elif é uma abreviação de “else if”. Novamente, exatamente uma ramificação será executada.

Não há limite para o número de instruções elif. Se houver uma cláusula else, ela tem que estar
no final, mas não tem que haver um.
if escolha == 'a':

print('Mal palpite')

elif escolha == 'b':

print('Bom palpite')

elif escolha == 'c':

print('Fecha, mas não está correto')

Cada condição é verificada em ordem. Se o primeiro for falso, o próximo é verificado e


continua. Se uma delas for verdadeira, a ramificação correspondente é executada e a instrução
termina. Mesmo que mais de uma condição seja verdadeira, apenas a primeira ramificação
verdadeira é executada.

3.6 Condicionais aninhados


Uma condicional também pode ser aninhada dentro de outra. Poderíamos ter escrito o
exemplo de três ramos como este:

if x == y:

print('x e y são iguais')

else:

if x < y:

print('x é menor que y')

else:

print('x é maior que y')

A condicional externa contém duas ramificações. A primeira ramificação contém um simples


declaração. A segunda ramificação contém outra instrução if, que tem dois ramos próprios.
Esses dois ramos são declarações simples, embora eles poderiam ter sido declarações
condicionais também.

Embora a indentação das declarações torne a estrutura aparente, condicionais tornam-se


difíceis de ler muito rapidamente. Em geral, é uma boa ideia mas evite-os quando puder.

Os operadores lógicos geralmente fornecem uma maneira de simplificar instruções


condicionais aninhadas.

Por exemplo, podemos reescrever o seguinte código usando uma única condicional:
if 0 <x:

if x < 10:

print('x é um número positivo de um dígito.')

A instrução print é executada apenas se passarmos por ambas as condicionais, portanto, pode
obter o mesmo efeito com o operador and:

if 0 < x end x < 10:

print('x é um número positivo de um dígito.')

3.7 Capturando exceções usando try e except


Anteriormente, vimos um segmento de código em que usamos as funções raw_input e int
para lê e analisa um número inteiro inserido pelo usuário. Também vimos quão traiçoeiro fazer
isso pode ser:

>>> velocidade = input(prompt)

Qual é a velocidade de uma andorinha?

uma andorinha africana ou brasileira?

>>> int(velocidade)

alueError: invalid literal for int() with base 10:

Quando estamos executando essas instruções no interpretador Python, obtemos um novo


prompt do intérprete, pense "oops" e passe para a próxima instrução.

No entanto, se você colocar esse código em um script Python e esse erro ocorrer, seu script
imediatamente para em suas trilhas com um traceback. Não executa a seguinte declaração.

Aqui está um exemplo de programa para converter uma temperatura Fahrenheit em uma
temperatura Celsius:

inp = input('Digite a temperatura Fahrenheit: ')

fahr = float(inp)

cel = (fahr - 32,0) * 5,0 / 9,0

print(cel)
# Código: http://www.pythonlearn.com/code3/fahren.py

Se executarmos este código e fornecermos uma entrada inválida, ele simplesmente falhará
com um mensagem de erro:

python fahren.py

Digite Fahrenheit Temperatura: 72

22.22222222222222

Existe uma estrutura de execução condicional incorporada ao Python para lidar com esses
tipos de erros esperados e inesperados denominados “try/except”. A ideia de tentar exceto
que você sabe que alguma sequência de instruções pode ter um problema e você deseja
adicionar algumas instruções a serem executadas se ocorrer um erro. Esses instruções extras
(o bloco exceto) são ignoradas se não houver erro.

Você pode pensar no recurso try and except em Python como uma “apólice de seguro” em
uma sequência de declarações.

Podemos reescrever nosso conversor de temperatura da seguinte maneira:

inp = input('Digite a temperatura Fahrenheit:')

try:

fahr = float(inp)

cel = (fahr - 32,0) * 5,0 / 9,0

print(célula)

except:

print('Digite um numero')

# Código: http://www.pythonlearn.com/code3/fahren2.py

O Python começa executando a sequência de instruções no bloco try. Se tudo correr bem, ele
pula o bloco except e continua. Se ocorrer uma exceção no bloco try, o Python sai do bloco try
e executa a sequência de instruções no bloco except.
python fahren2.py

Digite Fahrenheit Temperatura: 72

22.22222222222222

python fahren2.py

Digite Fahrenheit Temperatura: blablabla

Por favor, coloque um numero

Tratar uma exceção com uma instrução try é chamado de captura de exceção. Neste exemplo,
a cláusula except imprime uma mensagem de erro. Em geral, pegar uma exceção lhe dá uma
chance de corrigir o problema, ou tentar novamente, ou pelo menos encerrar o programa
graciosamente.

3.8 Avaliação de curto-circuito de expressões lógicas


Quando o Python está processando uma expressão lógica como x >= 2 e (x/y) > 2, ele avalia a
expressão da esquerda para a direita. Por causa da definição, e, se x é menor que 2, a
expressão x >= 2 é False e então toda a expressão é Falsa independentemente de (x/y) > 2 ser
avaliado como Verdadeiro ou Falso.

Quando o Python detecta que não há nada a ganhar avaliando o resto de uma expressão
lógica, ele interrompe sua avaliação e não faz os cálculos no restante da expressão lógica.
Quando a avaliação de uma expressão lógica para porque o valor geral já é conhecido, é
chamado de curto-circuito da avaliação.

Embora isso possa parecer um ponto delicado, o comportamento do curto-circuito leva a uma
solução inteligente técnica chamada padrão guardião. Considere a seguinte sequência de
código no interpretador Python:

>>> x = 6

>>> y = 2

>>> x >= 2 and (x/y) > 2

True

>>> x = 1

>>> y = 0

>>> x >= 2 and (x/y) > 2

False

>>> x = 6
>>> y = 0

>>> x >= 2 and (x/y) > 2

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: division by zero

>>>

O terceiro cálculo falhou porque o Python estava avaliando (x/y) e y era zero, que causa um
erro de tempo de execução. Mas o segundo exemplo não falhou porque a primeira parte da
expressão x >= 2 avaliada como False, então o (x/y) nunca foi executado devido à regra de
curto-circuito e não houve erro. Podemos construir a expressão lógica para colocar
estrategicamente uma avaliação de guarda logo antes da avaliação que pode causar um erro
da seguinte forma:

>>> x = 1

>>> y = 0

>>> x >= 2 and y != 0 and (x/y) > 2

False

>>> x = 6

>>> y = 0

>>> x >= 2 and y != 0 and (x/y) > 2

False

>>> x >= 2 and (x/y) > 2 and y != 0

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: division by zero

>>>

Na primeira expressão lógica, x >= 2 é False, então a avaliação para no end. Na segunda
expressão lógica, x >= 2 é verdadeiro, mas y != 0 é falso, então nunca alcançara (x/y).

Na terceira expressão lógica, o y != 0 está após o cálculo (x/y), então a expressão falha com um
erro.

Na segunda expressão, dizemos que y != 0 atua como um guarda para garantir que só
executara (x/y) se y for diferente de zero.
3.9 Depuração
O traceback Python exibido quando ocorre um erro contém muitas informações, mas pode ser
esmagadora. As partes mais úteis são geralmente:

• Que tipo de erro foi

• Onde ocorreu.

Erros de sintaxe geralmente são fáceis de encontrar, mas existem algumas dicas. Erros de
Espaço em branco podem ser complicados porque espaços e tabulações são invisíveis e
estamos acostumados a ignora-los.

>>> x = 5

>>> y = 6

File "<stdin>", line 1

y=6

IndentationError: unexpected indent

Neste exemplo, o problema é que a segunda linha é recuada por um espaço. Mas a mensagem
de erro aponta para y, o que é enganoso. Em geral, as mensagens de erro indica onde o
problema foi descoberto, mas o erro real pode ser anterior no código, às vezes em uma linha
anterior.

Em geral, as mensagens de erro informam onde o problema foi descoberto, mas são muitas
vezes onde não foi causado.

3.10 Glossário
Corpo - A sequência de instruções dentro de uma instrução composta.

Expressão booleana - Uma expressão cujo valor é True ou False.

Ramificação - Uma das sequências alternativas de instruções em uma instrução condicional.

Condicional encadeado - Uma instrução condicional com uma série de alternativas galhos.

Operador de comparação - Um dos operadores que compara seus operandos: ==, !=, >, <, >= e
<=.

Instrução condicional - Uma instrução que controla o fluxo de execução dependente em


alguma condição.
Condição - A expressão booleana em uma instrução condicional que determina qual ramo é
executado.

Instrução composta - Uma instrução que consiste em um cabeçalho e um corpo. O cabeçalho


termina com dois pontos (:). O corpo é recuado em relação ao cabeçalho.

Padrão guardião - Onde construímos uma expressão lógica com componentes adicionais
parisons para aproveitar o comportamento de curto-circuito.

Operador lógico - Um dos operadores que combina expressões booleanas: end, or, e not.

Condicional aninhada - Uma instrução condicional que aparece em uma das ramificações de
outra declaração condicional.

Traceback - Uma lista das funções que estão sendo executadas, impressa quando uma exceção
ocorre.

Curto-circuito - Quando o Python está no meio da avaliação de uma expressão lógica e


interrompe a avaliação porque o Python conhece o valor final para a expressão sem precisar
avaliar o restante da expressão.

3.11 Exercícios
Exercício 1: Reescreva seu cálculo de pagamento para dar ao empregado 1,5 vezes a taxa
horária para horas trabalhadas acima de 40 horas.

Insira as horas: 45

Digite Taxa: 10

Pagamento: 475,0

Exercício 2: Reescreva seu programa de pagamento usando try e except para que seu
programa lide com entrada não numérica graciosamente, imprimindo uma mensagem e
saindo do programa. A seguir, duas execuções do programa:

Insira as horas: 20

Digite Taxa: nove

Erro, insira a entrada numérica

Insira as horas: quarenta

Erro, insira a entrada numérica


Exercício 3: Escreva um programa para solicitar uma pontuação entre 0,0 e 1,0. Se a
pontuação estiver fora do intervalo, imprima uma mensagem de erro. Se a pontuação estiver
entre 0,0 e 1,0, imprima uma nota usando a seguinte tabela:

Nota de Pontuação Nota

>= 0,9 A

>= 0,8 B

>= 0,7 C

>= 0,6 D

< 0,6 F

~~~

Insira a pontuação: 0,95 A ~~

Insira a pontuação: perfeito

Pontuação ruim

Insira a pontuação: 10,0

Pontuação ruim

Insira a pontuação: 0,75

Insira a pontuação: 0,5

Execute o programa repetidamente como mostrado acima para testar os vários valores
diferentes para a entrada.
Capítulo 4
Funções

4.1 Chamadas de função


No contexto da programação, uma função é uma sequência nomeada de instruções que
executa um cálculo. Ao definir uma função, você especifica o nome e a sequência de
declarações. Mais tarde, você pode “chamar” a função pelo nome. Nós temos um exemplo de
chamada de função:

>>> type(32)

<class 'int'>

O nome da função é tipo. A expressão entre parênteses é chamada de argumento da função. O


argumento é um valor ou variável que estamos passando na função como entrada para a
função. O resultado, para a função de tipo, é o tipo do argumento.

É comum dizer que uma função “pega” um argumento e “devolve” um resultado. O resultado
é chamado de valor de retorno.

4.2 Funções integradas


O Python fornece várias funções internas importantes que podemos usar sem necessidade de
fornecer a definição da função. Os criadores do Python escreveram um conjunto de funções
para resolver problemas comuns e incluí-los em Python para nós usarmos.

As funções max e min nos fornecem os maiores e menores valores em uma lista,
positivamente:

>>> max('Hello world')

'w'

>>> min('Hello world')

''

>>>
A função max nos diz o “maior caractere” na string (que acaba sendo ser a letra “w”) e a
função min nos mostra o menor caractere (que acaba por ser um espaço).

Outra função interna muito comum é a função len que nos diz quantos itens estão em seu
argumento. Se o argumento para len for uma string, ele retorna o número de caracteres na
string.

>>> len('Olá mundo')

11

>>>

Essas funções não se limitam a olhar para strings. Eles podem operar em qualquer conjunto de
valores, como veremos nos próximos capítulos.

Você deve tratar os nomes das funções incorporadas como palavras reservadas (ou seja, evite
usar “max” como um nome de variável).

4.3 Funções de conversão de tipo


Python também fornece funções integradas que convertem valores de um tipo para outro. A
função int pega qualquer valor e o converte em um número inteiro, se puder, ou o contrário:

>>> int('32')

32

>>> int('Hello')

ValueError: invalid literal for int() with base 10: 'Hello'

int pode converter valores de ponto flutuante em inteiros, mas não arredonda; isso corta fora
a parte da fração:

>>> int(3.99999)

>>> int(-2.3)

-2

float converte números inteiros e strings em números de ponto flutuante:


>>> float(32)

32.0

>>> float('3.14159')

3.14159

Por fim, str converte seu argumento em uma string:

>>> str(32)

'32'

>>> str(3.14159)

'3.14159'

4.4 Números aleatórios


Dadas as mesmas entradas, a maioria dos programas de computador gera as mesmas saídas a
cada tempo, então eles são ditos determinísticos. O determinismo geralmente é uma coisa
boa, já que esperamos que o mesmo cálculo produza o mesmo resultado. Para algumas
aplicações. No entanto, queremos que o computador seja imprevisível. Os jogos são um óbvio
exemplo, mas há mais.

Tornar um programa verdadeiramente não determinístico acaba não sendo tão fácil, mas há
maneiras de fazê-lo, pelo menos, parecer não determinístico. Uma delas é usar algoritmos que
geram números pseudoaleatórios. Números pseudoaleatórios não são verdadeiramente
aleatórios porque são gerados por uma computação determinística, mas apenas olhando para
os números, é praticamente impossível distingui-los do aleatório.

O módulo random fornece funções que geram números pseudo-aleatórios (que chamarei
simplesmente de “aleatório” daqui em diante).

A função random retorna um float aleatório entre 0,0 e 1,0 (incluindo 0,0 mas não 1.0). Cada
vez que você chama aleatório, obtém o próximo número em uma longa série.

Para ver uma amostra, execute este loop:

import random

for i in range(10):

x = random.random()

print(x)
Este programa produz a seguinte lista de 10 números aleatórios entre 0,0 e 1.0, mas não
incluindo 1.0.

0.11132867921152356

0.5950949227890241

0.04820265884996877

0.841003109276478

0.997914947094958

0.04842330803368111

0.7416295948208405

0.510535245390327

0.27447040171978143

0.028511805472785867

Exercício 1: Execute o programa em seu sistema e veja quais números você obtém. Corra o
programa mais de uma vez e veja quais números você obtém.

A função aleatória é apenas uma das muitas funções que lidam com números aleatórios.

A função randint pega os parâmetros baixo e alto e retorna um número inteiro entre baixo e
alto (incluindo ambos).

>>> random.randint(5, 10)

>>> random.randint(5, 10)

Para escolher um elemento de uma sequência aleatoriamente, você pode usar choice:

>>> t = [1, 2, 3]

>>> random.choice(t)

>>> random.choice(t)

3
O módulo random também fornece funções para gerar valores aleatórios de con-

distribuições contínuas, incluindo gaussiana, exponencial, gama e algumas outras.

4.5 Funções matemáticas


Python tem um módulo matemático que fornece a maioria das funções matemáticas
familiares.

Antes de podermos usar o módulo, temos que importá-lo:

>>> import math

Essa instrução cria um objeto de módulo chamado math. Se você imprimir o módulo objeto,
você obtém algumas informações sobre ele:

>>> print(math)

<module 'math' (built-in)>

O objeto do módulo contém as funções e variáveis definidas no módulo. Para acessar uma das
funções, você deve especificar o nome do módulo e o nome da função, separados por um
ponto (também conhecido como ponto). Este formato é chamado notação de ponto.

>>> relação = potência_sinal / potência_ruído

>>> decibéis = 10 * math.log10(ratio)

>>> radianos = 0,7

>>> altura = math.sin(radianos)

O primeiro exemplo calcula o logaritmo de base 10 da relação sinal-ruído. O módulo math


também fornece uma função chamada log que calcula o logaritmo base. O segundo exemplo
encontra o seno de radianos. O nome da variável é uma dica que as outras funções
trigonométricas (cos, tan, etc.) recebem argumentos em radianos. Para converter de graus
para radianos, dividida por 360 e multiplique por 2π:

>>> graus = 45

>>> radianos = graus / 360,0 * 2 * math.pi

>>> math.sin(radianos)
0,7071067811865476

A expressão math.pi obtém a variável pi do módulo math. O valor desta variável é uma
aproximação de π, com precisão de cerca de 15 dígitos.

Se você conhece sua trigonometria, pode verificar o resultado anterior comparando-o para a
raiz quadrada de dois dividido por dois:

>>> math.sqrt(2) / 2.0

0.7071067811865476

4.6 Adicionando novas funções


Até agora, usamos apenas as funções que vêm com o Python, mas também é possível
adicionar novas funções. Uma definição de função especifica o nome de uma nova função e a
sequência de instruções que são executadas quando a função é chamada.

Uma vez que definimos uma função, podemos reutilizá-la várias vezes ao longo de nosso
programa.

Aqui está um exemplo:

def print_lyrics():

print("Sou um lenhador e estou bem.")

print('Durmo a noite toda e trabalho o dia todo.')

def é uma palavra-chave que indica que esta é uma definição de função. O nome da função é
print_lyrics. As regras para nomes de função são as mesmas que para nomes de variáveis:
letras, números e alguns sinais de pontuação são legais, mas o primeiro caractere não pode ser
um número. Você não pode usar uma palavra-chave como o nome de uma função, e você deve
evitar ter uma variável e uma função com o mesmo nome.

Os parênteses vazios após o nome indicam que esta função não leva nenhum argumento.
Posteriormente, construiremos funções que recebem argumentos como entrada.

A primeira linha da definição da função é chamada de cabeçalho; o resto é chamado de corpo.


O cabeçalho deve terminar com dois pontos e o corpo deve ser recuado.

Por convenção, o recuo é sempre de quatro espaços. O corpo pode conter qualquer número
de declarações.
As strings nas instruções de impressão são colocadas entre aspas. Aspas simples e duplas
fazem a mesma coisa; a maioria das pessoas usa aspas simples, exceto em casos como este
onde uma aspa simples (que também é um apóstrofo) aparece na string.

Se você digitar uma definição de função no modo interativo, o interpretador imprimirá


reticências (. . .) para avisar que a definição não está completa:

>>> def print_lyrics():

... print("Sou um lenhador e estou bem.")

... print('Durmo a noite toda e trabalho o dia todo.')

...

Para finalizar a função, você deve inserir uma linha vazia (isso não é necessário em um roteiro).

A definição de uma função cria uma variável com o mesmo nome.

>>> print(print_lyrics)

<function print_lyrics at 0xb7e99e9c>

>>> print(type(print_lyrics))

<class 'function'>

O valor de print_lyrics é um objeto de função, que tem o tipo “função”. A sintaxe para chamar
a nova função é a mesma das funções internas:

>>> print_lyrics()

Sou um lenhador e estou bem.

Durmo a noite toda e trabalho o dia todo.

Depois de definir uma função, você pode usá-la dentro de outra função. Por exemplo, para
repetir o refrão anterior, poderíamos escrever uma função chamada repeat_lyrics:

def repeat_lyrics():

print_lyrics()

print_lyrics()
E então chame repeat_lyrics:

>>> repeat_lyrics()

Sou um lenhador e estou bem.

Durmo a noite toda e trabalho o dia todo.

Sou um lenhador e estou bem.

Durmo a noite toda e trabalho o dia todo.

Mas não é assim que a banda toca.

4.7 Definições e usos


Reunindo os fragmentos de código da seção anterior, todo o programa se parece com isso:

def print_lyrics():

print("Sou um lenhador e estou bem.")

print('Durmo a noite toda e trabalho o dia todo.')

def repeat_lyrics():

print_lyrics()

print_lyrics()

repeat_lyrics()

# Code: http://www.pythonlearn.com/code3/lyrics.py

Este programa contém duas definições de função: print_lyrics e repeat_lyrics. As definições de


função são executadas como outras instruções, mas o efeito é criar objetos de função. As
instruções dentro da função não são executadas até que a função seja chamada e a definição
da função não gere nenhuma saída.

Como você pode esperar, você precisa criar uma função antes de executá-la. Em outras
palavras, a definição da função deve ser executada antes da primeira vez que ela é chamada.
Exercício 2: Mova a última linha deste programa para cima, de modo que a chamada da função
apareca antes das definições. Execute o programa e veja qual mensagem de erro você acha.

Exercício 3: Mova a chamada de função de volta para o fundo e mova a definição de


print_lyrics após a definição de repeat_lyrics. O que acontece quando você executar este
programa?

4.8 Fluxo de execução


Para garantir que uma função seja definida antes de seu primeiro uso, você deve saber a
ordem na qual as instruções são executadas, que é chamada de fluxo de execução.

A execução sempre começa na primeira instrução do programa. As declarações são


executados um de cada vez, de cima para baixo.

As definições de função não alteram o fluxo de execução do programa, mas as instruções


dentro da função não são executadas até que a função seja chamada.

Uma chamada de função é como um desvio no fluxo de execução. Em vez de ir para a próxima
instrução, o fluxo salta para o corpo da função, executa todas as instruções lá, e depois volta
para continuar de onde parou.

Isso parece bastante simples, até você se lembrar de que uma função pode chamar outra
enquanto estiver no meio de uma função.

Felizmente, o Python é bom em rastrear onde está, então cada vez que uma função terminar,
o programa continua de onde parou na função que o chamou. Quando chega ao final do
programa, ele termina.

Qual é a moral dessa história sórdida? Quando você lê um programa, nem sempre deseja ler
de cima para baixo. Às vezes faz mais sentido se você seguir o fluxo de execução.

4.9 Parâmetros e argumentos


Algumas das funções internas que vimos requerem argumentos. Por exemplo, quando você
chama math.sin e passa um número como argumento. Algumas funções levam mais de um
argumento: math.pow leva dois, a base e o expoente.Dentro da função, os argumentos são
atribuídos a variáveis chamadas parâmetros.

Aqui está um exemplo de uma função definida pelo usuário que recebe um argumento:

def print_twice(bruce):

print(bruce)

print(bruce)
Esta função atribui o argumento a um parâmetro chamado bruce. Quando a função é
chamada, ela imprime o valor do parâmetro (seja ele qual for) duas vezes.

Esta função funciona com qualquer valor que possa ser impresso.

>>> print_twice('Spam')

Spam

Spam

>>> print_twice(17)

17

17

>>> import math

>>> print_twice(math.pi)

3.141592653589793

3.141592653589793

As mesmas regras de composição que se aplicam a funções integradas também se aplicam a


funções definidas pelo usuário, para que possamos usar qualquer tipo de expressão como um
argumento para “imprimir duas vezes”:

(print_twice)

>>> print_twice('Spam '*4)

Spam Spam Spam Spam

Spam Spam Spam Spam

>>> print_twice(math.cos(math.pi))

-1.0

-1.0

O argumento é avaliado antes da função ser chamada, então nos exemplos, as expressões
“Spam '*4andmath.cos(math.pi)' são avaliadas apenas uma vez.

Você também pode usar uma variável como argumento:


>>> michael = 'Eric, the half a bee.'

>>> print_twice(michael)

Eric, the half a bee.

Eric, the half a bee.

O nome da variável que passamos como argumento (michael) não tem nada a ver com o nome
do parâmetro (bruce). Não importa o valor chamado de volta para casa (no chamador); aqui
em print_twice, chamamos todo mundo de bruce.

4.10 Funções frutíferas e funções nulas


Algumas das funções que estamos usando, como as funções matemáticas, produzem
resultados; por falta de um nome melhor, eu as chamo de funções frutíferas. Outras funções,
como print_twice, executa uma ação, mas não retorna um valor. Eles são chamados de
funções vazias.

Quando você chama uma função frutífera, quase sempre quer fazer algo com o resultado; por
exemplo, você pode atribuí-lo a uma variável ou usá-lo como parte de uma expressão:

x = math.cos(radians)

golden = (math.sqrt(5) + 1) / 2

Quando você chama uma função no modo interativo, o Python exibe o resultado:

>>> math.sqrt(5)

2.23606797749979

Mas em um script, se você chamar uma função frutífera e não armazenar o resultado da
função em uma variável, o valor de retorno desaparece na névoa!

math.sqrt(5)

Este script calcula a raiz quadrada de 5, mas como não armazena o resultado em uma variável
ou exibir o resultado, não é muito útil.
As funções vazias podem exibir algo na tela ou ter algum outro efeito, mas eles não têm um
valor de retorno. Se você tentar atribuir o resultado a uma variável, você obtém um valor
especial chamado None.

>>> result = print_twice('Bing')

Bing

Bing

>>> print(result)

None

O valor None não é o mesmo que a string “None”. É um valor especial que tem seu próprio
tipo:

>>> print(type(None))

<class 'NoneType'>

Para retornar um resultado de uma função, usamos a instrução return em nossa função. Por
exemplo, poderíamos criar uma função muito simples chamada addtwo que adiciona dois
números juntos e retorna um resultado.

def addtwo(a, b):

added = a + b

return added

x = addtwo(3, 5)

print(x)

# Code: http://www.pythonlearn.com/code3/addtwo.py

Quando este script for executado, a instrução print imprimirá “8” porque a função addtwo foi
chamada com 3 e 5 como argumentos. Dentro da função, os parâmetros aeb foram 3 e 5,
respectivamente. A função calculou a soma de os dois números e os colocou na variável de
função local chamada Added. Então ele usou a instrução return para enviar o valor calculado
de volta ao código de chamada como o resultado da função, que foi atribuído à variável x e
impresso.
4.11 Por que funções?
Pode não estar claro por que vale a pena dividir um programa em funções. Mas, existem vários
motivos:

• A criação de uma nova função oferece a oportunidade de nomear um grupo de instruções,


o que torna seu programa mais fácil de ler, entender e depurar.

• As funções podem tornar um programa menor eliminando o código repetitivo. Mais tarde,
se você fizer uma alteração, basta fazê-la em um só lugar.

• Dividir um programa longo em funções permite depurar as partes uma a uma vez e, em
seguida, montá-los em um todo de trabalho.

• Funções bem projetadas geralmente são úteis para muitos programas. Uma vez que você
escreve e depura um, você pode reutilizá-lo.

No restante do e-book, frequentemente usaremos uma definição de função para explicar um


conceito. Parte da habilidade de criar e usar funções é ter uma função, capturar
adequadamente uma ideia como “encontrar o menor valor em uma lista de valores”. Mais
tarde mostraremos o código que encontra o menor em uma lista de valores e apresentaremos
para você como uma função chamada min que recebe uma lista de valores como seu
argumento e retorna o menor valor da lista.

4.12 Depuração
Se você estiver usando um editor de texto para escrever seus scripts, poderá ter problemas
com espaços e tabulações. A melhor forma de evitar esses problemas é utilizar os espaços
exclusivamente (sem abas). A maioria dos editores de texto que conhecem o Python fazem
isso por padrão, mas alguns não.

Tabs e espaços geralmente são invisíveis, o que os torna difíceis de depurar, então tente
encontrar um editor que gerencie o recuo para você.

Além disso, não se esqueça de salvar seu programa antes de executá-lo. Algum
desenvolvimento ambientes fazem isso automaticamente, mas alguns não. Nesse caso, o
programa que você está vendo no editor de texto não é o mesmo que o programa que você
está correndo.

A depuração pode demorar muito se você continuar executando o mesmo programa incorreto
de novo e de novo!

Certifique-se de que o código que você está vendo é o código que você está executando. Se
você não tem certeza, coloque algo como print("hello") no início do programa e execute-o
novamente. Se você não vê o hello, você não está executando o programa certo!

4.13 Glossário
Algoritmo - Um processo geral para resolver uma categoria de problemas.

Argumento - Um valor fornecido a uma função quando a função é chamada. Este valor é
atribuído ao parâmetro correspondente na função.

Corpo - A sequência de instruções dentro de uma definição de função.

Composição - Usando uma expressão como parte de uma expressão maior, ou uma declaração
como parte de uma declaração maior.

Determinístico - Pertencente a um programa que faz a mesma coisa toda vez que é executado,
dadas as mesmas entradas.

Notação de ponto - A sintaxe para chamar uma função em outro módulo especificando o
nome do módulo seguido por um ponto (ponto) e o nome da função.

Fluxo de execução - A ordem na qual as instruções são executadas durante o correr do


programa.

Função frutífera - Uma função que retorna um valor.

Função - Uma sequência nomeada de instruções que executa alguma operação útil. As funções
podem ou não receber argumentos e podem ou não produzir um resultado.

Chamada de função - Uma instrução que executa uma função. Consiste na função nome
seguido por uma lista de argumentos.

Definição de função - Uma instrução que cria uma nova função, especificando seu nome,
parâmetros e as instruções que ele executa.

Objeto de função - Um valor criado por uma definição de função. O nome da função é uma
variável que se refere a um objeto de função.

Cabeçalho - A primeira linha de uma definição de função.

Instrução de importação - Uma instrução que lê um arquivo de módulo e cria um módulo


objeto.

Objeto de módulo - Um valor criado por uma instrução de importação que fornece acesso aos
dados e o código definidos em um módulo.

Parâmetro - Um nome usado dentro de uma função para se referir ao valor passado como um
argumento.

Pseudoaleatório - Pertencente a uma sequência de números que parecem ser aleatórios, mas
são gerados por um programa determinístico.

Valor de retorno - O resultado de uma função. Se uma chamada de função for usada como
uma expressão, o valor de retorno é o valor da expressão.

função nula - Uma função que não retorna um valor.


4.14 Exercícios
Exercício 4: Qual é o propósito da palavra-chave “def” em Python?

a) É uma gíria que significa “o seguinte código é muito legal”

b) Indica o início de uma função

c) Indica que a seguinte seção de código recuada deve ser armazenada para uso posterior

d) b e c são verdadeiras

e) Nenhuma das anteriores

Exercício 5: O que o seguinte programa em Python imprimirá?

def fred():

print("Zap")

def jane():

print("ABC")

jane()

fred()

jane()

a) Zap ABC jane fred jane

b) Zap ABC Zap

c) ABC Zap jane

d) ABC Zap ABC

e) Zap Zap Zap

Exercício 6: Reescreva seu cálculo de pagamento com tempo e meio para horas extras e crie
uma função chamada computepay que leva dois parâmetros (horas e taxa).

Insira as horas: 45

Digite Taxa: 10

Pagamento: 475,0

Exercício 7: Reescreva o programa de notas do capítulo anterior usando uma função chamada
computergrade que recebe uma pontuação como parâmetro e retorna uma nota como uma
corda.
Nota Pontuação

> 0,9 A

> 0,8 B

> 0,7 C

> 0,6 D

<= 0,6 F

Execução do Programa:

Insira a pontuação: 0,95

Insira a pontuação: perfeito

Pontuação ruim

Insira a pontuação: 10,0

Pontuação ruim

Insira a pontuação: 0,75

Insira a pontuação: 0,5

Execute o programa repetidamente para testar os vários valores diferentes para entrada.
Capítulo 5
Iteração

5.1 Atualizando variáveis


Um padrão comum em instruções de atribuição é uma instrução de atribuição que atualiza
data de uma variável, onde o novo valor da variável depende do antigo.

x=x+1

Isso significa “obter o valor atual de x, adicionar 1 e, em seguida, atualizar x com o novo valor."

Se você tentar atualizar uma variável que não existe, receberá um erro, porque o Python avalia
o lado direito antes de atribuir um valor a x:

>>> x = x + 1

NameError: name 'x' is not defined

Antes de atualizar uma variável, você deve inicializá-la, geralmente com uma simples
atribuição:

>>> x = 0

>>> x = x + 1

Atualizar uma variável adicionando 1 é chamado de incremento; subtrair 1 é chamado de


decremento.

5.2 A instrução while


Os computadores são frequentemente usados para automatizar tarefas repetitivas. Repetição
idêntica ou tarefas semelhantes sem cometer erros, é algo que os computadores fazem bem e
as pessoas fazer mal. Como a iteração é tão comum, o Python fornece vários recursos para
facilitar.
Uma forma de iteração em Python é a instrução while. Aqui está como um programa simples
que faz uma contagem regressiva de cinco e depois diz “Blastoff!”.

n=5

while n > 0:

print(n)

n=n-1

print('Blastoff!')

Você quase pode ler a instrução while como se fosse em inglês. Significa: “Enquanto n for
maior que 0, exiba o valor de n e reduza o valor de n em 1. Quando chegar a 0, saia da
instrução while e exiba a palavra Blastoff!”

Mais formalmente, aqui está o fluxo de execução de uma instrução while:

1. Avalie a condição, obtendo Verdadeiro ou Falso.

2. Se a condição for falsa, saia da instrução while e continue a execução na próxima


declaração.

3. Se a condição for verdadeira, execute o corpo e volte para a etapa 1.

Esse tipo de fluxo é chamado de loop porque a terceira etapa retorna a principal. Cada vez que
executamos o corpo do loop, chamamos de iteração. Para o loop acima, diríamos: “Ele teve
cinco iterações”, o que significa que o corpo do loop foi executado cinco vezes.

O corpo do loop deve alterar o valor de uma ou mais variáveis para que eventualmente, a
condição se torne falsa e o loop termina. Chamamos a variável capaz de mudar cada vez que o
loop é executado e controla quando o loop termina de variável de iteração. Se não houver
variável de iteração, o loop será repetido indefinidamente, resultando em um loop infinito.

5.3 Loops infinitos


Uma fonte inesgotável de diversão para os programadores é a observação de que as
instruções sobre o xampu, “Ensaboe, enxágue, repita”, são um loop infinito porque não á
nenhuma variável de iteração informando quantas vezes o loop deve ser executado.

No caso da contagem regressiva, podemos provar que o loop termina porque sabemos que o
valor de n é finito, e podemos ver que o valor de n fica menor a cada tempo através do loop,
então, eventualmente, temos que chegar a 0. Outras vezes, um loop é obviamente infinito
porque não tem nenhuma variável de iteração.
5.4 “Loops infinitos” e break
Às vezes, você não sabe que é hora de terminar um loop até chegar na metade do corpo.
Nesse caso, você pode escrever um loop infinito de propósito e usar o break para sair do loop.

Este loop é obviamente um loop infinito porque a expressão lógica na declaração while é
simplesmente a constante lógica True:

n = 10

while True:

print(n, end=' ')

n=n-1

print('Done!')

Se você cometer o erro e executar este código, aprenderá rapidamente como parar um
processo Python descontrolado em seu sistema ou, descubra onde está o botão de desligar do
seu computador. Este programa será executado para sempre ou até que a bateria acabe
porque a expressão lógica no topo do loop é sempre verdadeira em virtude do fato de que a
expressão é o valor constante True.

Embora este seja um loop infinito disfuncional, ainda podemos usar esse padrão para construir
loops úteis, desde que adicionemos cuidadosamente o código ao corpo do loop para
explicitamente saia do loop usando break quando atingirmos a condição de saída.

Por exemplo, supunhetamos que você queira obter informações do usuário até que ele digite
concluído.

Você poderia escrever:

while True:

line = input('> ')

if line == 'done':

break

print(line)

print('Done!')

# Code: http://www.pythonlearn.com/code3/copytildone1.py
A condição do loop é True, que é sempre verdadeira, então o loop é executado repetidamente
até atingir a instrução break.

Cada vez, ele solicita ao usuário um colchete angular. Se o usuário digitar feito, a instrução
break sai do loop. Caso contrário, o programa ecoa qualquer coisa, o usuário digita e volta para
o início do loop. Aqui está um exemplo de execução:

> hello there

hello there

> finished

finished

> done

Done!

Essa maneira de escrever loops while é comum porque você pode verificar a condição em
qualquer lugar do loop (não apenas no topo) e você pode expressar a condição de parada
afirmativamente (“pare quando isso acontecer”) em vez de negativamente (“continue até isso
acontece.").

5.5 Finalizando iterações com continue


Às vezes você está em uma iteração de um loop e deseja terminar a iteração atual e pule
imediatamente para a próxima iteração. Nesse caso, você pode usar a instrução continue para
pular para a próxima iteração sem terminar o corpo do loop para a iteração atual.

Aqui está um exemplo de um loop que copia sua entrada até que o usuário digite “done”, mas
trata as linhas que começam com o caractere hash como linhas que não devem ser impressas
(tipo como comentários do Python).

while True:

line = input('> ')

if line[0] == '#':

continue

if line == 'done':

break

print(line)

print('Done!')
# Code: http://www.pythonlearn.com/code3/copytildone2.py

Aqui está um exemplo de execução deste novo programa com continue adicionado.

> hello there

hello there

> # don't print this

> print this!

print this!

> done

Done!

Todas as linhas são impressas, exceto aquela que começa com o sinal de jogo da velha, porque
quando o continue é executado, ele termina a iteração atual e volta para a instrução while
para iniciar a próxima iteração, ignorando assim a instrução print.

5.6 Loops definidos usando for


Às vezes, queremos percorrer um conjunto de coisas, como uma lista de palavras, as linhas em
um arquivo ou uma lista de números. Quando temos uma lista de coisas para percorrer, pode
construir um loop definido usando uma instrução for. Chamamos a instrução while um loop
indefinido porque ele simplesmente faz um loop até que alguma condição se torne falsa,
Considerando que o loop for está percorrendo um conjunto conhecido de itens, então ele
percorre tantas iterações quantos forem os itens do conjunto.

A sintaxe de um loop for é semelhante ao loop while em que há um for declaração e um corpo
de loop:

friends = ['Joseph', 'Glenn', 'Sally']

for friend in friends:

print('Happy New Year:', friend)

print('Done!')

Em termos de Python, a variável friends é uma lista1 de três strings e o loop for percorre a lista
e executa o corpo uma vez para cada uma das três strings, a lista resultando nesta saída:
Happy New Year: Joseph

Happy New Year: Glenn

Happy New Year: Sally

Done!

Traduzir este loop for para o inglês não é tão direto quanto o loop while, mas se você pensar
em amigos como um conjunto, é assim: “Execute as instruções no corpo do for loop uma vez
para cada amigo no conjunto chamado friends.”

Observando o loop for, for e in são palavras-chave reservadas do Python e amigo e amigos são
variáveis.

for friend in friends:

print('Happy New Year:', friend)

Em particular, friend é a variável de iteração para o loop for. O amigo variável muda para cada
iteração do loop e controla quando o loop for é concluído. A variável de iteração percorre
sucessivamente as três strings armazenadas na variável amigos.

5.7 Padrões de loop


Frequentemente, usamos um loop for ou while para percorrer uma lista de itens ou o
conteúdo de um arquivo ou estamos procurando por algo como o maior ou menor valor do
dados que examinamos.

Esses loops são geralmente construídos por:

• Inicializando uma ou mais variáveis antes do início do loop

• Realizar algum cálculo em cada item no corpo do loop, possivelmente alterando as


variáveis no corpo do loop

• Olhando para as variáveis resultantes quando o loop é concluído

Utilizaremos uma lista de números para demonstrar os conceitos e a construção destes


padrões de loop.

5.7.1 Loops de contagem e soma


Por exemplo, para contar o número de itens em uma lista, escreveríamos o seguinte para loop:
count = 0

for itervar in [3, 41, 12, 9, 74, 15]:

count = count + 1

print('Count: ', count)

Definimos a variável count como zero antes do início do loop e, em seguida, escrevemos um
loop for para percorrer a lista de números. Nossa variável de iteração é denominada itervar e
embora não usemos itervar no loop, ele controla o loop e causa o corpo do loop a ser
executado uma vez para cada um dos valores na lista. No corpo do loop, adicionamos 1 ao
valor atual de contagem para cada um dos valores na lista. Enquanto o loop está sendo
executado, o valor de count é o número de valores que vimos “até agora”.

Após a conclusão do loop, o valor de count é o número total de itens. O número total “cai no
nosso colo” no final do loop. Nós construímos o loop assim que temos o que queremos quando
o loop terminar.

Outro loop semelhante que calcula o total de um conjunto de números é o seguinte:

total = 0

for itervar in [3, 41, 12, 9, 74, 15]:

total = total + itervar

print('Total: ', total)

Neste loop, usamos a variável de iteração. Em vez de simplesmente adicionar um a contagem


como no loop anterior, adicionamos o número real (3, 41, 12, etc.) execução total durante
cada iteração do loop. Se você pensar na variável total, ela contém o “total acumulado dos
valores até o momento”. Portanto, antes do início do loop, o total é zero porque ainda não
vimos nenhum valor, durante o loop total é a execução total, e no final do loop total é o total
geral de todos os valores na lista.

À medida que o loop é executado, total acumula a soma dos elementos; uma variável usada
desta forma é às vezes chamada de acumulador. Nem o loop de contagem nem o loop de
soma são particularmente úteis na prática, porque existem funções internas len() e sum() que
calculam o número de itens em uma lista e o total dos itens na lista, respectivamente.

5.7.2 Loops máximo e mínimo


[maximumloop] Para encontrar o maior valor em uma lista ou sequência, construímos o
seguinte laço:
largest = None

print('Before:', largest)

for itervar in [3, 41, 12, 9, 74, 15]:

if largest is None or itervar > largest :

largest = itervar

print('Loop:', itervar, largest)

print('Largest:', largest)

Quando o programa é executado, a saída é a seguinte:

Before: None

Loop: 3 3

Loop: 41 41

Loop: 12 41

Loop: 9 41

Loop: 74 74

Loop: 15 74

Largest: 74

A variável maior é melhor pensada como o “maior valor que vimos até agora”. Antes do loop,
definimos o maior como a constante None. None é uma constante especial que podemos
armazenar em uma variável para marcar a variável como "vazia". Antes do início do loop, o
maior valor que vimos até agora é None, pois ainda não vimos nenhum valor. Enquanto o loop
está em execução, se o maior for None, então pegue o primeiro valor que vemos como o
maior até agora. Você pode ver na primeira iteração quando o valor de itervar é 3, já que o
maior é None, definimos imediatamente o maior ser 3.

Após a primeira iteração, o maior não é mais Nenhum, então a segunda parte da expressão
lógica composta que verifica itervar > maior dispara somente quando vemos um valor maior
que o “maior até agora”. Quando vemos um novo “mesmo maior” valor, tomamos esse novo
valor para o maior. Você pode ver no programa a maior produção progride de 3 para 41 para
74.

No final do loop, verificamos todos os valores e a variável maior agora contém o maior valor na
lista.

Para calcular o menor número, o código é muito semelhante com uma pequena alteração:
smallest = None

print('Before:', smallest)

for itervar in [3, 41, 12, 9, 74, 15]:

if smallest is None or itervar < smallest:

smallest = itervar

print('Loop:', itervar, smallest)

print('Smallest:', smallest)

Novamente, menor é o “menor até agora” antes, durante e depois da execução do loop.
Quando o loop for concluído, o menor contém o valor mínimo na lista.

Novamente, como na contagem e na soma, as funções internas max() e min() fazem escrever
esses loops exatos é desnecessário.

A seguir está uma versão simples da função min() integrada do Python:

def min(values):

smallest = None

for value in values:

if smallest is None or value < smallest:

smallest = value

return smallest

Na versão de função do menor código, removemos todas as instruções de impressão para ser
equivalente à função min que já está incorporada ao Python.

5.8 Depuração
Ao começar a escrever programas maiores, você pode passar mais tempo de depuração. Mais
código significa mais chances de cometer um erro e mais lugares para erros se esconderem.
Uma maneira de reduzir o tempo de depuração é “depurar por bisseção”. Por exemplo, se
existem 100 linhas em seu programa e você as verifica uma de cada vez, seria como dar 100
passos.

Em vez disso, tente dividir o problema pela metade. Olhe para o meio do programa, ou perto
dele, para um valor intermediário que você pode verificar. Adicione uma instrução de
impressão (ou outra coisa que tenha um efeito verificável) e execute o programa. Se a
verificação do ponto médio estiver incorreta, o problema deve estar na primeira metade do
programa. Se estiver correto, o problema está no segundo tempo.
Cada vez que você executa uma verificação como esta, você reduz pela metade o número de
linhas que precisa procurar. Depois de seis passos (que é muito menos do que 100), você
estaria reduzido a uma ou duas linhas de código, pelo menos em teoria.

Na prática nem sempre fica claro qual é o “meio do programa” e nem sempre possível verificá-
lo. Não faz sentido contar linhas e encontrar o exato ponto médio. Em vez disso, pense em
lugares no programa onde pode haver erros e lugares onde é fácil colocar um cheque. Em
seguida, escolha um local onde você acha as chances são quase as mesmas de que o bug esteja
antes ou depois da verificação.

5.9 Glossário
Acumulador - Uma variável usada em um loop para somar ou acumular um resultado.

Contador - Uma variável usada em um loop para contar o número de vezes que algo
aconteceu. Inicializamos um contador em zero e então incrementamos o contador a cada vez
que queremos “contar” algo.

Decrementar - Uma atualização que diminui o valor de uma variável.

Inicializar - Uma atribuição que dá um valor inicial a uma variável que será atualizada.

Incremento - Uma atualização que aumenta o valor de uma variável (geralmente em um).

Loop infinito - Um loop no qual a condição final nunca é satisfeita ou para qual não há
condição de terminação.

Iteração - Execução repetida de um conjunto de instruções usando uma função que chama a si
mesmo ou a um loop.

5.10 Exercícios
Exercício 1: Escreva um programa que leia números repetidamente até que o usuário digite
"feito". Depois de inserir "done", imprima o total, a contagem e a média dos números. Se o
usuário inserir algo diferente de um número, detecte o erro usando try e except e imprima
uma mensagem de erro e pule para o próximo número.

Digite um número: 4

Digite um número: 5

Digite um número: dados inválidos

Entrada inválida

Digite um número: 7

Digite um número: feito

16 3 5.333333333333333
Exercício 2: Escreva outro programa que solicite uma lista de números como acima e no final
imprime o máximo e o mínimo dos números da média.
Capítulo 6
String
6.1 Uma string é uma sequência
Uma string é uma sequência de caracteres. Você pode acessar os personagens um de cada vez
com o operador colchete:

>>> fruit = 'banana'

>>> letter = fruit[1]

A segunda instrução extrai o caractere na posição de índice 1 da variável fruit e a atribui à


variável letter.

A expressão entre colchetes é chamada de índice. O índice indica qual caractere na sequência
que você quiser (daí o nome).

Mas você pode não obter o que espera:

>>> print(letter)

Para a maioria das pessoas, a primeira letra de “banana” é b, não a. Mas em Python, o índice é
um deslocamento do início da string e a localização da primeira letra é zero.

>>> letter = fruit[0]

>>> print(letter)

Então b é a 0ª letra (“zero-eth”) de “banana”, a é a 1ª letra (“one-eth”), e n é a 2ª letra (“two-


eth”).

Você pode usar qualquer expressão, incluindo variáveis e operadores, como um índice, mas o
valor do índice deve ser um número inteiro. Caso contrário, você obtém:

>>> letter = fruit[1.5]


TypeError: string indices must be integers

[0]

[1]

[2]

[3]

[4]

[5]

6.2 Obtendo o comprimento de uma string usando len


len é uma função interna que retorna o número de caracteres em uma string:

>>> fruit = 'banana'

>>> len(fruit)

Para obter a última letra de uma string, você pode tentar algo assim:

>>> length = len(fruit)

>>> last = fruit[length]

IndexError: string index out of range

A razão para o IndexError é que não há nenhuma letra em 'banana' com o índice 6. Como
começamos a contar do zero, as seis letras são numeradas de 0 a 5. Para obter o último
caractere, você deve subtrair 1 do comprimento:
>>> last = fruit[length-1]

>>> print(last)

Como alternativa, você pode usar índices negativos, que contam para trás a partir do final da
corda. A expressão fruit[-1] retorna a última letra, fruit[-2] retorna a penúltima, e assim por
diante.

6.3 Traversal através de uma string com um loop


Muitos cálculos envolvem o processamento de uma string, um caractere por vez. Muitas vezes
eles começam no começo, selecionam cada personagem por vez, fazem algo com ele e
continua até o final. Esse padrão de processamento é chamado de travessia.

index = 0

while index < len(fruit):

letter = fruit[index]

print(letter)

index = index + 1

Este loop percorre a string e exibe cada letra em uma linha por si só. A condição do loop é
index \< len(fruit), portanto, quando o índice é igual ao comprimento da string, a condição é
falsa e o corpo do loop não é executado. O último caractere acessado é aquele com o índice
len(fruit)-1, que é o último caractere na string.

Exercício 1: Escreva um loop while que comece no último caractere da string e faça seu
caminho de volta para o primeiro caractere da string, imprimindo cada letra em uma linha
separada, exceto para trás.

Outra maneira de escrever uma travessia é com um loop for:

for char in fruit:

print(char)

Cada vez que passa pelo loop, o próximo caractere na string é atribuído a variável char. O loop
continua até que nenhum caractere seja deixado.
6.4 Fatias de cordas
Um segmento de uma string é chamado de fatia. Selecionar uma fatia é semelhante a
selecionar uma personagem:

>>> s = 'Monty Python'

>>> print(s[0:5])

Monty

>>> print(s[6:12])

Python

O operador retorna a parte da string do caractere “n-eth” para o caractere “m-eth”, incluindo
o primeiro, mas excluindo o último.

Se você omitir o primeiro índice (antes dos dois pontos), a fatia começa no início da corda. Se
você omitir o segundo índice, a fatia vai para o final da string:

>>> fruit = 'banana'

>>> fruit[:3]

'ban'

>>> fruit[3:]

'ana'

Se o primeiro índice for maior ou igual ao segundo, o resultado é uma string vazia,
representada por duas aspas:

>>> fruit = 'banana'

>>> fruit[3:3]

''

Uma string vazia não contém caracteres e tem comprimento 0, mas fora isso, é igual a
qualquer outra string.

Exercício 2: Dado que fruit é uma string, o que fruit[:] significa?


6.5 Strings são imutáveis
É tentador usar o operador do lado esquerdo de uma atribuição, com a intenção de alterar um
caractere em uma string. Por exemplo:

>>> greeting = 'Hello, world!'

>>> greeting[0] = 'J'

TypeError: 'str' object does not support item assignment

O “objeto” neste caso é a string e o “item” é o caractere que você tentou atribuir. Por
enquanto, um objeto é o mesmo que um valor, mas vamos refinar isso mais tarde. Um item é
um dos valores em uma sequência.

O motivo do erro é que as strings são imutáveis, o que significa que você não pode alterar uma
string existente. O melhor que você pode fazer é criar uma nova string que seja uma variação
da original:

>>> greeting = 'Hello, world!'

>>> new_greeting = 'J' + greeting[1:]

>>> print(new_greeting)

Jello, world!

Este exemplo concatena uma nova primeira letra em uma fatia de saudação. não tem efeito na
string original.

6.6 Looping e contagem


O seguinte programa conta o número de vezes que a letra aparece em uma string:

word = 'banana'

count = 0

for letter in word:

if letter == 'a':

count = count + 1

print(count)
Este programa demonstra outro padrão de computação chamado contador. A variável count é
inicializada em 0 e então incrementada cada vez que um a é encontrado. Quando o loop
termina, count contém o resultado: o número total de a’s.

Exercício 3:

Encapsule esse código em uma função chamada count e generalize-o para que aceita a string e
a letra como argumentos.

6.7 O operador in
A palavra in é um operador booleano que recebe duas strings e retorna True se o primeiro
aparece como uma substring no segundo:

>>> 'a' in 'banana'

True

>>> 'seed' in 'banana'

False

6.8 Comparação de strings


Os operadores de comparação funcionam em strings. Para ver se duas strings são iguais:

if word == 'banana':

print('All right, bananas.')

Outras operações de comparação são úteis para colocar palavras em ordem alfabética:

if word < 'banana':

print('Your word,' + word + ', comes before banana.')

elif word > 'banana':

print('Your word,' + word + ', comes after banana.')

else:

print('All right, bananas.')


Python não lida com letras maiúsculas e minúsculas da mesma forma que as pessoas fazem.
Todas as letras maiúsculas vêm antes de todas as letras minúsculas, então:

Your word, Pineapple, comes before banana.

Uma maneira comum de resolver esse problema é converter strings em um formato padrão,
como tudo em minúsculas, antes de realizar a comparação. Lembre-se disso caso você tenha
que se defender contra um homem armado com um abacaxi.

6.9 métodos de string


Strings são um exemplo de objetos Python. Um objeto contém ambos os dados (o real string
em si) e métodos, que são efetivamente funções incorporadas ao objeto e estão disponíveis
para qualquer instância do objeto.

Python tem uma função chamada dir que lista os métodos disponíveis para um objeto. A
função type mostra o tipo de um objeto e a função dir mostra os métodos disponíveis.

>>> stuff = 'Hello world'

>>> type(stuff)

<class 'str'>

>>> dir(stuff)

['capitalize', 'casefold', 'center', 'count', 'encode',

'endswith' , 'expandtabs', 'find', 'format', 'format_map',

'index' , 'isalnum', 'isalpha', 'isdecimal', 'isdigit',

'isidentifier' , 'islower', 'isnumeric', 'isprintable',

'isspace' , 'istitle', 'isupper', 'join', 'ljust', 'lower',

'lstrip' , 'maketrans', 'partition', 'replace', 'rfind',

'rindex' , 'rjust', 'rpartition', 'rsplit', 'rstrip',

'split' , 'splitlines', 'startswith', 'strip', 'swapcase',

'title' , 'translate', 'upper', 'zfill']

>>> help(str.capitalize)

Help on method_descriptor:

capitalize(...)

S.capitalize() -> str


Return a capitalized version of S, i.e. make the first character

have upper case and the rest lower case.

>>>

Embora a função dir liste os métodos, você pode usar ajuda para obter algumas
documentações sobre um método, uma melhor fonte de documentação para métodos de
string seria https://docs.python.org/3.5/library/stdtypes.html#string-methods.

Chamar um método é semelhante a chamar uma função (recebe argumentos e retorna um


valor), mas a sintaxe é diferente. Chamamos um método anexando o nome do método para o
nome da variável usando o ponto como delimitador.

Por exemplo, o método upper pega uma string e retorna uma nova string com todas as letras
maiúsculas:

Em vez da sintaxe da função upper(word), ele usa a sintaxe do método word.upper().

>>> word = 'banana'

>>> new_word = word.upper()

>>> print(new_word)

BANANA

Esta forma de notação de ponto especifica o nome do método, upper, e o nome da string para
aplicar o método, palavra. Os parênteses vazios indicam que este método não aceita nenhum
argumento.

Uma chamada de método é chamada de invocação; neste caso, diríamos que estamos
invocando superior na palavra.

Por exemplo, existe um método de string chamado find que procura a posição de uma string
dentro de outra:

>>> word = 'banana'

>>> index = word.find('a')

>>> print(index)

Neste exemplo, invocamos find sobre word e passamos a letra que estamos procurando como

um parâmetro.
O método find pode encontrar substrings, bem como caracteres:

>>> word.find('na')

Ele pode receber como segundo argumento o índice onde deve começar:

>>> word.find('na', 3)

Uma tarefa comum é remover espaços em branco (espaços, tabulações ou novas linhas) do
início e fim de uma string usando o método strip:

>>> line = ' Here we go '

>>> line.strip()

'Here we go'

Alguns métodos, como startswith, retornam valores booleanos.

>>> line = 'Have a nice day'

>>> line.startswith('Have')

True

>>> line.startswith('h')

False

Você notará que começa com maiúsculas, e minúsculas para corresponder, então às vezes
pegamos uma linha e mapeamos tudo para letras minúsculas antes de fazer qualquer
verificação usando o método lower.

>>> line = 'Have a nice day'

>>> line.startswith('h')

False
>>> line.lower()

'have a nice day'

>>> line.lower().startswith('h')

True

No último exemplo, o método lower é chamado e então usamos startswith para ver se a string
minúscula resultante começa com a letra “h”. Enquanto estivermos cuidado com a ordem,
podemos fazer várias chamadas de métodos em uma única expressão.

Exercício 4: Existe um método de string chamado count que é semelhante à função do


exercício anterior. Leia a documentação deste método em
https://docs.python.org/3.5/library/stdtypes.html#string-methods e escreva uma invocação
que conta o número de vezes que a letra a ocorre em “banana”.

6.10 Analisando strings


Frequentemente, queremos examinar uma string e encontrar uma substring. Por exemplo se
estivéssemos em uma série de linhas formatadas da seguinte forma:

From stephen.marquard@ uct.ac.za Sat Jan 5 09:14:16 2008

E queríamos extrair apenas a segunda metade do endereço (ou seja, uct.ac.za) de cada linha,
podemos fazer isso usando o método find e o corte de strings.

Primeiro, encontraremos a posição do @ na string. Então vamos encontrar a posição do


primeiro espaço após o sinal de arroba. E então usaremos o corte de strings para extraia a
parte da string que estamos procurando.

>>> data = 'From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008'

>>> atpos = data.find('@')

>>> print(atpos)

21

>>> sppos = data.find(' ',atpos)

>>> print(sppos)

31

>>> host = data[atpos+1:sppos]

>>> print(host)
uct.ac.za

>>>

Usamos uma versão do método find que nos permite especificar uma posição em a string onde
queremos encontrar para começar a procurar. Ao fatiar, extraímos o caracteres de “um além
do arroba, mas não incluindo o espaço personagem".

A documentação do método find está disponível em

https://docs.python.org/3.5/library/stdtypes.html#string-methods.

6.11 Operador de formato


O operador de formato % nos permite construir strings, substituindo partes das strings com os
dados armazenados em variáveis. Quando aplicado a números inteiros, % é o módulo
operador. Mas quando o primeiro operando é uma string, % é o operador de formato.

O primeiro operando é a string de formato, que contém uma ou mais sequências de formato
que especificam como o segundo operando é formatado. O resultado é uma string.

Por exemplo, a sequência de formato “%d” significa que o segundo operando deve ser
formatado como um número inteiro (d significa “decimal”):

>>> camels = 42

>>> '%d' % camels

'42'

O resultado é a string “42”, que não deve ser confundida com o valor inteiro 42.

Uma sequência de formato pode aparecer em qualquer lugar na string, para que você possa
incorporar um valor em uma frase:

>>> camels = 42

>>> 'I have spotted %d camels.' % camels

'I have spotted 42 camels.'

Se houver mais de uma sequência de formato na string, o segundo argumento terá que ser
uma tupla. Cada sequência de formato é casada com um elemento da tupla, em ordem.

O exemplo a seguir usa “%d” para formatar um inteiro, “%g” para formatar um flutuante
número do ponto (não pergunte por quê) e “%s” para formatar uma string:
>>> 'In %d years I have spotted %g %s.' % (3, 0.1, 'camels')

'In 3 years I have spotted 0.1 camels.'

O número de elementos na tupla deve corresponder ao número de sequências de formato na


corda. Os tipos dos elementos também devem corresponder às sequências de formato:

>>> '%d %d %d' % (1, 2)

TypeError: not enough arguments for format string

>>> '%d' % 'dollars'

TypeError: %d format: a number is required, not str

No primeiro exemplo, não há elementos suficientes; na segunda, o elemento é o tipo errado.

O operador de formato é poderoso, mas pode ser difícil de usar. você pode ler mais sobre isso
em https://docs.python.org/3.5/library/stdtypes.html#printf-style-string-formatting.

6.12 Depuração
Uma habilidade que você deve cultivar ao programar é sempre se perguntar: “O que poderia
dar errado aqui?” ou, alternativamente, “Que loucura nosso usuário pode fazer para travar
nosso programa (aparentemente) perfeito?”

Por exemplo, veja o programa que usamos para demonstrar o loop while no capítulo sobre
iteração:

while True:

line = input('> ')

if line[0] == '#':

continue

if line == 'done':

break

print(line)

print('Done!')

# Code: http://www.pythonlearn.com/code3/copytildone2.py
Veja o que acontece quando o usuário insere uma linha de entrada vazia:

> hello there

hello there

> # don't print this

> print this!

print this!

>

Traceback (most recent call last):

File "copytildone.py", line 3, in <module>

if line[0] == '#':

IndexError: string index out of range

O código funciona bem até que seja apresentada uma linha vazia. Então não há zero-th
personagem, então obtemos um traceback. Existem duas soluções para isso tornar a linha três
“segura” mesmo que a linha esteja vazia.

Uma possibilidade é simplesmente usar o método startswith que retorna False se a string está
vazia.

if line.startswith('#'):

Outra maneira é escrever com segurança a instrução if usando o padrão guardião e certifique-
se de que a segunda expressão lógica seja avaliada apenas onde houver pelo menos um
caractere na string.:

if len(line) > 0 and line[0] == '#':

6.13 Glossário
Contador - Uma variável usada para contar algo, geralmente inicializada em zero e depois
incrementada.

String vazia - Uma string sem caracteres e comprimento 0

Operador de formato - Um operador, %, que recebe uma string de formato e uma tupla e
gera/apaga uma string que inclui os elementos da tupla formatados conforme especificado
pela string de formato.
Sequência de formato - Uma sequência de caracteres em uma string de formato, como %d,
que especifica como um valor deve ser formatado.

String de formato - Uma string, usada com o operador de formato, que contém sequências.

Sinalizador - Uma variável booleana usada para indicar se uma condição é verdadeira ou falsa.

Invocação - Uma instrução que chama um método.

Imutável - A propriedade de uma sequência cujos itens não podem ser atribuídos.

Índice - Um valor inteiro usado para selecionar um item em uma sequência, como um
caractere em uma corda.

Item - Um dos valores em uma sequência.

Método - Uma função associada a um objeto e chamada usando a notação de ponto.

Objeto - Algo a que uma variável pode se referir. Por enquanto, você pode usar “objeto” e
“valor” de forma intercambiável.

Pesquisa - Um padrão de passagem que para quando encontra o que está procurando.

Sequência - Um conjunto ordenado; ou seja, um conjunto de valores onde cada valor é


identificado por um índice inteiro.

Fatia - Uma parte de uma string especificada por um intervalo de índices.

Percorrer - Para percorrer os itens em uma sequência, executando uma operação semelhante
em cada um.

6.14 Exercícios
Exercício 5: Pegue o seguinte código Python que armazena uma string:

‘str = ’X-DSPAM-Confidence:0.8475’

Use localizar e segmentar strings para extrair a parte da string após os dois pontos caractere e,
em seguida, use a função float para converter a string extraída em um número de ponto
flutuante.

Exercício 6: Leia a documentação dos métodos de string em


https://docs.python.org/3.5/library/stdtypes.html#string-methods Você pode querer
experimentar alguns deles para ter certeza de que entendeu como eles trabalham. Remover e
substituir são particularmente úteis.

A documentação usa uma sintaxe que pode ser confusa. Por exemplo, em find(sub[, start[,
end]]), os colchetes indicam argumentos opcionais. é obrigatório, mas o início é opcional
hahaha e, se você incluir o início, o fim será opcional. 
Capítulo 7
Arquivos
7.1 Persistência
Até agora, aprendemos como escrever programas e comunicar nossas intenções para a
Unidade Central de Processamento usando execução condicional, funções e iterações.
Aprendemos como criar e usar estruturas de dados na Memória Principal. A CPU e a memória
são onde nosso software funciona e é executado. É onde todos os “pensamentos” acontecem.

Mas se você se lembra de nossas discussões sobre arquitetura de hardware, uma vez que a
energia é desligada, qualquer coisa armazenada na CPU ou na memória principal é apagada.
Então até agora, nossos programas são apenas exercícios divertidos e transitórios para
aprender Python.

Neste capítulo, começamos a trabalhar com Memória Secundária (ou arquivos). A memória
Secundária não é apagada quando a energia é desligada. Ou no caso de um flash USB drive, os
dados que escrevemos de nossos programas podem ser removidos do sistema e transportados
para outro sistema.

Vamos nos concentrar principalmente na leitura e gravação de arquivos de texto, como


aqueles que criamos em um editor de texto. Mais tarde veremos como trabalhar com arquivos
de banco de dados que são arquivos binários, especificamente projetados para serem lidos e
gravados por meio de software de banco de dados.

7.2 Abrindo arquivos


Quando queremos ler ou gravar um arquivo (digamos, em seu disco rígido), primeiro devemos
abrir o arquivo. A abertura do arquivo se comunica com seu sistema operacional, que sabe
onde os dados de cada arquivo são armazenados. Quando você abre um arquivo, você está
perguntando o sistema operacional para localizar o arquivo pelo nome e verifique se o arquivo
existe. Neste exemplo, abrimos o arquivo mbox.txt, que deve ser armazenado no mesma pasta
em que você está quando inicia o Python. Você pode baixar este arquivo em
www.pythonlearn.com/code3/mbox.txt

>>> fhand = open('mbox.txt')

>>> print(fhand)

<_io.TextIOWrapper name='mbox.txt' mode='r' encoding='cp1252'>

Se a abertura for bem-sucedida, o sistema operacional nos retorna um identificador de


arquivo. O arquivo handle não são os dados reais contidos no arquivo, mas sim um “handle”
que podemos usar para ler os dados. Você recebe um identificador, se o arquivo solicitado
existir e se você tem as permissões adequadas para ler o arquivo.

Se o arquivo não existir, a abertura falhará com um traceback e você não obterá um handle
para acessar o conteúdo do arquivo:

>>> fhand = open('stuff.txt')

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

FileNotFoundError: [Errno 2] No such file or directory: 'stuff.txt'

Mais tarde, usaremos try e except para lidar com mais elegância com a situação em que
tentamos abrir um arquivo que não existe.

7.3 Arquivos de texto e linhas


Um arquivo de texto pode ser pensado como uma sequência de linhas, assim como uma string
Python pode ser pensado como uma sequência de caracteres. Por exemplo, esta é uma
amostra de um arquivo de texto que registra a atividade de e-mail de vários indivíduos, é um
projeto de código aberto de uma equipe de desenvolvimento:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Return-Path: <postmaster@collab.sakaiproject.org>

Date: Sat, 5 Jan 2008 09:12:18 -0500

To: source@collab.sakaiproject.org

From: stephen.marquard@uct.ac.za

Subject: [sakai] svn commit: r39772 - content/branches/

Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772

...

Todo o arquivo de interações de e-mail está disponível em:

www.pythonlearn.com/code3/mbox.txt

E uma versão abreviada do arquivo está disponível em:

www.pythonlearn.com/code3/mbox-short.txt
Esses arquivos estão em um formato padrão para um arquivo que contém várias mensagens
de correio. As linhas que começam com “From” separam as mensagens, e as linhas que
começam com “De:” fazem parte das mensagens. Para mais informações sobre o formato
mbox, consulte en.wikipedia.org/wiki/Mbox.

Para quebrar o arquivo em linhas, existe um caractere especial que representa o “fim da linha”
chamada de caractere de nova linha.

Em Python, representamos o caractere de nova linha como uma barra invertida em constantes
de string. Mesmo que pareçam dois caracteres, na verdade é um único caractere. Quando
olhamos para a variável inserindo “stuff” no interpretador, ele nos mostra o \n na string, mas
quando usamos print para mostrar a string, vemos a string quebrada em duas linhas pelo
caractere de nova linha.

>>> stuff = 'Hello\nWorld!'

>>> stuff

'Hello\nWorld!'

>>> print(stuff)

Hello

World!

>>> stuff = 'X\nY'

>>> print(stuff)

>>> len(stuff)

Você também pode ver que o comprimento da string X\nY é de três caracteres porque o
caractere de nova linha é um único caractere.

Então, quando olhamos para as linhas de um arquivo, precisamos imaginar que existe um
caractere invisível chamado de nova linha no final de cada linha que marca o fim da linha. 

Portanto, o caractere de nova linha separa os caracteres do arquivo em linhas.

7.4 Lendo arquivos


Embora o identificador de arquivo não contenha os dados do arquivo, é muito fácil construir
um loop for para ler e contar cada uma das linhas em um arquivo:
fhand = open('mbox-short.txt')

count = 0

for line in fhand:

count = count + 1

print('Line Count:', count)

# Code: http://www.pythonlearn.com/code3/open.py

Podemos usar o identificador de arquivo como a sequência em nosso loop for. Nosso loop for
simplesmente conta o número de linhas no arquivo e as imprime. A tradução aproximada do
loop for para o inglês é, “para cada linha no arquivo representado pelo arquivo identificador,
adicione um à variável de contagem.”

A razão pela qual a função open não lê o arquivo inteiro é que o arquivo pode ser bastante
grande com muitos gigabytes de dados. A declaração aberta leva a mesma quantidade de
tempo, independentemente do tamanho do arquivo. O loop for realmente causa os dados a
serem lidos do arquivo.

Quando o arquivo é lido usando um loop for dessa maneira, o Python cuida da divisão dos
dados no arquivo em linhas separadas usando o caractere de nova linha. Python lê cada linha
através da nova linha e inclui a nova linha como o último caractere na variável de linha para
cada iteração do loop for.

Como o loop for lê os dados uma linha por vez, ele pode ler e contar as linhas em arquivos
muito grandes sem esgotar a memória principal para armazenar os dados. O programa acima
pode contar as linhas em qualquer tamanho de arquivo usando muito pouca memória, pois
cada linha é lida, contada e descartada.

Se você sabe que o arquivo é relativamente pequeno comparado ao tamanho de sua memória
principal, você pode ler o arquivo inteiro em uma string usando o método read no
identificador de arquivo.

>>> fhand = open('mbox-short.txt')

>>> inp = fhand.read()

>>> print(len(inp))

94626

>>> print(inp[:20])

From stephen.marquar

Neste exemplo, todo o conteúdo (todos os 94.626 caracteres) do arquivo mbox-short.txt são
lidos diretamente na variável inp. Usamos string slic-ing para imprimir os primeiros 20
caracteres dos dados de string armazenados em inp.
Quando o arquivo é lido dessa maneira, todos os caracteres, incluindo todas as linhas e
caracteres de nova linha são uma grande string na variável inp. Lembre-se que isso forma da
função aberta, só deve ser usada se os dados do arquivo couberem confortavelmente na
memória principal do seu computador.

Se o arquivo for muito grande para caber na memória principal, você deve escrever seu
programa para ler o arquivo em partes usando um loop for ou while.

7.5 Searching through a file


Quando você está pesquisando dados em um arquivo, é um padrão muito comum ler através
de um arquivo, ignorando a maioria das linhas e processando apenas as linhas que atendem a
uma condição particular. Podemos combinar o padrão para ler um arquivo com métodos string
para construir mecanismos de busca simples.

Por exemplo, se quisermos ler um arquivo e imprimir apenas as linhas que começam com o
prefixo “From:”, poderíamos usar o método string startswith para selecionar apenas aquelas
linhas com o prefixo desejado:

fhand = open('mbox-short.txt')

count = 0

for line in fhand:

if line.startswith('From:'):

print(line)

# Code: http://www.pythonlearn.com/code3/search1.py

Quando este programa é executado, obtemos a seguinte saída:

From: stephen.marquard@uct.ac.za

From: louis@media.berkeley.edu

From: zqian@umich.edu

From: rjlowe@iupui.edu

...

A saída parece ótima, pois as únicas linhas que vemos são aquelas que começam com “De:”,
mas por que estamos vendo as linhas extras em branco? Isso se deve a esse invisível caractere
de nova linha. Cada uma das linhas termina com uma nova linha, então a instrução print
imprime a string na linha variável que inclui uma nova linha e depois imprime adiciona outra
nova linha, resultando no efeito de espaçamento duplo que vemos.

Poderíamos usar o corte de linha para imprimir todos, exceto o último caractere, mas uma
abordagem mais simples é usar o método rstrip que remove os espaços em branco do lado
direito de uma string do seguinte modo:

fhand = open('mbox-short.txt')

for line in fhand:

line = line.rstrip()

if line.startswith('From:'):

print(line)

# Code: http://www.pythonlearn.com/code3/search2.py

Quando este programa é executado, obtemos a seguinte saída:

From: stephen.marquard@uct.ac.za

From: louis@media.berkeley.edu

From: zqian@umich.edu

From: rjlowe@iupui.edu

From: zqian@umich.edu

From: rjlowe@iupui.edu

From: cwen@iupui.edu

...

À medida que seus programas de processamento de arquivos ficam mais complicados, você
pode querer estruturar seus loops de pesquisa usando continue. A ideia básica do loop de
pesquisa é que você está procurando linhas “interessantes” e efetivamente pulando linhas
“desinteressantes”. E então, quando encontramos uma linha interessante, fazemos algo com
essa linha.

Podemos estruturar o loop para seguir o padrão de pular linhas desinteressantes como segue:

fhand = open('mbox-short.txt')

for line in fhand:

line = line.rstrip()
# Pular 'linhas desinteressantes'

if not line.startswith('From:'):

continue

# Processe nossa linha 'interessante'

print(line)

# Code: http://www.pythonlearn.com/code3/search3.py

A saída do programa é a mesma. Em inglês, as linhas desinteressantes são aquelas que não
começam com “From:”, que pulamos usando continue. Para as linhas “interessantes” (ou seja,
aquelas que começam com “De:”) realizamos o processamento nessas linhas.

Podemos usar o método find string para simular uma pesquisa de editor de texto que encontra
linhas onde a string de pesquisa está em qualquer lugar da linha. Como find procura por uma
ocorrência de uma string dentro de outra string e retorna a posição da string ou -1 se a string
não foi encontrada, podemos escrever o seguinte loop para mostrar as linhas que contêm a
string “@uct.ac.za” (ou seja, eles vêm da Universidade da Cidade do Cabo na África do Sul):

fhand = open('mbox-short.txt')

for line in fhand:

line = line.rstrip()

if line.find('@uct.ac.za') == -1: continue

print(line)

# Code: http://www.pythonlearn.com/code3/search4.py

Que produz a seguinte saída:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

X-Authentication-Warning: set sender to stephen.marquard@uct.ac.za using -f

From: stephen.marquard@uct.ac.za

Author: stephen.marquard@uct.ac.za

From david.horwitz@uct.ac.za Fri Jan 4 07:02:32 2008

X-Authentication-Warning: set sender to david.horwitz@uct.ac.za using -f

From: david.horwitz@uct.ac.za

Author: david.horwitz@uct.ac.za
...

7.6 Deixando o usuário escolher o nome do arquivo


Nós realmente não queremos ter que editar nosso código Python toda vez que quisermos
processar um arquivo diferente. Seria mais útil pedir ao usuário para inserir o arquivo string de
nome cada vez que o programa é executado para que eles possam usar nosso programa em
diferentes arquivos sem alterar o código Python.

Isso é bastante simples de fazer lendo o nome do arquivo do usuário usando raw_input do
seguinte modo:

fname = input('Enter the file name: ')

fhand = open(fname)

count = 0

for line in fhand:

if line.startswith('Subject:'):

count = count + 1

print('There were', count, 'subject lines in', fname)

# Code: http://www.pythonlearn.com/code3/search6.py

Lemos o nome do arquivo do usuário e o colocamos em uma variável chamada fname e abra
esse arquivo. Agora podemos executar o programa repetidamente em arquivos diferentes.

python search6.py

Enter the file name: mbox.txt

There were 1797 subject lines in mbox.txt

python search6.py

Enter the file name: mbox-short.txt

There were 27 subject lines in mbox-short.txt

Antes de dar uma olhada na próxima seção, dê uma olhada no programa acima e pergunte a
você mesmo: "O que poderia dar errado aqui?" ou “O que nosso usuário amigável faria que
nosso pequeno e agradável programa fosse encerrado de forma deselegante com um
traceback, fazendo-nos parecer não tão legais aos olhos de nossos usuários?”
7.7 Usando try, except e open
Eu disse para você não espiar. Esta é a sua última chance.

E se nosso usuário digitar algo que não seja um nome de arquivo?

python search6.py

Enter the file name: missing.txt

Traceback (most recent call last):

File "search6.py", line 2, in <module>

fhand = open(fname)

FileNotFoundError: [Errno 2] No such file or directory: 'missing.txt'

python search6.py

Enter the file name: na na boo boo

Traceback (most recent call last):

File "search6.py", line 2, in <module>

fhand = open(fname)

FileNotFoundError: [Errno 2] No such file or directory: 'na na boo boo'

Não ria. Os usuários eventualmente farão tudo o que puderem para quebrar seus programas,
seja de propósito ou com intenção maliciosa. De fato, uma parte importante de qualquer
equipe de desenvolvimento de software é uma pessoa ou grupo chamado Garantia de
qualidade (ou QA para abreviar), cujo trabalho é fazer as coisas mais loucas possível em uma
tentativa de quebrar o software que o programador criou. Ou pode ser SOMENTE UM
HACKER!!

A equipe de QA é responsável por encontrar as falhas nos programas antes que tenhamos
entregado o programa aos usuários finais que podem estar comprando o software ou pagando
para escrever o software. Portanto, a equipe de controle de qualidade é a melhor equipe do
programador amigo.

Então, agora que vemos a falha no programa, podemos corrigi-la elegantemente usando a
estrutura try/except. Precisamos assumir que a chamada aberta pode falhar e adicionar código
de recuperação quando a abertura falha da seguinte forma:
fname = input('Enter the file name: ')

try:

fhand = open(fname)

except:

print('File cannot be opened:', fname)

exit()

count = 0

for line in fhand:

if line.startswith('Subject:'):

count = count + 1

print('There were', count, 'subject lines in', fname)

# Code: http://www.pythonlearn.com/code3/search7.py

A função exit encerra o programa. É uma função que chamamos que nunca retorna. Agora,
quando nosso usuário (ou equipe de QA) digita tolices ou nomes de arquivos ruins, nós
“pegue-os” e recupere-se graciosamente:

python search7.py

Enter the file name: mbox.txt

There were 1797 subject lines in mbox.txt

python search7.py

Enter the file name: na na boo boo

File cannot be opened: na na boo boo

Proteger a chamada aberta é um bom exemplo do uso adequado de try e except em um


programa Python. Usamos o termo “Pythonic” quando estamos fazendo algo do “jeito
Python”. Podemos dizer que o exemplo acima é a maneira Pythonic de “abra um arquivo”.

Depois de se tornar mais habilidoso em Python, você pode se engajar em diálogos com outros
Programadores Python para decidir qual das duas soluções equivalentes para um problema é
“mais pitônico”. O objetivo de ser “mais pitônico” captura a noção de que programação é
parte engenharia e parte arte. Nem sempre estamos interessados em apenas fazendo algo
funcionar, também queremos que nossa solução sejam elegantes e seja apreciado como
elegante por nossos pares.

7.8 Escrevendo arquivos


Para gravar um arquivo, você deve abri-lo com o modo “w” como segundo parâmetro:

>>> fout = open('output.txt', 'w')

>>> print(fout)

<_io.TextIOWrapper name='output.txt' mode='w' encoding='cp1252'>

Se o arquivo já existir, abri-lo no modo de gravação limpa os dados antigos e inicia fresco, por
isso tome cuidado! Se o arquivo não existir, um novo será criado.

O método write do objeto de manipulação de arquivo coloca dados no arquivo, retornando o


número de caracteres escritos. O modo de gravação padrão é texto para escrita (e leitura) em
cadeias de caracteres.

>>> line1 = "This here's the wattle,\n"

>>> fout.write(line1)

24

Novamente, o objeto de arquivo rastreia onde está, portanto, se você chamar write
novamente, ele adicionará os novos dados até o final.

Devemos nos certificar de gerenciar as extremidades das linhas enquanto escrevemos no


arquivo explicitamente inserindo o caractere de nova linha quando queremos terminar uma
linha. A declaração de impressão anexa automaticamente uma nova linha, mas o método write
não adiciona a nova linha automaticamente.

>>> line2 = 'the emblem of our land.\n'

>>> fout.write(line2)

24

Quando terminar de escrever, você deve fechar o arquivo para garantir que o último bit de
dados é gravado fisicamente no disco para que não seja perdido se a energia acabar
desligando.
>>> fout.close()

Poderíamos fechar os arquivos que abrimos para leitura também, mas podemos ser um pouco
desleixados se estivermos abrindo apenas alguns arquivos, pois o Python garante que todos os
arquivos abertos sejam fechados quando o programa termina. Quando estamos escrevendo
arquivos, queremos explicitamente fechar os arquivos para não deixar nada ao acaso.

7.9 Depuração
Ao ler e gravar arquivos, você pode ter problemas com brancos. Ritmo. Esses erros podem ser
difíceis de depurar porque espaços, tabulações e novas linhas são normalmente invisível:

>>> s = '1 2\t 3\n 4'

>>> print(s)

123

A função incorporada repr pode ajudar. Toma qualquer objeto como argumento e Retorna
uma representação de string do objeto. Para strings, representa espaço em branco com
sequências de barra invertida:

>>> print(repr(s))

'1 2\t 3\n 4'

Isso pode ser útil para depuração.

Outro problema que você pode encontrar é que sistemas diferentes usam caracteres
diferentes. Caracteres para indicar o fim de uma linha. Alguns sistemas usam uma nova linha,
representada por \n. Outros usam um caractere de retorno, representado por \r. Alguns usam
os dois. Se você mover arquivos entre sistemas diferentes, essas inconsistências podem causar
problemas.

Para a maioria dos sistemas, existem aplicativos para converter de um formato para outro.
Você pode encontrá-los (e ler mais sobre este assunto) em wikipedia.org/wiki/Newline. Ou,
claro, você mesmo pode escrever um. 
7.10 Glossary
Catch - Para evitar que uma exceção termine um programa usando a declarações try e except.

Nova linha - Um caractere especial usado em arquivos e strings para indicar o fim de uma
linha.

Pythonic - Uma técnica que funciona elegantemente em Python. “Usar try e except é a
maneira Pythonica de se recuperar um arquivos perdidos”.

Garantia de qualidade - Uma pessoa ou equipe focada em garantir a qualidade geral de um


produto de software. O controle de qualidade geralmente está envolvido no teste de um
produto e na identificação de problemas antes de o produto ser lançado.

Arquivo de texto - Uma sequência de caracteres armazenados em armazenamento


permanente como um disco rígido.

7.11 Exercises
Exercício 1: Escreva um programa para ler um arquivo e imprimir o conteúdo do arquivo (linha
por linha) tudo em caixa alta. A execução do programa ficará da seguinte forma:

python shout.py

Digite um nome de arquivo: mbox-short.txt

FROM STEPHEN.MARQUARD@UCT.AC.ZA SAT JAN 5 09:14:16 2008

RETURN-PATH: <POSTMASTER@COLLAB.SAKAIPROJECT.ORG>

RECEIVED: FROM MURDER (MAIL.UMICH.EDU [141.211.14.90])

BY FRANKENSTEIN.MAIL.UMICH.EDU (CYRUS V2.3.8) WITH LMTPA;

SAT, 05 JAN 2008 09:14:16 -0500

Você pode baixar o arquivo de:

www.pythonlearn.com/code3/mbox-short.txt

Exercício 2: Escreva um programa para solicitar um nome de arquivo e, em seguida, leia o


arquivo e procure por linhas do formulário:

X-DSPAM-Confidence:0.8475

Quando você encontrar uma linha que começa com “X-DSPAM-Confidence:” separe a linha
para extrair o número de ponto flutuante na linha. Conte essas linhas e em seguida, calcule o
total dos valores de confiança de spam dessas linhas. Quando você chegar ao final do arquivo,
imprima a confiança média de spam.
Digite o nome do arquivo: mbox.txt

Confiança média de spam: 0,894128046745

Digite o nome do arquivo: mbox-short.txt

Confiança média de spam: 0,750718518519

Teste seu arquivo nos arquivos mbox.txt e mbox-short.txt.

Exercício 3: Às vezes, quando os programadores ficam entediados ou querem se divertir um


pouco, eles adicionam um ovo de páscoa inofensivo ao programa Modifique o programa que
solicita o usuário para o nome do arquivo para que imprima uma mensagem engraçada
quando o usuário digitar o nome exato do arquivo “na na boo boo”. O programa deve se
comportar normalmente para todos outros arquivos que existem e não existem. Aqui está um
exemplo de execução do programa:

python egg.py

Enter the file name: mbox.txt

There were 1797 subject lines in mbox.txt

python egg.py

Enter the file name: missing.tyxt

File cannot be opened: missing.tyxt

python egg.py

Enter the file name: na na boo boo

NA NA BOO BOO TO YOU - You have been punk'd!

Não estamos encorajando você a colocar Easter Eggs em seus programas; isso é apenas um
exercício. Hahahaha
Capítulo 8
Listas
8.1 Uma lista é uma sequência
Como uma string, uma lista é uma sequência de valores. Em uma string, os valores são
caracteres em uma lista, eles podem ser de qualquer tipo. Os valores na lista são chamados
elementos ou às vezes Unid.

Existem várias maneiras de criar uma nova lista; o mais simples é incluir os elementos entre
colchetes ([ e ]):

~~ {.python} [10, 20, 30, 40]['sapo crocante', 'bexiga de carneiro', 'vômito de cotovia'] ~~

{.Pitão}

O primeiro exemplo é uma lista de quatro inteiros. A segunda é uma lista de três strings. Os
elementos de uma lista não precisam ser do mesmo tipo. A lista a seguir contém uma string,
um float, um integer e (lo!) outra lista:

['spam', 2.0, 5, [10, 20]]

Uma lista dentro de outra lista é aninhada.

Uma lista que não contém elementos é chamada de lista vazia; você pode criar um com
colchetes vazios, [].

Como você pode esperar, você pode atribuir valores de lista a variáveis:

>>> queijos = ['Cheddar', 'Edam', 'Gouda']

>>> números = [17, 123]

>>> vazio = []

>>> print(queijos, números, vazio)

['Cheddar', 'Edam', 'Gouda'] [17, 123] []


8.2 Listas são mutáveis
A sintaxe para acessar os elementos de uma lista é a mesma para acessar os caracteres de uma
string: o operador colchete. A expressão dentro dos parênteses especifica o índice. Lembre-se
que os índices começam em 0:

>>> print(cheeses[0])

Cheddar

Ao contrário das strings, as listas são mutáveis porque você pode alterar a ordem dos itens em
uma lista ou reatribuir um item em uma lista. Quando o operador colchete aparece ao lado
esquerdo de uma atribuição, identifica o elemento da lista que será atribuído.

>>> numbers = [17, 123]

>>> numbers[1] = 5

>>> print(numbers)

[17, 5]

O décimo elemento dos números, que costumava ser 123, agora é 5.

Você pode pensar em uma lista como uma relação entre índices e elementos. Esse
relacionamento é chamado de mapeamento; cada índice “mapeia” um dos elementos.

Os índices de lista funcionam da mesma forma que os índices de string:

• Qualquer expressão inteira pode ser usada como um índice.

• Se você tentar ler ou escrever um elemento que não existe, você obtém um IndexError.

• Se um índice tiver um valor negativo, ele faz a contagem regressiva a partir do final da lista.

O operador in também funciona em listas.

>>> cheeses = ['Cheddar', 'Edam', 'Gouda']

>>> 'Edam' in cheeses

True

>>> 'Brie' in cheeses

False
8.3 Percorrendo uma lista
A maneira mais comum de percorrer os elementos de uma lista é com um loop for. A sintaxe é
a mesma das strings:

for cheese in cheeses:

print(cheese)

Isso funciona bem se você só precisa ler os elementos da lista. Mas se você quer para escrever
ou atualizar os elementos, você precisa dos índices. Uma maneira comum de fazer isso é
combinar as funções range e len:

for i in range(len(numbers)):

numbers[i] = numbers[i] * 2

Este loop percorre a lista e atualiza cada elemento. len retorna o número de elementos na
lista. Range retorna uma lista de índices de 0 a n − 1, onde n é o comprimento da lista. Cada
vez que passar pelo loop, i obtém o índice do próximo elemento. A instrução de atribuição no
corpo usa i para ler o valor antigo do elemento e para atribuir o novo valor.

Um loop for sobre uma lista vazia nunca executa o corpo:

for x in empty:

print('This never happens.')

Embora uma lista possa conter outra lista, a lista aninhada ainda conta como um único
elemento. O comprimento desta lista é quatro:

['spam', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]

8.4 Listar operações


O operador + concatena as listas:
>>> a = [1, 2, 3]

>>> b = [4, 5, 6]

>>> c = a + b

>>> print(c)

[1, 2, 3, 4, 5, 6]

Da mesma forma, o operador repete uma lista um determinado número de vezes:

>>> [0] * 4

[0, 0, 0, 0]

>>> [1, 2, 3] * 3

[1, 2, 3, 1, 2, 3, 1, 2, 3]

O primeiro exemplo se repete quatro vezes. O segundo exemplo repete a lista três vezes.

8.5 Listar fatias


O operador de fatia também funciona em listas:

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']

>>> t[1:3]

['b', 'c']

>>> t[:4]

['a', 'b', 'c', 'd']

>>> t[3:]

['d', 'e', 'f']

Se você omitir o primeiro índice, a fatia começa no início. Se você omitir o segundo, a fatia vai
até o fim. Portanto, se você omitir ambos, a fatia será uma cópia de toda a lista.

>>> t[:]

['a', 'b', 'c', 'd', 'e', 'f']


Como as listas são mutáveis, muitas vezes é útil fazer uma cópia antes de executar as
operações que dobram, giram ou mutilam listas.

Um operador de fatia no lado esquerdo de uma atribuição pode atualizar vários elementos:

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']

>>> t[1:3] = ['x', 'y']

>>> print(t)

['a', 'x', 'y', 'd', 'e', 'f']

8.6 Listar métodos


Python fornece métodos que operam em listas. Por exemplo, append adiciona um novo
elemento ao final de uma lista:

>>> t = ['a', 'b', 'c']

>>> t.append('d')

>>> print(t)

['a', 'b', 'c', 'd']

extend pega uma lista como um argumento e anexa todos os elementos:

>>> t1 = ['a', 'b', 'c']

>>> t2 = ['d', 'e']

>>> t1.extend(t2)

>>> print(t1)

['a', 'b', 'c', 'd', 'e']

Este exemplo deixa t2 inalterado.

sort organiza os elementos da lista de baixo para cima:


>>> t = ['d', 'c', 'e', 'b', 'a']

>>> t.sort()

>>> print(t)

['a', 'b', 'c', 'd', 'e']

A maioria dos métodos de lista é void; eles modificam a lista e retornam None. Se você
acidentalmente escrever t = t.sort(), ficará desapontado com o resultado.

8.7 Excluindo elementos


Existem várias maneiras de excluir elementos de uma lista. Se você conhece o índice do
elemento que você deseja, você pode usar pop:

>>> t = ['a', 'b', 'c']

>>> x = t.pop(1)

>>> print(t)

['a', 'c']

>>> print(x)

pop modifica a lista e retorna o elemento que foi removido. Se você não fornecer um índice,
ele exclui e retorna o último elemento.

Se você não precisa do valor removido, pode usar o operador del:

>>> t = ['a', 'b', 'c']

>>> del t[1]

>>> print(t)

['a', 'c']

Se você conhece o elemento que deseja remover (mas não o índice), pode usar remove:

>>> t = ['a', 'b', 'c']

>>> t.remove('b')
>>> print(t)

['a', 'c']

O valor de retorno de remove é Nenhum.

Para remover mais de um elemento, você pode usar del com um índice de fatia:

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']

>>> del t[1:5]

>>> print(t)

['a', 'f']

Como de costume, a fatia seleciona todos os elementos, mas não incluindo, o segundo índice.

8.8 Listas e funções


Existem várias funções integradas que podem ser usadas em listas que permitem examinar
rapidamente uma lista sem escrever seus próprios loops:

>>> nums = [3, 41, 12, 9, 74, 15]

>>> print(len(nums))

>>> print(max(nums))

74

>>> print(min(nums))

>>> print(sum(nums))

154

>>> print(sum(nums)/len(nums))

25

A função sum() só funciona quando os elementos da lista são números. A outras funções
(max(), len(), etc.) trabalham com listas de strings e outros tipos que podem ser comparáveis.
Poderíamos reescrever um programa anterior que calculasse a média de uma lista de números
inserido pelo usuário usando uma lista.

Primeiro, o programa para calcular uma média sem uma lista:

total = 0

count = 0

while (True):

inp = input('Enter a number: ')

if inp == 'done': break

value = float(inp)

total = total + value

count = count + 1

average = total / count

print('Average:', average)

# Code: http://www.pythonlearn.com/code3/avenum.py

Neste programa, temos as variáveis count e total para manter o número e executando o total
dos números do usuário enquanto solicitamos repetidamente ao usuário um número.

Poderíamos simplesmente lembrar cada número à medida que o usuário o inseriu e usar
funções para calcular a soma e contar no final.

numlist = list()

while (True):

inp = input('Enter a number: ')

if inp == 'done': break

value = float(inp)

numlist.append(value)

average = sum(numlist) / len(numlist)

print('Average:', average)

# Code: http://www.pythonlearn.com/code3/avelist.py
Fazemos uma lista vazia antes do início do loop e, a cada vez que tivermos um número, nós o
anexamos à lista. No final do programa, simplesmente calculamos a soma dos números na lista
e divido-a pela contagem dos números na lista para ver a média.

8.9 Listas e strings


Uma string é uma sequência de caracteres e uma lista é uma sequência de valores, mas uma
lista de caracteres não é o mesmo que uma string. Para converter de uma string para uma lista
de caracteres, você pode usar a lista:

>>> s = 'spam'

>>> t = list(s)

>>> print(t)

['s', 'p', 'a', 'm']

Como lista é o nome de uma função interna, você deve evitar usá-la como um nome variável.
Também evite a letra l porque parece muito com o número 1. É por isso que eu uso t.

A função de lista quebra uma string em letras individuais. Se você quiser quebrar uma string
em palavras, você pode usar o método split:

>>> s = 'pining for the fjords'

>>> t = s.split()

>>> print(t)

['pining', 'for', 'the', 'fjords']

>>> print(t[2])

The

Depois de usar split para quebrar a string em uma lista de palavras, você pode usar o comando
operador de índice (colchete) para examinar uma palavra específica na lista.

Você pode chamar split com um argumento opcional chamado delimitador que especifica
quais caracteres usar como limites de palavras. O exemplo a seguir usa um hífen como
delimitador:

>>> s = 'spam-spam-spam'

>>> delimiter = '-'


>>> s.split(delimiter)

['spam', 'spam', 'spam']

join é o inverso de split. Ele pega uma lista de strings e concatena os elementos. join é um
método de string, então você deve invocá-lo no delimitador e passar a lista como parâmetro:

>>> t = ['pining', 'for', 'the', 'fjords']

>>> delimiter = ' '

>>> delimiter.join(t)

'pining for the fjords'

Nesse caso, o delimitador é um caractere de espaço; portanto, join coloca um espaço entre as
palavras. Para concatenar strings sem espaços, você pode usar a string vazia, “”, como um
delimitador.

8.10 Linhas de análise


Normalmente, quando estamos lendo um arquivo, queremos fazer algo nas linhas além de
apenas imprimindo toda a linha. Muitas vezes queremos encontrar as “linhas interessantes” e
então analisar a linha para encontrar alguma parte interessante da linha. E se quiséssemos
imprimir, retirar o dia da semana das linhas que começam com “De”?

From stephen.marquard@uct.ac.zaSat Jan 5 09:14:16 2008

O método split é muito eficaz quando confrontado com este tipo de problema. Podermos
escrever um pequeno programa que procure linhas onde a linha começa com “From”, Split
essas linhas e, em seguida, imprimir a terceira palavra na linha:

fhand = open('mbox-short.txt')

for line in fhand:

line = line.rstrip()

if not line.startswith('From '): continue

words = line.split()

print(words[2])

# Code: http://www.pythonlearn.com/code3/search5.py
Aqui também usamos a forma contraída da instrução if onde colocamos o continue na mesma
linha do if. Esta forma contraída do if funciona o mesmo que se o continue estivesse na
próxima linha e recuado.

O programa produz a seguinte saída:

Sat

Fri

Fri

Fri

...

Mais tarde, aprenderemos técnicas cada vez mais sofisticadas para escolher as linhas para
trabalharmos e como separamos essas linhas para encontrar a informação exata que estamos
procurando.

8.11 Objetos e valores


Se executarmos estas instruções de atribuição:

a = 'banana'

b = 'banana'

Sabemos que a e b referem-se a uma string, mas não sabemos se eles se referem à mesma
corda. Existem dois estados possíveis:

Em um caso, a e b referem-se a dois objetos diferentes que têm o mesmo valor. No segundo
caso, eles se referem ao mesmo objeto.

Para verificar se duas variáveis se referem ao mesmo objeto, você pode usar a operação is
ator.

>>> a = 'banana'

>>> b = 'banana'

>>> a is b

True
Neste exemplo, o Python criou apenas um objeto string, e ambos a e b referem-se a isto.

Mas quando você cria duas listas, obtém dois objetos:

>>> a = [1, 2, 3]

>>> b = [1, 2, 3]

>>> a is b

False

Neste caso diríamos que as duas listas são equivalentes, pois possuem os mesmos elementos,
mas não idênticos, porque não são o mesmo objeto. Se dois objetos são idênticos, também
são equivalentes, mas se são equivalentes, não são necessariamente idênticos.

Até agora, usamos “objeto” e “valor” de forma intercambiável, mas é mais preciso dizer que
um objeto tem um valor. Se você executar a = [1,2,3], a se refere a um objeto de lista cujo
valor é uma sequência específica de elementos. Se outra lista tiver os mesmos elementos,
diríamos que tem o mesmo valor.

8.12 Aliasing
Se a se refere a um objeto e você atribui b = a, então ambas as variáveis se referem ao mesmo
objeto:

>>> a = [1, 2, 3]

>>> b = a

>>> b is a

True

A associação de uma variável com um objeto é chamada de referência. Neste exemplo,


existem duas referências ao mesmo objeto.

Um objeto com mais de uma referência tem mais de um nome, então dizemos que o objeto é
aliased.

Se o objeto com alias for mutável, as alterações feitas com um alias afetarão o outro:
>>> b[0] = 17

>>> print(a)

[17, 2, 3]

Embora esse comportamento possa ser útil, ele é propenso a erros. Em geral, é mais seguro
evitar aliasing quando estiver trabalhando com objetos mutáveis.

Para objetos imutáveis como strings, o aliasing não é um problema tão grande. Exemplo:

a = 'banana'

b = 'banana'

Quase nunca faz diferença se a e b se referem à mesma string ou não.

8.13 Listar argumentos


Quando você passa uma lista para uma função, a função obtém uma referência à lista. Se a
função modifica um parâmetro de lista, o chamador vê a mudança. Por exemplo, delete_head
remove o primeiro elemento de uma lista:

def delete_head(t):

del t[0]

Veja como é usado:

>>> letters = ['a', 'b', 'c']

>>> delete_head(letters)

>>> print(letters)

['b', 'c']

O parâmetro t e as letras variáveis são aliases para o mesmo objeto.

É importante distinguir entre operações que modificam listas e operações que criam novas
listas. Por exemplo, o método append modifica uma lista, mas o operador + cria uma nova
lista:
>>> t1 = [1, 2]

>>> t2 = t1.append(3)

>>> print(t1)

[1, 2, 3]

>>> print(t2)

None

>>> t3 = t1 + [3]

>>> print(t3)

[1, 2, 3]

>>> t2 is t3

False

Essa diferença é importante quando você escreve funções que devem modificar listas. Por
exemplo, esta função não exclui o cabeçalho de uma lista:

def bad_delete_head(t):

t = t[1:] # ERRADO!

O operador de fatia cria uma nova lista e a atribuição faz referência a ela, mas nada disso tem
efeito na lista que foi passada como argumento.

Uma alternativa é escrever uma função que crie e retorne uma nova lista. Por exemplo, tail
retorna todos menos o primeiro elemento de uma lista:

def tail(t):

return t[1:]

Esta função deixa a lista original inalterada. Veja como é usado:

>>> letters = ['a', 'b', 'c']

>>> rest = tail(letters)

>>> print(rest)
['b', 'c']

Exercício 1: Escreva uma função chamada chop que pegue uma lista e a modifique, removendo
o primeiro e últimos elementos e retorna None.

Em seguida, escreva uma função chamada middle que recebe uma lista e retorna uma nova
lista que contém todos, exceto o primeiro e o último elementos.

8.14 Depuração
O uso descuidado de listas (e outros objetos mutáveis) pode levar a longas horas de
depuração.

Aqui estão algumas armadilhas comuns e maneiras de evitá-las:

1. Não se esqueça de que a maioria dos métodos de lista modifica o argumento e retorna
None. Isso é o oposto dos métodos de string, que retornam uma nova string e deixa o original
em paz.

Se você está acostumado a escrever código de string como este:

word = word.strip()

É tentador escrever um código de lista como este: ~~ {.python} t = t.sort() #ERRADO! ~~

Como sort retorna None, a próxima operação que você executar com t provavelmente falhara.

Antes de usar métodos e operadores de lista, você deve ler a documentação cuidadosamente
e, em seguida, teste-os no modo interativo. Os métodos e os operadores que listam
compartilham com outras sequências (como strings) são documentados em
https://docs.python.org/2/library/stdtypes.html#string-methods . Os métodos e operadores
que se aplicam apenas a sequências mutáveis são documentados em
https://docs.python.org/2/library/stdtypes.html#mutable-sequence-types .

2. Escolha um idioma e fique com ele.

Parte do problema com as listas é que existem muitas maneiras de fazer as coisas. Por
exemplo, para remover um elemento de uma lista, você pode usar pop, remove, del, ou até
mesmo uma atribuição de fatia.

Para adicionar um elemento, você pode usar o método append ou o operador +. Mas não se
esqueça que estes estão certos:

t.append(x)

t = t + [x]
E estes estão errados:

t.append([x]) # ERRADO!

t = t.append(x) # ERRADO!

t + [x] # ERRADO!

t = t + x # ERRADO!

Experimente cada um desses exemplos no modo interativo para certificar-se de que você
entendeu suportar o que eles fazem. Observe que apenas o último causa um erro de execução;
os outros três são legais, mas fazem a coisa errada.

3. Faça cópias para evitar serrilhado.

Se você quiser usar um método como sort que modifica o argumento, mas você precisa
manter a lista original também, você pode fazer uma cópia.

orig = t[:]

t.sort()

Neste exemplo, você também pode usar a função interna sorted, que retorna uma nova lista
ordenada e deixa o original sozinho. Mas nesse caso você deve evitar usar sort como um nome
de variável!

4. Listas, divisões e arquivos

Quando lemos e analisamos arquivos, há muitas oportunidades de encontrar entrada que


pode travar nosso programa, por isso é uma boa ideia revisitar o guardião padrão quando se
trata de escrever programas que leem um arquivo e olham para uma “agulha no palheiro”.

Vamos revisitar nosso programa que está procurando o dia da semana na partir de linhas do
nosso arquivo:

From stephen.marquard@uct.ac.zaSatJan 5 09:14:16 2008

Como estamos quebrando esta linha em palavras, poderíamos simplesmente olhar para a
primeira palavra da linha para determinar se estamos interessados na linha em todo. Podemos
usar continue para pular linhas que não tenha "De" como a primeira palavra da seguinte
forma:
fhand = open('mbox-short.txt')

for line in fhand:

words = line.split()

if words[0] != 'From' : continue

print(words[2])

Isso parece muito mais simples e nem precisamos fazer o rstrip para remover a nova linha no
final do arquivo. Mas é melhor?

python search8.py

Sat

Traceback (most recent call last):

File "search8.py", line 5, in <module>

if words[0] != 'From' : continue

IndexError: list index out of range

Isso meio que funciona e vemos o dia da primeira linha (Sáb), mas depois o programa falha
com um erro de rastreamento. O que deu errado? que bagunça de dados causaram a falha de
nosso elegante, inteligente e muito Pythonic programa? 

Você pode olhar para ele por um longo tempo e decifrá-lo ou perguntar a alguém para obter
ajuda, mas a abordagem mais rápida e inteligente é adicionar uma instrução de impressão. O
melhor lugar para adicionar a instrução de impressão é logo antes da linha onde o programa
falhou e imprimir os dados que parecem estar causando a falha.

for line in fhand:

words = line.split()

print('Debug:', words)

if words[0] != 'From' : continue

print(words[2])

Quando executamos o programa, muitas saídas saem da tela, mas no final, vemos nossa saída
de depuração e o traceback para sabermos o que aconteceu pouco antes do traceback.

Debug: ['X-DSPAM-Confidence:', '0.8475']


Debug: ['X-DSPAM-Probability:', '0.0000']

Debug: []

Traceback (most recent call last):

File "search9.py", line 6, in <module>

if words[0] != 'From' : continue

IndexError: list index out of range

Cada linha de depuração está imprimindo a lista de palavras que obtemos quando dividimos a
linha em palavras. Quando o programa falha, a lista de palavras fica vazia []. Se abrirmos o
arquivo em um editor de texto e olharmos para o arquivo, nesse ponto parece do seguinte
modo:

X-DSPAM-Result: Innocent

X-DSPAM-Processed: Sat Jan 5 09:14:16 2008

X-DSPAM-Confidence: 0.8475

X-DSPAM-Probability: 0.0000

Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772

O erro ocorre quando nosso programa encontra uma linha em branco! claro que são “palavras
zero” em uma linha em branco. Por que não pensamos nisso quando estávamos escrevendo o
código? Quando o código procura a primeira palavra (palavra[0]) para verificar e ver se
corresponde a “De”, obtemos um erro de “índice fora do intervalo”.

É claro que este é o lugar perfeito para adicionar algum código de guardião para evitar
checagem. a primeira palavra se a primeira palavra não estiver lá. Existem muitas maneiras de
proteja este código; escolheremos verificar o número de palavras que temos antes de
olharmos para a primeira palavra:

fhand = open('mbox-short.txt')

count = 0

for line in fhand:

words = line.split()

# print 'Debug:', words

if len(words) == 0 : continue

if words[0] != 'From' : continue


print(words[2])

Primeiro, comentamos a instrução de impressão de depuração em vez de removê-la, caso


nossa modificação falhe e precisemos depurar novamente. Então nós adicionamos uma
declaração do responsável que verifica se temos zero palavras e, em caso afirmativo, use
continue para pular para a próxima linha no arquivo.

Podemos pensar nas duas instruções continue como nos ajudando a refinar o conjunto de
linhas que são “interessantes” para nós e que queremos processar um pouco mais. Uma linha
que não tem palavras “desinteressante” para nós, então pulamos para a próxima linha. Uma
linha que não tem “De” como primeira palavra é desinteressante para nós, então nós o
ignoramos.

O programa modificado é executado com sucesso, então talvez esteja correto. Nossa
declaração do guardião garante que as palavras [0] nunca falharão, mas talvez não seja
suficiente. Quando estamos programando, devemos estar sempre pensando: "O que pode dar
errado?"

Exercício 2: Descubra qual linha do programa acima ainda não está devidamente protegida.
Veja se você pode construir um arquivo de texto que faz com que o programa falhe e
modifique o programa para que a linha esteja devidamente protegida e teste-a para ter
certeza de que lida com seu novo arquivo de texto.

Exercício 3: Reescreva o código do guardião no exemplo acima sem duas instruções if. Em vez
disso, use uma expressão lógica composta usando o operador lógico and com uma única
instrução if.

8.15 Glossário
Aliasing - Uma circunstância em que duas ou mais variáveis se referem ao mesmo objeto.

Delimitador - Um caractere ou string usado para indicar onde uma string deve ser dividida.

Elemento - Um dos valores em uma lista (ou outra sequência); também chamados de itens.

Equivalente - Tendo o mesmo valor.

Índice - Um valor inteiro que indica um elemento em uma lista.

Idêntico - Ser o mesmo objeto (o que implica equivalência).

Lista - Uma sequência de valores.

Travessia de lista - O acesso sequencial de cada elemento em uma lista.

Lista aninhada - Uma lista que é um elemento de outra lista.

Objeto - Algo a que uma variável pode se referir. Um objeto tem um tipo e um valor.

Referência - A associação entre uma variável e seu valor.


8.16 Exercícios
Exercício 4: Baixe uma cópia do arquivo em www.pythonlearn.com/code3/romeo.txt

Escreva um programa para abrir o arquivo romeo.txt e leia-o linha por linha. Para cada linha,
divida a linha em uma lista de palavras usando a função split.

Para cada palavra, verifique se a palavra já está em uma lista. Se a palavra não estiver em uma
lista, adicioná-lo à lista.

Quando o programa for concluído, classifique e imprima as palavras resultantes em ordem


alfabética.

Enter file: romeo.txt

['Arise', 'But', 'It', 'Juliet', 'Who', 'already',

'and', 'breaks', 'east', 'envious', 'fair', 'grief',

'is', 'kill', 'light', 'moon', 'pale', 'sick', 'soft',

'sun', 'the', 'through', 'what', 'window',

'with', 'yonder']

Exercício 5: Escreva um programa para ler os dados da caixa de correio e quando você
encontrar a linha que começa com “From”, você dividirá a linha em palavras usando a função
divisão. Estamos interessados em saber quem enviou a mensagem, que é a segunda palavra na
linha De.

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Você analisará a linha De e imprimirá a segunda palavra para cada linha De, então você
também contará o número de linhas De (não De:) e imprimirá no final.

Este é um bom exemplo de saída com algumas linhas removidas:

python fromcount.py

Enter a file name: mbox-short.txt

stephen.marquard@uct.ac.za

louis@media.berkeley.edu

zqian@umich.edu

[...algumas saídas removidas...]


ray@media.berkeley.edu

cwen@iupui.edu

cwen@iupui.edu

cwen@iupui.edu

Havia 27 linhas no arquivo com From como a primeira palavra

Exercício 6: Reescreva o programa que solicita ao usuário uma lista de números e imprima o
máximo e o mínimo dos números no final quando o usuário entra "concluído". Escreva o
programa para armazenar os números que o usuário insere em uma lista e use as funções
max() e min() para calcular o máximo e o mínimo de números após a conclusão do loop.

Digite um número: 6

Digite um número: 2

Digite um número: 9

Digite um número: 3

Digite um número: 5

Digite um número: feito

Máximo: 9,0

Mínimo: 2,0
Capítulo 9
Dicionários

Um dicionário é como uma lista, mas mais geral. Em uma lista, as posições de índice devem ser
inteiros; em um dicionário, os índices podem ser (quase) de qualquer tipo.

Você pode pensar em um dicionário como um mapeamento entre um conjunto de índices (que
são chamadas chaves) e um conjunto de valores. Cada chave é mapeada para um valor. A
associação de uma chave e um valor é chamado de par chave-valor ou às vezes um item.

Como exemplo, construiremos um dicionário que mapeia palavras em inglês para espanhol,
então as chaves e os valores são todos strings.

A função dict cria um novo dicionário sem itens. Porque o ditado é o nome de uma função
interna, você deve evitar usá-lo como um nome de variável.

>>> eng2sp = dict()

>>> print(eng2sp)

{}

As chaves, {}, representam um dicionário vazio. Para adicionar itens ao dicionário, você pode
usar colchetes:

>>> eng2sp['one'] = 'uno'

Esta linha cria um item que mapeia da chave 'one' para o valor "uno". Se nós imprimirmos o
dicionário novamente, vemos um par chave-valor com dois pontos entre a chave e o valor:

>>> print(eng2sp)

{'one': 'uno'}

Este formato de saída também é um formato de entrada. Por exemplo, você pode criar um
novo dicionário com três itens. Mas se você imprimir eng2sp, poderá se surpreender:
>>> eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}

>>> print(eng2sp)

{'one': 'uno', 'three': 'tres', 'two': 'dos'}

A ordem dos pares chave-valor não é a mesma. Na verdade, se você digitar o mesmo exemplo
em seu computador, você pode obter um resultado diferente. Em geral, a ordem dos itens em
um dicionário é imprevisível.

Mas isso não é um problema porque os elementos de um dicionário nunca são indexados com
índices inteiros. Em vez disso, você usa as chaves para procurar os valores correspondentes:

>>> print(eng2sp['two'])

'dos'

A chave 'dos' sempre mapeia para o valor "dos" para que a ordem dos itens não matter.

Se a chave não estiver no dicionário, você receberá uma exceção:

>>> print(eng2sp['four'])

KeyError: 'four'

A função len funciona em dicionários; ele retorna o número de pares chave-valor:

>>> len(eng2sp)

O operador in funciona em dicionários; ele informa se algo aparece como uma chave no
dicionário (aparecer como um valor não é bom o suficiente).

>>> 'one' in eng2sp

True

>>> 'uno' in eng2sp

False
Para ver se algo aparece como um valor em um dicionário, você pode usar os valores do
método, que retorna os valores como uma lista e, em seguida, use o operador in:

>>> vals = list(eng2sp.values())

>>> 'uno' in vals

True

O operador in usa algoritmos diferentes para listas e dicionários. Para listas, usa um algoritmo
de busca linear. À medida que a lista aumenta, o tempo de pesquisa aumenta em proporção
direta ao comprimento da lista. Para dicionários, o Python usa um algoritmo chamado tabela
hash que possui uma propriedade notável: o operador in leva aproximadamente a mesma
quantidade de tempo, não importa quantos itens haja em um dicionário. Não vou explicar por
que as funções de hash são tão mágicas, mas você pode ler mais sobre isso em
wikipedia.org/wiki/Hash_table.

Exercício 1: [lista de palavras2]

Escreva um programa que leia as palavras em words.txt e as armazene como chaves em um


dicionário. Não importa quais sejam os valores. Então você pode usar o in operador como uma
maneira rápida de verificar se uma string está no dicionário.

9.1 Dicionário como um conjunto de contadores


Suponha que você receba uma string e queira contar quantas vezes cada letra parece. Existem
várias maneiras de você fazer isso:

1. Você pode criar 26 variáveis, uma para cada letra do alfabeto. Então você poderia
percorrer a string e, para cada caractere, incrementar o correspondente contador,
provavelmente usando uma condicional encadeada.

2. Você pode criar uma lista com 26 elementos. Então você pode converter cada caractere
a um número (usando a função integrada ord), use o número como um índice na lista e
incremente o contador apropriado.

3. Você pode criar um dicionário com caracteres como chaves e contadores como valores
correspondentes. A primeira vez que você vê um personagem, você deve adicionar um
item ao dicionário. Depois disso, você incrementaria o valor de um artigo existente.

Cada uma dessas opções executa o mesmo cálculo, mas cada uma delas implementa esse
cálculo de uma maneira diferente.
Uma implementação é uma forma de executar uma computação; algumas implementações
são melhores que outras. Por exemplo, uma vantagem da implementação do dicionário, a
vantagem é que não precisamos saber de antemão quais letras aparecem na string e só temos
que abrir espaço para as letras que aparecem.

Aqui está o que o código pode parecer:

word = 'brontosaurus'

d = dict()

for c in word:

if c not in d:

d[c] = 1

else:

d[c] = d[c] + 1

print(d)

Estamos efetivamente computando um histograma, que é um termo estatístico para um


conjunto de contadores (ou frequências).

O loop for percorre a string. Cada vez que passar pelo loop, se o caractere c não está no
dicionário, criamos um novo item com a chave c e o valor inicial 1 (já que vimos esta carta uma
vez). Se c já estiver no dicionário, incrementamos d[c].

Aqui está a saída do programa:

{'a': 1, 'b': 1, 'o': 2, 'n': 1, 's': 2, 'r': 2, 'u': 2, 't': 1}

O histograma indica que as letras 'a' e "b" aparecem uma vez; “o” aparece duas vezes, e assim
por diante.

Os dicionários têm um método chamado get que recebe uma chave e um valor padrão. Se o
key aparece no dicionário, get retorna o valor correspondente; caso contrário isto retorna o
valor padrão. Por exemplo:

>>> counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}

>>> print(counts.get('jan', 0))

100

>>> print(counts.get('tim', 0))


0

Podemos usar get para escrever nosso loop de histograma de forma mais concisa. Porque o
método get lida automaticamente com o caso em que uma chave não está em um dicionário,
podemos reduza quatro linhas para uma e elimine a instrução if.

word = 'brontosaurus'

d = dict()

for c in word:

d[c] = d.get(c,0) + 1

print(d)

A utilização do método get para simplificar esse loop de contagem acaba sendo comumente
um “idioma” usado em Python e vamos usá-lo muitas vezes no resto do e-book. Portanto, você
deve parar um momento e comparar o loop usando a instrução if e no operador com o loop
usando o método get. Eles fazem exatamente a mesma coisa, mas uma é mais sucinta.

9.2 Dicionários e arquivos


Um dos usos comuns de um dicionário é contar a ocorrência de palavras em um arquivo com
algum texto escrito. Vamos começar com um arquivo muito simples de palavras tiradas do
texto de Romeu e Julieta.

Para o primeiro conjunto de exemplos, usaremos uma versão abreviada e simplificada do texto
sem pontuação. Posteriormente trabalharemos com o texto da cena com pontuação incluída.

But soft what light through yonder window breaks

It is the east and Juliet is the sun

Arise fair sun and kill the envious moon

Who is already sick and pale with grief

Vamos escrever um programa Python para ler as linhas do arquivo, quebrar cada linha em uma
lista de palavras e, em seguida, percorrer cada uma das palavras na linha e conte cada palavra
usando um dicionário.

Você verá que temos dois loops for. O loop externo está lendo as linhas do arquivo e o loop
interno está iterando através de cada uma das palavras naquela linha em particular. Este é um
exemplo de um padrão chamado loops aninhados porque um dos loops é o loop externo e o
outro loop é o loop interno.

Como o loop interno executa todas as suas iterações cada vez que o loop externo faz uma
única iteração, pensamos no loop interno como iterando “mais rapidamente” e o loop externo
como iteração mais lenta.

A combinação dos dois loops aninhados garante que contaremos cada palavra em cada linha
do arquivo de entrada.

fname = input('Enter the file name: ')

try:

fhand = open(fname)

except:

print('File cannot be opened:', fname)

exit()

counts = dict()

for line in fhand:

words = line.split()

for word in words:

if word not in counts:

counts[word] = 1

else:

counts[word] += 1

print(counts)

# Code: http://www.pythonlearn.com/code3/count1.py

Quando executamos o programa, vemos um despejo bruto de todas as contagens em hash não
classificado em ordem. (o arquivo romeo.txt está disponível em
www.pythonlearn.com/code3/romeo.txt )

python count1.py

Enter the file name: romeo.txt

{'and': 3, 'envious': 1, 'already': 1, 'fair': 1,


'is': 3, 'through': 1, 'pale': 1, 'yonder': 1,

'what': 1, 'sun': 2, 'Who': 1, 'But': 1, 'moon': 1,

'window': 1, 'sick': 1, 'east': 1, 'breaks': 1,

'grief': 1, 'with': 1, 'light': 1, 'It': 1, 'Arise': 1,

'kill': 1, 'the': 3, 'soft': 1, 'Juliet': 1}

É um pouco inconveniente procurar no dicionário as palavras mais comuns e suas contagens,


então precisamos adicionar mais algum código Python para obtermos a saída que será mais
útil.

9.3 Looping e dicionários


Se você usar um dicionário como sequência em uma instrução for, ele percorrerá as chaves do
dicionário. Este loop imprime cada chave e o valor correspondente:

counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}

for key in counts:

print(key, counts[key])

Aqui está a aparência da saída:

jan 100

chuck 1

annie 42

Novamente, as teclas não estão em nenhuma ordem específica.

Podemos usar esse padrão para implementar os vários idiomas de loop que criamos escrito
anteriormente. Por exemplo, se quisermos encontrar todas as entradas em um dicionário com
um valor acima de dez, poderíamos escrever o seguinte código:
counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}

for key in counts:

if counts[key] > 10 :

print(key, counts[key])

O loop for itera pelas chaves do dicionário, então devemos usar o índice operador para
recuperar o valor correspondente para cada chave. Aqui está o que a saída parece:

jan 100

annie 42

Vemos apenas as entradas com um valor acima de 10.

Se você deseja imprimir as chaves em ordem alfabética, primeiro faça uma lista das chaves no
dicionário usando o método de chaves disponível em objetos de dicionário e, em seguida,
classifique essa lista e percorra a lista classificada, procurando cada chave e imprimindo os
pares chave-valor na ordem classificada da seguinte forma:

counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}

lst = list(counts.keys())

print(lst)

lst.sort()

for key in lst:

print(key, counts[key])

Aqui está a aparência da saída:

['jan', 'chuck', 'annie']

annie 42

chuck 1

jan 100

Primeiro, você vê a lista de chaves em ordem não classificada que obtemos do método keys.
Em seguida, vemos os pares chave-valor na ordem do loop for.

9.4 Análise de texto avançada


No exemplo acima, usando o arquivo romeo.txt, tornamos o arquivo o mais simples possível,
removendo toda a pontuação manualmente. O texto real tem muita pontuação, como
mostrado abaixo.

But, soft! what light through yonder window breaks?

It is the east, and Juliet is the sun.

Arise, fair sun, and kill the envious moon,

Who is already sick and pale with grief,

Como a função split do Python procura por espaços e trata palavras como tokens separados
por espaços, trataríamos as palavras “suave!” e “suave” como palavras diferentes e criaria
uma entrada de dicionário separada para cada palavra.

Além disso, como o arquivo possui letras maiúsculas, trataríamos “quem” e “Quem” como
diferentes palavras com contagens diferentes.

Podemos resolver esses dois problemas usando os métodos de string lower, punctuation, e
traduzir. A tradução é o mais sutil dos métodos. Aqui está a documentação para traduzir:

string.translate(s, table[, deletechars])

Exclua todos os caracteres de que estão em deletechars (se houver) e, em seguida, traduza os
caracteres usando a tabela, que deve ser uma string de 256 caracteres fornecendo a tradução
para cada valor de caractere, indexado por seu ordinal. Se a tabela for Nenhuma, somente a
etapa de exclusão de caracteres é executada.

Não especificaremos a tabela, mas usaremos o parâmetro deletechars para excluir toda a
pontuação. Vamos até deixar Python nos dizer a lista de caracteres que considera
“pontuação”:
>>> import string

>>> string.punctuation

'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

Fazemos as seguintes modificações em nosso programa:

import string

fname = input('Enter the file name: ')

try:

fhand = open(fname)

except:

print('File cannot be opened:', fname)

exit()

counts = dict()

for line in fhand:

line = line.rstrip()

line = line.translate(line.maketrans('', '', string.punctuation))

line = line.lower()

words = line.split()

for word in words:

if word not in counts:

counts[word] = 1

else:

counts[word] += 1

print(counts)

# Code: http://www.pythonlearn.com/code3/count2.py

Usamos translate para remover toda a pontuação e lower para forçar a linha a diminuir. Caso
contrário, o programa permanece inalterado. Observe que para Python 2.5 e anteriores,
translate não aceita None como o primeiro parâmetro, então use este código para a chamada
de tradução:

print a.translate(string.maketrans(' ',' '), string.punctuation


Parte de aprender a “Arte do Python” ou “Pensar Pythonicamente” é perceber que o Python
geralmente possui recursos integrados para muitos problemas comuns de análise de dados.
Com o tempo, você verá código de exemplo suficiente e lerá o suficiente da documentação
para saber onde procurar para ver se alguém já escreveu algo que faça seu trabalho muito
mais fácil.

O seguinte é uma versão abreviada da saída:

Enter the file name: romeo-full.txt

{'swearst': 1, 'all': 6, 'afeard': 1, 'leave': 2, 'these': 2,

'kinsmen': 2, 'what': 11, 'thinkst': 1, 'love': 24, 'cloak': 1,

a': 24, 'orchard': 2, 'light': 5, 'lovers': 2, 'romeo': 40,

'maiden': 1, 'whiteupturned': 1, 'juliet': 32, 'gentleman': 1,

'it': 22, 'leans': 1, 'canst': 1, 'having': 1, ...}

Analisar essa saída ainda é difícil de manejar e podemos usar o Python para nos fornecer
exatamente o que estamos procurando, mas, para isso, precisamos aprender sobre as tuplas
do Python.

Vamos pegar este exemplo assim que aprendermos sobre tuplas.

9.5 Depuração
À medida que você trabalha com conjuntos de dados maiores, pode se tornar complicado
depurar imprimindo e verificando os dados manualmente. Aqui estão algumas sugestões para
depurar grandes conjuntos de dados:

Reduza a entrada Se possível, reduza o tamanho do conjunto de dados. Por exemplo se o


programa ler um arquivo de texto, comece apenas com as primeiras 10 linhas ou com o menor
exemplo que você pode encontrar. Você pode editar os próprios arquivos ou (melhor)
modifique o programa para que ele leia apenas as primeiras n linhas. Se houver um erro, você
pode reduzir n ao menor valor que manifesta o erro e, em seguida, aumente-o gradualmente
conforme você encontra e corrige erros.

Verifique resumos e tipos Em vez de imprimir e verificar todo o conjunto de dados, considere
imprimir resumos dos dados: por exemplo, o número de itens em um dicionário ou o total de
uma lista de números. Uma causa comum de erros de tempo de execução é um valor que não
é do tipo correto. Para depurando esse tipo de erro, muitas vezes basta imprimir o tipo de um
valor.

Escreva autoverificações Às vezes, você pode escrever código para verificar erros
automaticamente. Calmamente. Por exemplo, se você estiver calculando a média de uma lista
de números, você poderia verificar se o resultado não é maior que o maior elemento da lista
ou menor que o menor. Isso é chamado de “verificação de sanidade” porque detecta
resultados que são “completamente ilógicos”.

Outro tipo de verificação compara os resultados de dois cálculos para ver se eles são
consistentes. Isso é chamado de "checagem de Consistência".

Imprima bem a saída A formatação da saída de depuração pode facilitar a detectar um erro.

Novamente, o tempo gasto na construção de andaimes pode reduzir o tempo gasto na


depuração.

9.6 Glossário
Dicionário - Um mapeamento de um conjunto de chaves para seus valores correspondentes.

Hashtable - O algoritmo usado para implementar dicionários Python.

Função de hash - Uma função usada por uma tabela de hash para calcular a localização de uma
chave.

Histograma - Um conjunto de contadores.

Implementação - Uma maneira de executar uma computação.

Item - Outro nome para um par chave-valor.

Chave - Um objeto que aparece em um dicionário como a primeira parte de um par chave-
valor.

Par chave-valor - A representação do mapeamento de uma chave para um valor.

Lookup - Uma operação de dicionário que pega uma chave e localiza o valor correspondente.

Loops aninhados - Quando há um ou mais loops “dentro” de outro loop. O loop interno é
executado toda vez que o loop externo é executado uma vez, até a conclusão.

Valor - Um objeto que aparece em um dicionário como a segunda parte de um valor-chave


par. Isso é mais específico do que nosso uso anterior da palavra “valor”.

9.7 Exercícios
Exercício 2: Escreva um programa que classifique cada mensagem de correio em que dia da
semana em que o commit foi feito. Para fazer isso, procure por linhas que começam com
“From”, então procure a terceira palavra e mantenha uma contagem contínua de cada um dos
dias da semana. No final do programa imprima o conteúdo do seu dicionário (encomende Não
importa).

Linha de amostra:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008


Exemplo de Execução:

python dow.py

Enter a file name: mbox-short.txt

{'Fri': 20, 'Thu': 6, 'Sat': 1}

Exercício 3: Escreva um programa para ler um log de correio, construa um histograma usando
um dicionário para contar quantas mensagens vieram de cada endereço de e-mail e imprima o
dicionário.

Enter file name: mbox-short.txt

{'gopal.ramasammycook@gmail.com': 1, 'louis@media.berkeley.edu': 3,

'cwen@iupui.edu': 5, 'antranig@caret.cam.ac.uk': 1,

'rjlowe@iupui.edu': 2, 'gsilver@umich.edu': 3,

'david.horwitz@uct.ac.za': 4, 'wagnermr@iupui.edu': 1,

'zqian@umich.edu': 4, 'stephen.marquard@uct.ac.za': 2,

'ray@media.berkeley.edu': 1}

Exercício 4: Adicione um código ao programa acima para descobrir quem tem mais mensagens
no arquivo.

Após a leitura de todos os dados e a criação do dicionário, consulte o dicionário usando um


loop máximo (consulte a seção [maximumloop]) para descobrir quem tem mais mensagens e
imprima quantas mensagens a pessoa tem.

Enter a file name: mbox-short.txt

cwen@iupui.edu 5

Enter a file name: mbox.txt

zqian@umich.edu 195

Exercício 5: Este programa registra o nome de domínio (em vez do endereço) onde a
mensagem foi enviada em vez de quem o e-mail veio (ou seja, todo o e-mail de endereço). No
final do programa, imprima o conteúdo do seu dicionário.

python schoolcount.py
Enter a file name: mbox-short.txt

{'media.berkeley.edu': 4, 'uct.ac.za': 6, 'umich.edu': 7,

'gmail.com': 1, 'caret.cam.ac.uk': 1, 'iupui.edu': 8}


Capítulo 10
Tuplas

10.1 Tuplas são imutáveis


uma tupla é uma sequência de valores muito parecida com uma lista. Os valores armazenados
em uma tupla podem ser de qualquer tipo e são indexados por números inteiros. A diferença
importante é que as tuplas são imutáveis. Tuplas também são comparáveis e hasháveis para
que possamos classificar listas deles e usar tuplas como valores-chave em dicionários Python.
Sintaticamente, uma tupla é uma lista de valores separados por vírgulas:

>>> t = 'a', 'b', 'c', 'd', 'e'

Embora não seja necessário, é comum colocar tuplas entre parênteses para ajudar
identificarmos tuplas rapidamente quando olhamos para o código Python:

>>> t = ('a', 'b', 'c', 'd', 'e')

Para criar uma tupla com um único elemento, você deve incluir a vírgula final:

>>> t1 = ('a',)

>>> type(t1)

<type 'tuple'>

Sem a vírgula, o Python trata ('a') como uma expressão com uma string entre parênteses, teses
que é avaliada como uma string:

>>> t2 = ('a')

>>> type(t2)

<type 'str'>
Outra maneira de construir uma tupla é a tupla de função interna. Sem argumentos, ele cria
uma tupla vazia:

>>> t = tuple()

>>> print(t)

()

Se o argumento for uma sequência (string, lista ou tupla), o resultado da chamada para tupla é
uma tupla com os elementos da sequência:

>>> t = tuple('lupins')

>>> print(t)

('l', 'u', 'p', 'i', 'n', 's')

Como tupla é o nome de um construtor, você deve evitar usá-la como uma variável nome.

A maioria dos operadores de lista também funciona em tuplas. O operador colchete indexa um
elemento:

>>> t = ('a', 'b', 'c', 'd', 'e')

>>> print(t[0])

'a'

E o operador de fatia seleciona um intervalo de elementos.

>>> print(t[1:3])

('b', 'c')

Mas se você tentar modificar um dos elementos da tupla, receberá um erro:

>>> t[0] = 'A'

TypeError: object doesn't support item assignment


Você não pode modificar os elementos de uma tupla, mas pode substituir uma tupla por outra:

>>> t = ('A',) + t[1:]

>>> print(t)

('A', 'b', 'c', 'd', 'e')

10.2 Comparando tuplas


Os operadores de comparação trabalham com tuplas e outras sequências. Python começa
comparando o primeiro elemento de cada sequência. Se forem iguais, segue para o próximo
elemento, e assim por diante, até encontrar elementos diferentes. Elementos subsequentes
não são considerados (mesmo que sejam muito grandes).

>>> (0, 1, 2) < (0, 3, 4)

True

>>> (0, 1, 2000000) < (0, 3, 4)

True

A função de classificação funciona da mesma maneira. Ele classifica principalmente pelo


primeiro elemento, mas em caso de empate, ordena por segundo elemento, e assim
sucessivamente. Esse recurso se presta a um padrão chamado DSU

Decore uma sequência construindo uma lista de tuplas com uma ou mais chaves de
classificação precedendo os elementos da sequência

Classifique (Sort) a lista de tuplas usando a classificação interna do Python e

Remova (Undecorate) a decoração extraindo os elementos classificados da sequência.

[DSU]

Por exemplo, suponha que você tenha uma lista de palavras e queira classificá-las de mais
longo para o mais curto:
txt = 'but soft what light in yonder window breaks'

words = txt.split()

t = list()

for word in words:

t.append((len(word), word))

t.sort(reverse=True)

res = list()

for length, word in t:

res.append(word)

print(res)

# Code: http://www.pythonlearn.com/code3/soft.py

O primeiro loop constrói uma lista de tuplas, onde cada tupla é uma palavra precedida por seu
comprimento.

sort compara o primeiro elemento, length, first, e considera apenas o segundo elemento para
desempate. O argumento de palavra-chave reverse=True diz ao sort para entrar ordem
decrescente.

O segundo loop percorre a lista de tuplas e constrói uma lista de palavras em ordem
descendente. Ordem de comprimento. As palavras de quatro caracteres são classificadas em
ordem alfabética reversa, então “what” aparece antes de “soft” na lista a seguir.

A saída do programa é a seguinte:

['yonder', 'window', 'breaks', 'light', 'what',

'soft', 'but', 'in']

É claro que a linha perde muito de seu impacto poético quando transformada em uma lista
Python e classificados em ordem de comprimento de palavra decrescente.

10.3 Atribuição de tupla


Um dos recursos sintáticos exclusivos da linguagem Python é a capacidade de ter uma tupla no
lado esquerdo de uma instrução de atribuição. Isso permite que você atribua mais de uma
variável de cada vez quando o lado esquerdo é uma sequência.

Neste exemplo, temos uma lista de dois elementos (que é uma sequência) e atribuímos o
primeiro e os segundos elementos da sequência às variáveis x e y em uma única instrução.
>>> m = [ 'have', 'fun' ]

>>> x, y = m

>>> x

'have'

>>> y

'fun'

>>>

Não é mágica, o Python traduz aproximadamente a sintaxe de atribuição de tupla para ser a
seguinte:

>>> m = [ 'have', 'fun' ]

>>> x = m[0]

>>> y = m[1]

>>> x

'have'

>>> y

'fun'

>>>

Estilisticamente, quando usamos uma tupla no lado esquerdo da instrução de atribuição,


omita os parênteses, mas o seguinte é uma sintaxe igualmente válida:

>>> m = [ 'have', 'fun' ]

>>> (x, y) = m

>>> x

'have'

>>> y

'fun'

>>>
Uma aplicação particularmente inteligente de atribuição de tuplas nos permite trocar os
valores de duas variáveis em uma única instrução:

>>> a, b = b, a

Ambos os lados dessa instrução são tuplas, mas o lado esquerdo é uma tupla de variáveis, o
lado direito é uma tupla de expressões. Cada valor no lado direito é atribuído à sua respectiva
variável no lado esquerdo. Todas as expressões do lado direito são avaliados antes de qualquer
uma das tarefas.

O número de variáveis à esquerda e o número de valores à direita devem ser o mesmo:

>>> a, b = 1, 2, 3

ValueError: too many values to unpack

Mais geralmente, o lado direito pode ser qualquer tipo de sequência (string, lista ou tupla). Por
exemplo, para dividir um endereço de e-mail em um nome de usuário e um domínio, você
pode escrever:

>>> addr = 'monty@python.org'

>>> uname, domain = addr.split('@')

O valor de retorno de split é uma lista com dois elementos; o primeiro elemento é atribuído
para uname, o segundo para domínio.

>>> print(uname)

monty

>>> print(domain)

python.org

10.4 Dicionários e tuplas


Os dicionários possuem um método chamado items que retorna uma lista de tuplas, onde
cada tupla é um par chave-valor:
>>> d = {'a':10, 'b':1, 'c':22}

>>> t = list(d.items())

>>> print(t)

[('b', 1), ('a', 10), ('c', 22)]

Como você deve esperar de um dicionário, os itens não estão em nenhuma ordem específica.

No entanto, como a lista de tuplas é uma lista e as tuplas são comparáveis, podemos agora
ordenar a lista de tuplas. Converter um dicionário em uma lista de tuplas é uma maneira de
produzir o conteúdo de um dicionário classificado por chave:

>>> d = {'a':10, 'b':1, 'c':22}

>>> t = list(d.items())

>>> t

[('b', 1), ('a', 10), ('c', 22)]

>>> t.sort()

>>> t

[('a', 10), ('b', 1), ('c', 22)]

A nova lista é classificada em ordem alfabética crescente pelo valor da chave.

10.5 Atribuição múltipla com dicionários


Combinando itens, atribuição de tupla e for, você pode ver um bom padrão de código para
percorrer as chaves e valores de um dicionário em um único loop:

for key, val in list(d.items()):

print(val, key)

Este loop tem duas variáveis de iteração porque items retorna uma lista de tuplas e chaves, val
é uma atribuição de tupla que itera sucessivamente por cada um dos valores-chave pares no
dicionário.
Para cada iteração no loop, a chave e o valor são avançados para o próximo par chave-valor no
dicionário (ainda em ordem de hash).

A saída deste loop é:

10 a

22 c

1b

Novamente, está na ordem da chave de hash (ou seja, nenhuma ordem específica).

Se combinarmos essas duas técnicas, podemos imprimir o conteúdo de um dicionário


classificados pelo valor armazenado em cada par chave-valor.

Para fazer isso, primeiro fazemos uma lista de tuplas onde cada tupla está (valor, chave). O
método items nos daria uma lista de tuplas (chave, valor), mas desta vez queremos para
classificar por valor, não por chave. Depois de construirmos a lista com a chave-valor tuplas, é
uma questão simples ordenar a lista na ordem inversa e imprimir a nova, lista ordenada.

>>> d = {'a':10, 'b':1, 'c':22}

>>> l = list()

>>> for key, val in d.items() :

... l.append( (val, key) )

...

>>> l

[(10, 'a'), (22, 'c'), (1, 'b')]

>>> l.sort(reverse=True)

>>> l

[(22, 'c'), (10, 'a'), (1, 'b')]

>>>

Construindo cuidadosamente a lista de tuplas para ter o valor como primeiro elemento de
cada tupla, podemos classificar a lista de tuplas e classificar o conteúdo do nosso dicionário
por valor.
10.6 As palavras mais comuns
Voltando ao nosso exemplo contínuo do texto de Romeu e Julieta, Ato 2, Cena 2, podemos
aumentar nosso programa para usar esta técnica para imprimir as dez palavras mais comuns
no texto como segue:

import string

fhand = open('romeo-full.txt')

counts = dict()

for line in fhand:

line = line.translate(string.punctuation)

line = line.lower()

words = line.split()

for word in words:

if word not in counts:

counts[word] = 1

else:

counts[word] += 1

# Ordenar o dicionário por valor

lst = list()

for key, val in list(counts.items()):

lst.append((val, key))

lst.sort(reverse=True)

for key, val in lst[:10]:

print(key, val)

# Code: http://www.pythonlearn.com/code3/count3.py

A primeira parte do programa que lê o arquivo e calcula o dicionário que mapeia cada palavra
para a contagem de palavras no documento permanece inalterada. Mas em vez de
simplesmente imprimir contagens e encerrar o programa, construímos uma lista de (val, key)
tuplas e, em seguida, classifique a lista na ordem inversa.

Como o valor é o primeiro, ele será usado para as comparações. Se houver mais de uma tupla
com o mesmo valor, ele vai olhar para o segundo elemento (a chave), então as tuplas em que
o valor é o mesmo serão classificadas pela ordem alfabética da chave.
No final, escrevemos um bom loop for que faz uma iteração de atribuição múltipla e imprime
as dez palavras mais comuns iterando por uma fatia da lista (primeiro[:10]).

Então agora a saída finalmente se parece com o que queremos para nossa análise de
frequência de palavras.

61 i

42 and

40 romeo

34 to

34 the

32 thou

32 juliet

30 that

29 my

24 thee

O fato de que essa complexa análise de dados pode ser feita com um método fácil de entender
o programa Python de 19 linhas é uma das razões pelas quais o Python é uma boa escolha
como uma linguagem para explorar informações.

10.7 Usando tuplas como chaves em dicionários


Como as tuplas são passíveis de hash e as listas não, se quisermos criar uma chave composta
para usar em um dicionário, devemos usar uma tupla como chave.

Encontraríamos uma chave composta se quiséssemos criar uma lista telefônica que mapeia
pares de sobrenome e primeiro nome para números de telefone. Assumindo que definimos as
variáveis last, first e number, poderíamos escrever um dicionário de declaração de atribuição
da seguinte forma:

directory[last,first] = number

A expressão entre colchetes é uma tupla. Poderíamos usar atribuição de tupla em um loop for
para percorrer este dicionário.
for last, first in directory:

print(first, last, directory[last,first])

Este loop percorre as chaves no diretório, que são tuplas. Atribui os elementos de cada tupla
para o último e o primeiro, depois imprime o nome e o telefone correspondente ao número.

10.8 Sequências: strings, listas e tuplas


Eu me concentrei em listas de tuplas, mas quase todos os exemplos neste capítulo também
trabalham com listas, tuplas de tuplas e tuplas de listas. Para evitar enumerar, considerando as
combinações possíveis, às vezes é mais fácil falar em sequências de sequências.

Em muitos contextos, os diferentes tipos de sequências (strings, listas e tuplas) podem ser
usados de forma intercambiáveis. Então, como e por que você escolhe um sobre os outros?

Para começar com o óbvio, as strings são mais limitadas do que outras sequências porque os
elementos devem ser caracteres. Eles também são imutáveis. Se você precisar da capacidade
de alterar os caracteres em uma string (em vez de criar uma nova string), você pode querer
usar uma lista de caracteres em vez disso.

Listas são mais comuns que tuplas, principalmente porque são mutáveis. Mas aqui são alguns
casos em que você pode preferir tuplas:

1. Em alguns contextos, como uma instrução return, é sintaticamente mais simples criar
uma tupla do que uma lista. Em outros contextos, você pode preferir uma lista.

2. Se você quiser usar uma sequência como uma chave de dicionário, você deve usar um
tipo mutável como uma tupla ou string.

3. Se você estiver passando uma sequência como argumento para uma função, usando
tuplas você reduz o potencial de comportamento inesperado devido ao aliasing.

Como as tuplas são imutáveis, elas não fornecem métodos como sort e reverse, que
modificam as listas existentes. No entanto, o Python fornece as funções integradas
classificadas e inversas, que pegam qualquer sequência como parâmetro e retornam uma nova
sequência com os mesmos elementos em uma ordem diferente.

10.9 Depuração
Listas, dicionários e tuplas são conhecidos genericamente como estruturas de dados; nesse
capítulo, estamos começando a ver estruturas de dados compostas, como listas de tuplas, e
dicionários que contêm tuplas como chaves e listas como valores. Estrutura de dados
compostos são úteis, mas são propensas ao que chamo de erros de forma; ou seja, erros
causados quando uma estrutura de dados tem o tipo, tamanho ou composição errados, ou
talvez você escreve algum código e esquece a forma de seus dados e introduz um erro.

Por exemplo, se você está esperando uma lista com um inteiro e eu lhe der um simples inteiro
(não em uma lista), não funcionará.

Quando você está depurando um programa, e especialmente se estiver trabalhando em um


bug, há quatro coisas para tentar:

Reading Examine seu código, leia-o para si mesmo e verifique se ele diz o que você quis
dizer.

Executando-o Experimente fazendo alterações e executando versões diferentes. Muitas


vezes se você exibir a coisa certa no lugar certo do programa, o problema ler e torna-se óbvio,
mas às vezes você tem que gastar algum tempo para construir andaimes.

Remoendo Tire um tempo para pensar! Que tipo de erro é: sintaxe, tempo de execução,
semântico? Quais informações você pode obter das mensagens de erro ou da saída do
programa? Que tipo de erro pode causar o problema que você está vendo? O que você mudou
por último, antes do problema aparecer?

Recuar Em algum momento, o melhor a fazer é recuar, desfazer as mudanças recentes, até
chegar a um programa que funcione e que você entenda. Então você pode começar a
reconstruir.

Os programadores iniciantes às vezes ficam presos em uma dessas atividades e esquecem os


outros. Cada atividade vem com seu próprio modo de falha.

Por exemplo, ler seu código pode ajudar se o problema for um erro tipográfico, mas não se o
problema for um mal-entendido conceitual. Se você não entender o que seu programa faz,
você pode lê-lo 100 vezes e nunca ver o erro, porque o erro está na sua cabeça.

A execução de experimentos pode ajudar, especialmente se você executar testes pequenos e


simples. Mas se você executa experimentos sem pensar ou ler seu código, você pode cair em
um padrão que chamo de “programação de passeio aleatório”, que é o processo de fazer
mudanças aleatórias até que o programa faça a coisa certa. Desnecessário dizer, alterações
aleatórias na programação pode levar muito tempo para ser resolvida.

Você tem que ter tempo para pensar. A depuração é como uma ciência experimental. Você
deve ter pelo menos uma hipótese sobre qual é o problema. Se houver dois ou mais
possibilidades, tente pensar em um teste que eliminaria uma delas.

Fazer uma pausa ajuda a pensar. Falar também. Se você explicar o problema para outra pessoa
(ou até para si mesmo), às vezes você encontrará a resposta antes que você termine de fazer a
pergunta.
Mas mesmo as melhores técnicas de depuração falharão se houver muitos erros ou se o
código que você está tentando corrigir for muito grande e complicado. Às vezes a melhor
opção é recuar, simplificando o programa até chegar a algo que funcione, e nisso, entendendo
o programa.

Os programadores iniciantes muitas vezes relutam em recuar porque não suportam, exclua
uma linha de código (mesmo que esteja errado). Se isso faz você se sentir melhor, copie seu
programa em outro arquivo antes de começar a desmontá-lo. Então você pode colar as peças
de volta um pouco de cada vez.

Encontrar um erro difícil requer leitura, corrida, ruminação e, às vezes, retirada. Se você ficar
preso em uma dessas atividades, tente as outras.

10.10 Glossário
Comparável - Um tipo onde um valor pode ser verificado para ver se é maior, menor ou igual a
outro valor do mesmo tipo. Tipos que são comparável pode ser colocado em uma lista e
classificado.

Estrutura de dados - Uma coleção de valores relacionados, geralmente organizados em listas,


dicionários, tuplas, etc.

DSU - Abreviação de “decorate-sort-undecorate”, um padrão que envolve a construção de


uma lista de tuplas, ordenando e extraindo parte do resultado.

Reunir - A operação de montagem de uma tupla de argumento de comprimento variável.

Hashable - Um tipo que tem uma função de hash. Tipos imutáveis como inteiros, flutuantes, e
strings são passíveis de hash; tipos mutáveis como listas e dicionários não são.

Dispersão - A operação de tratar uma sequência como uma lista de argumentos. forma (de
uma estrutura de dados) Um resumo do tipo, tamanho e composição de uma estrutura de
dados.

Singleton - Uma lista (ou outra sequência) com um único elemento.

Tupla - Uma sequência imutável de elementos.

Atribuição de tupla - Uma atribuição com uma sequência no lado direito e uma tupla de
variáveis à esquerda. O lado direito é avaliado e então seus elementos são atribuído às
variáveis à esquerda.

10.11 Exercícios
Exercício 1: Revise um programa anterior da seguinte forma: Leia e analise o “From” linhas e
retire os endereços da linha. Conte o número de mensagens de cada pessoa usando um
dicionário.

Após todos os dados terem sido lidos, imprima a pessoa com mais commits criando uma lista
de tuplas (count, email) do dicionário. Em seguida, classifique a lista em ordem inversa e
imprima a pessoa que tem mais commits.
Sample Line:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Enter a file name: mbox-short.txt

cwen@iupui.edu 5

Enter a file name: mbox.txt

zqian@umich.edu 195

Exercício 2: Este programa conta a distribuição da hora do dia para cada uma das mensagens.
Você pode extrair a hora da linha “from” encontrando a hora string e, em seguida, dividindo
essa string em partes usando o caractere de dois pontos. Uma vez que você acumulou as
contagens para cada hora, imprima as contagens, uma por linha, classificados por hora,
conforme mostrado abaixo.

Exemplo de Execução:

python timeofday.py

Enter a file name: mbox-short.txt

04 3

06 1

07 1

09 2

10 3

11 6

14 1

15 2

16 4

17 2

18 1

19 1

Exercício 3: Escreva um programa que leia um arquivo e imprima as letras em ordem


decrescente de frequência. Seu programa deve converter todas as entradas para letras
minúsculas e conte apenas as letras de a-z. Seu programa não deve contar espaços, dígitos,
pontuação, ou qualquer coisa diferente das letras a-z. Encontre amostras de texto de vários
idiomas e veja como a frequência das letras varia entre os idiomas. Compare os seus
resultados com as tabelas em wikipedia.org/wiki/Letter_frequencies.
Capítulo 11
Expressões regulares
Até agora, estivemos lendo arquivos, procurando padrões e extraindo vários pedaços de linhas
que achamos interessantes. Nós estavamos usando métodos de string como split e find e
usando listas e fatias de string para extrair partes das linhas.

Essa tarefa de pesquisar e extrair é tão comum que o Python tem uma ferramenta muito
poderosa na biblioteca chamada expressões regulares que lida com muitas dessas tarefas de
maneira bastante elegante. A razão pela qual não introduzimos expressões regulares
anteriormente neste e-book é porque embora sejam muito poderosos, são um pouco
complicados e sua sintaxe leva alguns tempo para ir se acostumando.

As expressões regulares são quase sua própria linguagem de programação para pesquisa e
analisar cadeias de caracteres. Na verdade, livros inteiros foram escritos sobre o tópico de
expressões regulares. Neste capítulo, abordaremos apenas os fundamentos das expressões.
Para obter mais detalhes sobre expressões regulares, consulte:

http://en.wikipedia.org/wiki/Regular_expression

https://docs.python.org/2/library/re.html

A biblioteca de expressões regulares deve ser importada para o seu programa antes de você
poder usá-la. O uso mais simples da biblioteca de expressões regulares é a função search(). O
programa a seguir demonstra um uso trivial da função de pesquisa.

# Procure pela linhas que contenham 'From'

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

if re.search('From:', line):

print(line)

# Code: http://www.pythonlearn.com/code3/re01.py

Abrimos o arquivo, percorremos cada linha e usamos a expressão regular search() para
imprimir apenas as linhas que contêm a string “From:”. Este programa não usa o poder real
das expressões regulares, já que poderíamos facilmente ter usado line.find() para obter o
mesmo resultado.

O poder das expressões regulares surge quando adicionamos caracteres especiais a string de
pesquisa que nos permite controlar com mais precisão quais linhas correspondem a corda.
Adicionar esses caracteres especiais à nossa expressão regular nos permite fazer combinação e
extração sofisticadas ao escrever muito pouco código.

Por exemplo, o caractere circunflexo é usado em expressões regulares para corresponder “o


início” de uma linha. Poderíamos mudar nosso programa para combinar apenas as linhas onde
“From:” estava no início da linha da seguinte forma:

# Procure por linhas que começam com 'From'

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

if re.search('^From:', line):

print(line)

# Code: http://www.pythonlearn.com/code3/re02.py

Agora, apenas corresponderemos às linhas que começam com a string “From:”. Isso ainda é
um exemplo muito simples que poderíamos ter feito de forma equivalente com o método
startwith() da biblioteca de strings. Mas serve para introduzir a noção de que regular
expressões contêm caracteres de ação especiais que nos dão mais controle sobre o que
corresponderá à expressão regular.

11.1 Correspondência de caracteres em expressões regulares


Existem vários outros personagens especiais que nos permitem construir personagens ainda
mais poderosos. O caractere especial mais comumente usado é o ponto ou ponto final, que
corresponde a qualquer caractere.

No exemplo a seguir, a expressão regular “F..m:” corresponderia a qualquer uma das strings
“From:”, “Fxxm:”, “F12m:” ou “F!@m:” desde que os caracteres de ponto na expressão regular
corresponde a qualquer caractere.

# Procure por linhas que começam com 'F', seguido por

# 2 caracteres, seguidos de 'm:'

import re

hand = open('mbox-short.txt')
for line in hand:

line = line.rstrip()

if re.search('^F..m:', line):

print(line)

# Code: http://www.pythonlearn.com/code3/re03.py

Isso é particularmente poderoso quando combinado com a capacidade de indicar que um


caractere pode ser repetido qualquer número de vezes usando os caracteres “*” ou “+” em
sua expressão regular. Esses caracteres especiais significam que, em vez de corresponder um
único caractere na string de pesquisa, eles correspondem a zero ou mais caracteres (no caso
do asterisco) ou um ou mais dos caracteres (no caso do sinal de mais).

Podemos restringir ainda mais as linhas que combinamos usando um curinga:

# Procure linhas que comecem com From e tenham um sinal de arroba

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

if re.search('^From:.+@', line):

print(line)

# Code: http://www.pythonlearn.com/code3/re04.py

A string de pesquisa “ˆFrom:.+@” corresponderá com sucesso às linhas que começam com
“from:”, seguido de um ou mais caracteres (“.+”), seguido de arroba. Então isso corresponderá
à seguinte linha:

From: uct.ac.za

Você pode pensar no curinga “.+” como uma expansão para corresponder a todos os
caracteres entre o caractere de dois pontos e o sinal de arroba.

From:
É bom pensar nos caracteres de adição e asterisco como “agressivos”. Por exemplo, a
sequência a seguir corresponderia ao último sinal de arroba na sequência conforme o “.+”
empurra para fora, como mostrado abaixo:

From: iupui.edu

É possível dizer a um asterisco ou sinal de mais para não ser tão “ganancioso” adicionando
outro personagem. Consulte a documentação detalhada para obter informações sobre como
desligar o comportamento ganancioso.

11.2 Extraindo dados usando expressões regulares


Se quisermos extrair dados de uma string em Python, podemos usar o método findall () para
extrair todas as substrings que correspondem a uma expressão regular. vamos usar o exemplo
de querer extrair qualquer coisa que se pareça com um endereço de e-mail de qualquer linha
independentemente do formato. Por exemplo, queremos obter os endereços de e-mail de
cada uma das seguintes linhas:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Return-Path: <postmaster@collab.sakaiproject.org>

for <source@collab.sakaiproject.org>;

Received: (from apache@localhost)

Author: stephen.marquard@uct.ac.za

Não queremos escrever código para cada um dos tipos de linhas, divisão e corte diferente para
cada linha. Este programa a seguir usa findall () para encontrar as linhas com endereços de e-
mail neles e extraia um ou mais endereços de cada uma dessas linhas.

import re

s = 'A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM'

lst = re.findall('\S+@\S+', s)

print(lst)

# Code: http://www.pythonlearn.com/code3/re05.py
O método findall() procura a string no segundo argumento e retorna uma lista de todas as
strings que se parecem com endereços de e-mail. Estamos usando um de dois caracteres
sequênciais que corresponde a uma caractere sem espaço em branco (\\S).

A saída do programa seria:

['csev@umich.edu', 'cwen@iupui.edu']

Traduzindo a expressão regular, procuramos substrings que tenham pelo menos um caractere
que não seja um espaço em branco, seguido por um sinal de arroba, seguido por pelo menos
um ou mais caracteres sem espaço em branco. O “\\S+” corresponde a tantos espaços que não
estão em branco possíveis.

A expressão regular corresponderia duas vezes (csev@umich.edu e cwen@iupui.edu), mas não


corresponderia à string “@2PM” porque não há caracteres não em branco antes do sinal de
arroba. Podemos usar essa expressão regular em um programa para ler todas as linhas em um
arquivo e imprimir qualquer coisa que se pareça com um endereço de e-mail como segue:

# Procure por linhas que tenham um sinal de arroba entre os caracteres

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

x = re.findall('\S+@\S+', line)

if len(x) > 0:

print(x)

# Code: http://www.pythonlearn.com/code3/re06.py

Lemos cada linha e extraímos todas as substrings que correspondem a nossa expressão. Como
findall() retorna uma lista, simplesmente verificamos se o número de elementos em nossa lista
retornada é maior que zero para imprimir apenas as linhas onde encontramos pelo menos
uma substring que se pareça com um endereço de e-mail.

Se executarmos o programa em mbox.txt, obteremos a seguinte saída:

['wagnermr@iupui.edu']

['cwen@iupui.edu']

['<postmaster@collab.sakaiproject.org>']
['<200801032122.m03LMFo4005148@nakamura.uits.iupui.edu>']

['<source@collab.sakaiproject.org>;']

['<source@collab.sakaiproject.org>;']

['<source@collab.sakaiproject.org>;']

['apache@localhost)']

['source@collab.sakaiproject.org;']

Alguns de nossos endereços de e-mail possuem caracteres incorretos como “<” ou “;” no
começo ou no fim. Vamos declarar que estamos interessados apenas na parte da string que
começa e termina com uma letra ou um número.

Para fazer isso, usamos outro recurso de expressões regulares. Colchetes são usado para
indicar um conjunto de vários caracteres aceitáveis que estamos dispostos a considerar
coincidindo. De certo modo, o “\\S” está pedindo para corresponder ao conjunto de “não
espaço". Agora seremos um pouco mais explícitos em relação aos personagens que iremos
corresponder.

Aqui está nossa nova expressão regular:

[a-zA-Z0-9]\S*@\S*[a-zA-Z]

Isso está ficando um pouco complicado e você pode começar a ver por que as expressões
regulares são sua própria linguagem para si mesmos. Traduzindo esta expressão regular,
estamos procurando por substrings que começam com uma única letra minúscula, letra
maiúscula ou número “[a-zA-Z0-9]”, seguido de zero ou mais caracteres não em branco
(“\\S*”), seguido por um sinal de arroba, seguido por zero ou mais caracteres não em branco
(“\\S*”), seguido de letra maiúscula ou minúscula. Observe que trocamos de “+” a “*” para
indicar zero ou mais caracteres não em branco desde “[a-zA-Z0-9]” já é um caractere não em
branco. Lembre-se que o “*” ou “+” se aplica ao único caractere imediatamente à esquerda do
sinal de adição ou asterisco.

Se usarmos essa expressão em nosso programa, nossos dados ficam muito mais limpos:

# Procure por linhas que tenham um sinal de arroba entre os caracteres

# Os caracteres devem ser uma letra ou número

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()
x = re.findall('[a-zA-Z0-9]\S+@\S+[a-zA-Z]', line)

if len(x) > 0:

print(x)

# Code: http://www.pythonlearn.com/code3/re07.py

...

['wagnermr@iupui.edu']

['cwen@iupui.edu']

['postmaster@collab.sakaiproject.org']

['200801032122.m03LMFo4005148@nakamura.uits.iupui.edu']

['source@collab.sakaiproject.org']

['source@collab.sakaiproject.org']

['source@collab.sakaiproject.org']

['apache@localhost']

Observe que nas linhas “source@collab.sakaiproject.org”, nossa expressão regular eliminou


duas letras no final da string (“>;”). Isto porque quando nós acrescentar “[a-zA-Z]” ao final de
nossa expressão regular, estamos exigindo que qualquer string que o analisador de expressão
regular encontrar deve terminar com uma letra. Então quando vê o “>” depois de
“sakaiproject.org>;” ele simplesmente para na última “correspondência” letra que encontrou
(ou seja, o “g” foi a última correspondência correta).

Observe também que a saída do programa é uma lista Python que possui uma string como o
único elemento na lista.

11.3 Combinando pesquisa e extração


Se quisermos encontrar números nas linhas que começam com a string “X-”, como:

X-DSPAM-Confidence: 0.8475

X-DSPAM-Probability: 0.0000

Não queremos apenas números de ponto flutuante de qualquer linha. Nós só queremos extrair
números de linhas que possuem a sintaxe acima.

Podemos construir a seguinte expressão regular para selecionar as linhas:


^X-.*: [0-9.]+

Traduzindo isso, estamos dizendo, queremos linhas que comecem com “X-”, seguido de zero
ou mais caracteres (“.*”), seguidos de dois pontos (“:”) e depois um espaço. Depois do espaço
em que procuramos um ou mais caracteres que sejam um dígito (0-9) ou um ponto “[0-9.]+”.
Observe que dentro dos colchetes, o ponto corresponde a um período real (ou seja, não é um
curinga entre colchetes).

Esta é uma expressão muito compacta que corresponderá apenas às linhas que estamos
interessado da seguinte forma:

# Procure por linhas que começam com 'X'

# caracteres de espaço em branco e ':'

# seguido por um espaço e qualquer número.

# O número pode incluir um decimal.

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

if re.search('^X\S*: [0-9.]+', line):

print(line)

# Code: http://www.pythonlearn.com/code3/re10.py

Quando executamos o programa, vemos os dados bem filtrados para mostrar apenas as linhas
que estamos procurando.

X-DSPAM-Confidence: 0.8475

X-DSPAM-Probability: 0.0000

X-DSPAM-Confidence: 0.6178

X-DSPAM-Probability: 0.0000

Mas agora temos que resolver o problema de extrair os números. Enquanto isso ser simples o
suficiente para usar split, podemos usar outro recurso de expressões regulares para pesquisar
e analisar a linha ao mesmo tempo.
Parênteses são outro caractere especial em expressões regulares. Quando você adiciona
parênteses a uma expressão regular, eles são ignorados ao corresponder à string. Mas quando
você está usando findall (), os parênteses indicam que, embora você queira a expressão inteira
para corresponder, você só está interessado em extrair uma parte da substring que
corresponde à expressão regular.

Então, fazemos a seguinte alteração em nosso programa:

# Procure por linhas que começam com 'X' seguido por qualquer

# caracteres sem espaço em branco e ':' seguido por um espaço

# e qualquer número. O número pode incluir um decimal.

# Em seguida, imprima o número se for maior que zero.

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

x = re.findall('^X\S*: ([0-9.]+)', line)

if len(x) > 0:

print(x)

# Code: http://www.pythonlearn.com/code3/re11.py

Em vez de chamar search (), adicionamos parênteses em torno da parte da expressão regular
que representa o número de ponto flutuante para indicar que queremos apenas o findall ()
para nos devolver a parte do número de ponto flutuante da corda correspondente.

A saída deste programa é a seguinte:

['0.8475']

['0.0000']

['0.6178']

['0.0000']

['0.6961']

['0.0000']

..
Os números ainda estão em uma lista e precisam ser convertidos de strings para pontos
flutuantes, vamos usamos o poder das expressões regulares para pesquisar e extrair as
informações que achamos interessantes.

Como no outro exemplo dessa técnica, se você olhar para o arquivo, verá várias linhas do
formulário:

Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772

Se quiséssemos extrair todos os números de revisão (o número inteiro no final dessas linhas)
usando a mesma técnica acima, poderíamos escrever o seguinte programa:

# Procure por linhas que começam com 'Detalhes: rev='

# seguido de números e '.'

# Em seguida, imprima o número se for maior que zero

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

x = re.findall('^Details:.*rev=([0-9.]+)', line)

if len(x) > 0:

print(x)

# Code: http://www.pythonlearn.com/code3/re12.py

Traduzindo nossa expressão regular, estamos procurando por linhas que começam com “De-
tails:”, seguido por qualquer número de caracteres (“.*”), seguido por “rev=” e então por um
ou mais dígitos. Queremos encontrar linhas que correspondam à expressão inteira, mas
queremos apenas extrair o número inteiro no final da linha, então cercamos “[0-9]+” com
parênteses.

Quando executamos o programa, obtemos a seguinte saída:

['39772']

['39771']

['39770']

['39769']
...

Lembre-se que o “[0-9]+” é “ganancioso” e tenta fazer uma string tão grande quanto dígitos
possível antes de extrair esses dígitos. Esse comportamento “ganancioso” é o motivo pelo qual
obter todos os cinco dígitos para cada número. A biblioteca de expressões regulares se
expande em ambas as direções até encontrar um não-dígito, ou o início ou o fim de uma linha.

Agora podemos usar expressões regulares para refazer um exercício anterior no e-book onde
estávamos interessados na hora do dia de cada mensagem de correio. Nós procuramos por
linhas do formulário:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Vamos extrair a hora do dia para cada linha. Anteriormente nós fizemos isso com duas
chamadas para dividir. Primeiro a linha foi dividida em palavras e depois puxamos a quinta
palavra e divida-a novamente no caractere de dois pontos para retirar os dois personagens nos
quais estávamos interessados.

Embora isso tenha funcionado, na verdade resulta em um código bastante frágil que assume
as linhas bem formatadas. Se você adicionar verificação de erro suficiente (ou um grande
pente/exceto bloco) para garantir que seu programa nunca falhe quando apresentado com
linhas formatadas incorretamente, o código aumentaria para 10 a 15 linhas de código que
foram bem difícil de ler.

Podemos fazer isso de uma maneira muito mais simples com a seguinte expressão regular:

^From .* [0-9][0-9]:

A tradução dessa expressão regular é que estamos procurando por linhas que começam com
“from” (observe o espaço), seguido de qualquer número de caracteres (“.*”), seguido por um
espaço, seguido de dois dígitos “[0-9][0-9]”, seguido de dois pontos. Esse é a definição dos
tipos de linhas que estamos procurando.

Para extrair apenas a hora usando findall(), adicionamos parênteses ao redor dos dois dígitos
da seguinte forma:

^From .* ([0-9][0-9]):

Isso resulta no seguinte programa:


# Procure por linhas que começam com From e um caractere

# seguido de um número de dois dígitos entre 00 e 99 seguido de ':'

# Em seguida, imprima o número se for maior que zero

import re

hand = open('mbox-short.txt')

for line in hand:

line = line.rstrip()

x = re.findall('^From .* ([0-9][0-9]):', line)

if len(x) > 0: print(x)

# Code: http://www.pythonlearn.com/code3/re13.py

Quando o programa é executado, ele produz a seguinte saída:

['09']

['18']

['16']

['15']

...

11.4 Personagem de fuga


Como usamos caracteres especiais em expressões regulares para corresponder ao início ou
final de uma linha ou especificar curingas, precisamos de uma maneira de indicar que esses
caracteres são "normais" e queremos corresponder ao caractere real, como um cifrão ou
acento circunflexo.

Podemos indicar que queremos simplesmente corresponder a um caractere prefixando esse


caractere com uma barra invertida. Por exemplo, podemos encontrar quantias de dinheiro
com o seguinte expressão regular.

import re

x = 'We just received $10.00 for cookies.'

y = re.findall('\$[0-9.]+',x)
Como prefixamos o cifrão com uma barra invertida, ele realmente corresponde ao dólar a
string de entrada em vez de corresponder ao “fim da linha” e o restante da expressão regular
corresponde a um ou mais dígitos ou ao caractere de ponto. Observação: Dentro de colchetes,
os caracteres não são “especiais”. Então, quando dizemos “[0-9.]”, realmente significa dígitos
ou um ponto. Fora dos colchetes, um ponto é o “wild-card” e corresponde a qualquer
caractere. Dentro de colchetes, o período é um periodo.

11.5 Resumo
Embora isso apenas tenha arranhado a superfície das expressões regulares, aprendemos um
pouco sobre a linguagem de expressões regulares. Eles são strings de pesquisa com caracteres
especiais que comunicam seus desejos ao sistema de expressão regular quanto ao que define
“correspondência” e o que é extraído das strings correspondentes.

Aqui estão alguns desses caracteres especiais e sequências de caracteres:

ˆ Corresponde ao início da linha.

$ Corresponde ao final da linha.

. Corresponde a qualquer caractere (um curinga).

\\s Corresponde a um caractere de espaço em branco.

\\S Corresponde a um caractere sem espaço em branco (oposto de \\s).

* Aplica-se ao caractere imediatamente anterior e indica que corresponde a zero ou mais do(s)
caractere(s) anterior(es).

*? Aplica-se ao caractere imediatamente anterior e indica que corresponde a zero ou mais


do(s) caractere(s) anterior(es) no “modo não ganancioso”.

• Aplica-se ao caractere imediatamente anterior e indica que corresponde a um ou mais do(s)


caracter(es) anterior(es).

+? Aplica-se ao caractere imediatamente anterior e indica que corresponde a um ou mais do(s)


caractere(s) anterior(es) no “modo não ganancioso”.

[aeiou] Corresponde a um único caractere desde que esse caractere esteja no conjunto
especificado. Neste exemplo, corresponderia a “a”, “e”, “i”, “o” ou “u”, mas nenhum outro
caractere.

[a-z0-9] Você pode especificar intervalos de caracteres usando o sinal de menos. Este exemplo
é um único caractere que deve ser uma letra minúscula ou um dígito.

[ˆA-Za-z] Quando o primeiro caractere na notação do conjunto é um circunflexo, ele inverte a


lógica. Este exemplo corresponde a um único caractere que não seja maiúsculo ou letra
minúscula.

( ) Quando parênteses são adicionados a uma expressão regular, eles são ignorados para o
propósito de correspondência, mas permitem que você extraia um subconjunto específico do
correspondente string em vez de toda a string ao usar findall ().
\\b Corresponde à string vazia, mas apenas no início ou no final de uma palavra.

\\B Corresponde à string vazia, mas não no início ou no final de uma palavra.

\\d Corresponde a qualquer dígito decimal; equivalente ao conjunto [0-9].

\\D Corresponde a qualquer caractere não numérico; equivalente ao conjunto [ˆ0-9].

11.6 Seção de bônus para usuários Unix/Linux


O suporte para pesquisar arquivos usando expressões regulares foi incorporado à operação
Unix. Sistema de programação desde a década de 1960 e está disponível em quase todas as
linguagens de programação de uma forma ou de outra.

Na verdade, existe um programa de linha de comando embutido no Unix chamado grep


(Generalized Regular Expression Parser) que faz praticamente o mesmo que o search()
exemplos neste capítulo. Portanto, se você tiver um sistema Macintosh ou Linux, você pode
tentar os seguintes comandos em sua janela de linha de comando.

$ grep '^From:' mbox-short.txt

From: stephen.marquard@uct.ac.za

From: louis@media.berkeley.edu

From: zqian@umich.edu

From: rjlowe@iupui.edu

Isso diz ao grep para mostrar as linhas que começam com a string “From:” no arquivo mbox-
short.txt. Se você experimentar um pouco o comando grep e ler a documentação para grep,
você encontrará algumas diferenças sutis entre o suporte regular e a expressões em Python e
suporte a expressões regulares em grep. como um exemplo, grep não suporta o caractere não
em branco “\\S” então você precisará para usar a notação de conjunto um pouco mais
complexa “[ˆ]”, que significa simplesmente combinar um caractere que não seja um espaço.

11.7 Depuração
Python tem alguma documentação interna simples e rudimentar que pode ser bastante útil se
você precisar de uma atualização rápida para acionar sua memória sobre o nome exato de um
determinado método. Esta documentação pode ser visualizada no interpretador Python no
modo interativo.

Você pode abrir um sistema de ajuda interativo usando help().


>>> help()

help> modules

Se você sabe qual módulo deseja usar, pode usar o comando dir() para encontrar os métodos
no módulo da seguinte maneira:

>>> import re

>>> dir(re)

[.. 'compile', 'copy_reg', 'error', 'escape', 'findall',

'finditer' , 'match', 'purge', 'search', 'split', 'sre_compile',

'sre_parse' , 'sub', 'subn', 'sys', 'template']

Você também pode obter uma pequena quantidade de documentação sobre um método
específico usando o comando dir.

>>> help (re.search)

Help on function search in module re:

search(pattern, string, flags=0)

Scan through string looking for a match to the pattern, returning

a match object, or None if no match was found.

>>>

A documentação integrada não é muito extensa, mas pode ser útil quando você está com
pressa ou não tem acesso a um navegador ou mecanismo de pesquisa.

11.8 Glossário
Código frágil - Código que funciona quando os dados de entrada estão em um formato
específico, mas é propenso a quebrar se houver algum desvio do formato correto. Nós
chamamos isso de “código frágil” porque é facilmente quebrado.

Correspondência gananciosa - A noção de que os caracteres “+” e “*” em uma expressão


regular a pressão se expande para fora para corresponder à maior string possível.

Grep - Um comando disponível na maioria dos sistemas Unix que pesquisa arquivos de texto
procurando linhas que correspondam a expressões regulares. O nome do comando fica para
“analisador generalizado de expressões regulares”.
Expressão regular - Uma linguagem para expressar cadeias de pesquisa mais complexas. A
expressão regular pode conter caracteres especiais que indicam que uma pesquisa
corresponde apenas no início ou no final de uma linha ou muitos outros recursos e habilidades
semelhantes.

Curinga - Um caractere especial que corresponde a qualquer caractere. Em expressões


regulares, o caractere curinga é o ponto.

11.9 Exercícios
Exercício 1: Escreva um programa simples para simular a operação do comando grep, mando
no Unix. Peça ao usuário para inserir uma expressão regular e contar o número de linhas que
correspondem à expressão regular:

$ python grep.py

Insira uma expressão regular: ^Autor

mbox.txt tinha 1798 linhas que correspondiam a ^Autor

$ python grep.py

Insira uma expressão regular: ^X-

mbox.txt tinha 14368 linhas que correspondiam a ^X-

$ python grep.py

Insira uma expressão regular: java$

mbox.txt tinha 4218 linhas que correspondiam a java$

Exercício 2: Escreva um programa para procurar linhas da forma:

`New Revision: 39772`

E extraia o número de cada uma das linhas usando uma expressão regular e o método findall().
Calcule a média dos números e imprima a média.

Enter file:mbox.txt

38549.7949721
Enter file:mbox-short.txt

39756.9259259
Capítulo 12
Programas em rede
Embora muitos dos exemplos neste livro se concentrem na leitura de arquivos e na procura
para dados nesses arquivos, existem muitas fontes diferentes de informação quando se
considera a Internet.

Neste capítulo, vamos fingir ser um navegador da Web e recuperar páginas da Web usando
protocolo de transporte de hipertexto (HTTP). Então vamos ler através da web dados da
página e analisá-los.

12.1 Protocolo de Transporte de Hipertexto - HTTP


O protocolo de rede que alimenta a web é, na verdade, bastante simples e possui um suporte
em Python chamado sockets, o que torna muito fácil tornar a conexões de rede e recuperar
dados sobre esses soquetes em um programa Python.

Um soquete é muito parecido com um arquivo, exceto que um único soquete fornece uma
conexão bidirecional entre dois programas. Você pode ler e gravar no mesmo soquete. Se você
escrever algo em um soquete, ele será enviado para o aplicativo na outra extremidade da
tomada. Se você ler o soquete, receberá os dados que o outro aplicativo enviou.

Mas se você tentar ler um soquete quando o programa na outra extremidade do soquete não
enviou nenhum dado, você apenas senta e espera. Se os programas em ambas as
extremidades do soquete simplesmente espere por alguns dados sem enviar nada, eles vão
esperar por muitoooo tempo.

Portanto, uma parte importante dos programas que se comunicam pela Internet é ter algum
tipo de protocolo. Um protocolo é um conjunto de regras precisas que determinam quem é de
ir primeiro, o que eles devem fazer e, em seguida, quais são as respostas a essa mensagem, e
quem envia a seguir, e assim por diante. De certa forma, os dois aplicativos em cada
extremidade do soquete está dançando e tomando cuidado para não pisar no pé um do outro.

Existem muitos documentos que descrevem esses protocolos de rede. O protocolo de


transporte de texto é descrito no seguinte documento:

http://www.w3.org/Protocols/rfc2616/rfc2616.txt

Este é um documento longo e complexo de 176 páginas com muitos detalhes. Se você achar
interessante, sinta-se livre para ler tudo. Mas se você der uma olhada na página 36 do
RFC2616, você encontrará a sintaxe para a solicitação GET. Para solicitar um documento de um
servidor web, fazemos uma conexão com o servidor www.pythonlearn.com em porta 80 e, em
seguida, envie uma linha do formulário
GET http://data.pr4e.org/romeo.txt HTTP/1.0

Onde o segundo parâmetro é a página da web que estamos solicitando e também envie uma
linha em branco. O servidor web responderá com algumas informações de cabeçalho sobre o
documento e uma linha em branco seguida do conteúdo do documento.

12.2 O navegador mais simples do mundo


Talvez a maneira mais fácil de mostrar como funciona o protocolo HTTP seja escrever um
programa Python simples que faz uma conexão com um servidor web e segue as regras do
protocolo HTTP para solicitar um documento e exibir o que o servidor manda de volta.

import socket

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

mysock.connect(('data.pr4e.org', 80))

cmd = 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\n\n'.encode()

mysock.send(cmd)

while True:

data = mysock.recv(512)

if (len(data) < 1):

break

print(data.decode())

mysock.close()

# Code: http://www.pythonlearn.com/code3/socket1.py

Primeiro o programa faz uma conexão com a porta 80 no servidor www.pythonlearn.com.


Como nosso programa está desempenhando o papel de “navegador da web”, o protocolo
HTTP diz que devemos enviar o comando GET seguido de uma linha em branco.

Depois de enviarmos essa linha em branco, escrevemos um loop que recebe dados em 512
caracteres pedaços do soquete e imprime os dados até que não haja mais dados para ler (ou
seja, o recv() retorna uma string vazia).

O programa produz a seguinte saída:

HTTP/1.1 200 OK

Date: Sun, 14 Mar 2010 23:52:41 GMT


Server: Apache

Last-Modified: Tue, 29 Dec 2009 01:31:22 GMT

ETag: "143c1b33-a7-4b395bea"

Accept-Ranges: bytes

Content-Length: 167

Connection: close

Content-Type: text/plain

Mas suave que luz através daquela janela quebra

É o leste e Julieta é o sol

Levante-se belo sol e mate a lua invejosa

Quem já está doente e pálido de dor

A saída começa com cabeçalhos que o servidor web envia para descrever o documento. Por
exemplo, o cabeçalho Content-Type indica que o documento é um documento de texto
simples (texto/simples).

Depois que o servidor nos envia os cabeçalhos, ele adiciona uma linha em branco para indicar
o final dos cabeçalhos e, em seguida, envia os dados reais do arquivo romeo.txt.

Este exemplo mostra como fazer uma conexão de rede de baixo nível com soquetes. Sockets
podem ser usados para se comunicar com um servidor web ou com um servidor de correio ou
muitos outros tipos de servidores. Basta encontrar o documento que descreve o protocolo e
escreve o código para enviar e receber os dados de acordo com o protocolo.

No entanto, como o protocolo que usamos com mais frequência é o protocolo da Web HTTP,
Python tem uma biblioteca especial projetada especificamente para suportar o protocolo HTTP
para a recuperação de documentos e dados na web.

12.3 Recuperando uma imagem por HTTP


No exemplo acima, recuperamos um arquivo de texto simples que tinha novas linhas no
arquivo e simplesmente copiamos os dados para a tela enquanto o programa rodava. Podemos
usar um programa semelhante para recuperar uma imagem usando HTTP. Em vez de copiar os
dados para a tela enquanto o programa é executado, acumulamos os dados em uma string,
aparamos fora dos cabeçalhos e, em seguida, salve os dados da imagem em um arquivo da
seguinte maneira:
import socket

import time

HOST = 'data.pr4e.org'

PORT = 80

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

mysock.connect((HOST, PORT))

mysock.sendall(b'GET http://data.pr4e.org/cover.jpg HTTP/1.0\n\n')

count = 0

picture = b""

while True:

data = mysock.recv(5120)

if (len(data) < 1): break

time.sleep(0.25)

count = count + len(data)

print(len(data), count)

picture = picture + data

mysock.close()

# Procure o final do cabeçalho (2 CRLF)

pos = picture.find(b"\r\n\r\n")

print('Header length', pos)

print(picture[:pos].decode())

# Pule o cabeçalho e salve os dados da imagem

picture = picture[pos+4:]

fhand = open("stuff.jpg", "wb")

fhand.write(picture)

fhand.close()

# Code: http://www.pythonlearn.com/code3/urljpeg.py

Quando o programa é executado, ele produz a seguinte saída:


$ python urljpeg.py

2920 2920

1460 4380

1460 5840

1460 7300

...

1460 62780

1460 64240

2920 67160

1460 68620

1681 70301

Header length 240

HTTP/1.1 200 OK

Date: Sat, 02 Nov 2013 02:15:07 GMT

Server: Apache

Last-Modified: Sat, 02 Nov 2013 02:01:26 GMT

ETag: "19c141-111a9-4ea280f8354b8"

Accept-Ranges: bytes

Content-Length: 70057

Connection: close

Content-Type: image/jpeg

Você pode ver que, para esta URL, o cabeçalho Content-Type indica o corpo do documento
que é uma imagem (image/jpeg). Assim que o programa for concluído, você poderá visualizar
os dados da imagem abrindo o arquivo stuff.jpg em um visualizador de imagens.

À medida que o programa é executado, você pode ver que não obtemos 5120 caracteres toda
vez, chamamos o método recv(). Recebemos tantos personagens quanto foram transferidos
através da rede para nós pelo servidor web no momento em que chamamos recv(). Nisso, Por
exemplo, obtemos 1460 ou 2920 caracteres cada vez que solicitamos até 5120 caracteres de
dados.

Seus resultados podem ser diferentes dependendo da velocidade da sua rede. Observe
também que ema última chamada para recv() obtemos 1681 bytes, que é o fim do fluxo, e na
próxima chamada para recv(), obtemos uma string de comprimento zero que nos diz que o
servidor chamado close () em sua extremidade do soquete e não há mais dados futuros.
Podemos desacelerar nossas chamadas recv() sucessivas descomentando a chamada para hora
de dormir(). Desta forma, esperamos um quarto de segundo após cada chamada para que o
servidor possa “ficar à frente” de nós e enviar mais dados para nós antes de chamarmos recv()
de novo. Com o delay, no lugar o programa executa da seguinte forma:

$ python urljpeg.py

1460 1460

5120 6580

5120 11700

...

5120 62900

5120 68020

2281 70301

Header length 240

HTTP/1.1 200 OK

Date: Sat, 02 Nov 2013 02:22:04 GMT

Server: Apache

Last-Modified: Sat, 02 Nov 2013 02:01:26 GMT

ETag: "19c141-111a9-4ea280f8354b8"

Accept-Ranges: bytes

Content-Length: 70057

Connection: close

Content-Type: image/jpeg

Agora, além da primeira e da última chamada para recv (), agora temos 5120 caracteres cada
vez que pedimos novos dados.

Existe um buffer entre o servidor que faz as solicitações send() e nosso aplicativo, fazendo
requisições recv(). Quando executamos o programa com o atraso no lugar, em algum
momento, o servidor pode preencher o buffer no soquete e ser forçado a pausar até que
nosso programa comece a esvaziar o buffer. A pausa do envio do aplicativo ou o aplicativo
receptor é chamado de “controle de fluxo”.
12.4 Recuperando páginas da web com urllib
Embora possamos enviar e receber dados manualmente por HTTP usando a biblioteca de
soquetes, existe uma maneira muito mais simples de executar essa tarefa comum em Python
usando o método biblioteca urllib.

Usando urllib, você pode tratar uma página da web como um arquivo. Você simplesmente
indica qual página da web você gostaria de recuperar e urllib lida com todo o HTTP protocolo e
detalhes do cabeçalho.

O código equivalente para ler o arquivo romeo.txt da web usando urllib é como segue:

import urllib.request, urllib.parse, urllib.error

fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')

for line in fhand:

print(line.decode().strip())

# Code: http://www.pythonlearn.com/code3/urllib1.py

Depois que a página da Web for aberta com urllib.urlopen, podemos tratá-la como um arquivo
e leia-o usando um loop for.

Quando o programa é executado, vemos apenas a saída do conteúdo do arquivo. Os


cabeçalhos ainda são enviados, mas o código urllib consome os cabeçalhos e retorna apenas os
dados para nós.

But soft what light through yonder window breaks

It is the east and Juliet is the sun

Arise fair sun and kill the envious moon

Who is already sick and pale with grief

Como exemplo, podemos escrever um programa para recuperar os dados de romeo.txt e


calcular a frequência de cada palavra no arquivo da seguinte maneira:

import urllib.request, urllib.parse, urllib.error

counts = dict()

fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')

for line in fhand:

words = line.decode().split()
for word in words:

counts[word] = counts.get(word, 0) + 1

print(counts)

# Code: http://www.pythonlearn.com/code3/urlwords.py

Novamente, depois de abrir a página da Web, podemos lê-la como um arquivo local.

12.5 Analisando HTML e raspando a web


Um dos usos comuns do recurso urllib em Python é raspar a web. A raspagem da Web é
quando escrevemos um programa que finge ser um navegador da Web e recupera páginas e
examina os dados nessas páginas em busca de padrões.

Por exemplo, um mecanismo de pesquisa como o Google procurará a origem de um site


página e extraia os links para outras páginas e recupere essas páginas, extraindo links, e assim
por diante. Usando essa técnica, o Google vasculha quase todos os páginas na web.

O Google também usa a frequência de links das páginas que encontra para uma página
específica como uma medida de quão “importante” é uma página e quão alta a página deve
aparecer em seus resultados de pesquisa.

12.6 Analisando HTML usando expressões regulares


Uma maneira simples de analisar HTML é usar expressões regulares para pesquisar
repetidamente e extrair substrings que correspondem a um determinado padrão.

Aqui está uma página web simples:

<h1>The First Page</h1>

<p>

If you like, you can switch to the

<a href="http://www.dr-chuck.com/page2.htm">

Second Page</a>.

</p>

Podemos construir uma expressão regular bem formada para corresponder e extrair o link e
valores do texto acima da seguinte forma:
href=http://.+?

Nossa expressão regular procura strings que começam com “href=”http://“, seguidas por um
ou mais caracteres (”.+¿'), seguido de outra aspa dupla. A questão marcada adicionada com
".+?" indica que a correspondência deve ser feita de forma "não gananciosa" moderada em vez
de um modo “ganancioso”. Uma correspondência não gulosa tenta encontrar a menor
sequência de correspondência possível e uma correspondência gulosa tenta encontrar a maior
cadeia correspondente possível.

Adicionamos parênteses à nossa expressão regular para indicar qual parte de nossa
correspondência string que gostaríamos de extrair e produzir o seguinte programa:

# Procure linhas que comecem com From e tenham um sinal de arroba

import urllib.request, urllib.parse, urllib.error

import re

url = input('Enter - ')

html = urllib.request.urlopen(url).read()

links = re.findall(b'href="(http://.*?)"', html)

for link in links:

print(link.decode())

# Code: http://www.pythonlearn.com/code3/urlregex.py

O método de expressão regular findall nos dará uma lista de todas as strings que
correspondem à nossa expressão regular, retornando apenas o texto do link entre a dupla
citações.

Quando executamos o programa, obtemos a seguinte saída:

python urlregex.py

Enter - http://www.dr-chuck.com/page1.htm

http://www.dr-chuck.com/page2.htm

python urlregex.py

Enter - http://www.pythonlearn.com/book.htm

http://www.greenteapress.com/thinkpython/thinkpython.html

http://allendowney.com/

http://www.pythonlearn.com/code
http://www.lib.umich.edu/espresso-book-machine

http://www.pythonlearn.com/py4inf-slides.zip

Expressões regulares funcionam muito bem quando seu HTML está bem formatado e
previsível. Mas como existem muitas páginas HTML "quebradas" por aí, uma solução é usar
apenas expressões regulares que pode perder alguns links válidos ou acabar com dados ruins.

Isso pode ser resolvido usando uma biblioteca de análise de HTML robusta.

12.7 Analisando HTML usando BeautifulSoup


Existem várias bibliotecas Python que podem ajudá-lo a analisar HTML e extrair dados das
páginas. Cada uma das bibliotecas tem seus pontos fortes e fracos e você pode escolher uma
com base em suas necessidades.

Como exemplo, vamos simplesmente analisar algumas entradas HTML e extrair links usando a
biblioteca BeautifulSoup. Você pode baixar e instalar o código BeautifulSoup em:

http://www.crummy.com/software/

Você pode baixar e “instalar” o BeautifulSoup ou simplesmente colocar o BeautifulSoup.py na


mesma pasta do seu aplicativo.

Mesmo que o HTML se pareça com o XML e algumas páginas são cuidadosamente construídas
para ser XML, a maioria do HTML geralmente é quebrada de maneiras que causam um
analisador de XML rejeitar toda a página de HTML como formada incorretamente.
BeautifulSoup tolera HTML altamente falho e ainda permite que você extraia facilmente os
dados de que precisa.

Usaremos o urllib para ler a página e depois usaremos o BeautifulSoup para extrair os
atributos href das tags âncoras.

# Para executar isso, você pode instalar o BeautifulSoup

# https://pypi.python.org/pypi/beautifulsoup4

# Ou baixe o arquivo

# http://www.pythonlearn.com/code3/bs4.zip

# e descompacte-o no mesmo diretório deste arquivo

import urllib.request, urllib.parse, urllib.error

from bs4 import BeautifulSoup

url = input('Enter - ')


html = urllib.request.urlopen(url).read()

soup = BeautifulSoup(html, 'html.parser')

# Retrieve all of the anchor tags

tags = soup('a')

for tag in tags:

print(tag.get('href', None))

# Code: http://www.pythonlearn.com/code3/urllinks.py

O programa solicita um endereço da web, abre a página da web, lê os dados e passa os dados
para o analisador BeautifulSoup e, em seguida, recupera todas as tags âncora e imprime o
atributo href para cada tag.

Quando o programa é executado, ele se parece com o seguinte:

python urllinks.py

Enter - http://www.dr-chuck.com/page1.htm

http://www.dr-chuck.com/page2.htm

python urllinks.py

Enter - http://www.pythonlearn.com/book.htm

http://www.greenteapress.com/thinkpython/thinkpython.html

http://allendowney.com/

http://www.si502.com/

http://www.lib.umich.edu/espresso-book-machine

http://www.pythonlearn.com/code

http://www.pythonlearn.com/

Você pode usar o BeautifulSoup para extrair várias partes de cada tag da seguinte maneira:

# Para executar isso, você pode instalar o BeautifulSoup

# https://pypi.python.org/pypi/beautifulsoup4

# Ou baixe o arquivo

# http://www.pythonlearn.com/code3/bs4.zip

# e descompacte-o no mesmo diretório deste arquivo


from urllib.request import urlopen

from bs4 import BeautifulSoup

url = input('Enter - ')

html = urlopen(url).read()

# html.parser é o analisador HTML incluído na biblioteca Python 3 padrão.

# informações sobre outros analisadores de HTML estão aqui:

# http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser

soup = BeautifulSoup(html, "html.parser")

# Recupera todas as tags âncora

tags = soup('a')

for tag in tags:

# Veja as partes de uma tag

print('TAG:', tag)

print('URL:', tag.get('href', None))

print('Contents:', tag.contents[0])

print('Attrs:', tag.attrs)

# Code: http://www.pythonlearn.com/code3/urllink2.py

python urllink2.py

Enter - http://www.dr-chuck.com/page1.htm

TAG: <a href="http://www.dr-chuck.com/page2.htm">

Second Page</a>

URL: http://www.dr-chuck.com/page2.htm

Content: ['\nSecond Page']

Attrs: [('href', 'http://www.dr-chuck.com/page2.htm')]

Esses exemplos apenas começam a mostrar o poder do BeautifulSoup quando se trata de


analise de HTML.
12.8 Lendo arquivos binários usando urllib
Às vezes, você deseja recuperar um arquivo não textual (ou binário), como uma imagem ou
arquivo de vídeo. Os dados nesses arquivos geralmente não são úteis para impressão, mas
você pode facilmente fazer uma cópia de uma URL para um arquivo local em seu disco rígido
usando urllib.

O padrão é abrir a URL e usar read para baixar todo o conteúdo do documento em uma
variável de string (img) e, em seguida, escreva essa informação em um arquivo local da
seguinte forma:

import urllib.request, urllib.parse, urllib.error

img = urllib.request.urlopen('http://data.pr4e.org/cover.jpg').read()

fhand = open('cover.jpg', 'wb')

fhand.write(img)

fhand.close()

# Code: http://www.pythonlearn.com/code3/curl1.py

12.9. GLOSSÁRIO
Este programa lê todos os dados de uma só vez na rede e os armazena na variável img na
memória principal do seu computador, em seguida abre o arquivo cover.jpg e grava os dados
em seu disco. Isso funcionará se o tamanho do arquivo for menor do que o tamanho da
memória do seu computador.

No entanto, se este for um arquivo de áudio ou vídeo grande, este programa pode travar ou
pelo menos executado extremamente lentamente quando o computador fica sem memória. A
fim de evitar ficar sem memória, recuperamos os dados em blocos (ou buffers) e então
escrevemos cada bloco em seu disco antes de recuperar o próximo bloco. Desta forma o
programa pode ler qualquer arquivo de tamanho sem usar toda a memória que você tem em
seu computador.

import urllib.request, urllib.parse, urllib.error

img = urllib.request.urlopen('http://data.pr4e.org/cover.jpg')

fhand = open('cover.jpg', 'wb')

size = 0

while True:

info = img.read(100000)

if len(info) < 1: break

size = size + len(info)


fhand.write(info)

print(size, 'characters copied.')

fhand.close()

# Code: http://www.pythonlearn.com/code3/curl2.py

Neste exemplo, lemos apenas 100.000 caracteres por vez e escrevemos os caracteres para o
arquivo cover.jpg antes de recuperar os próximos 100.000 caracteres de dados da web.

Este programa funciona da seguinte forma:

python curl2.py

568248 characters copied.

Se você possui um computador Unix ou Macintosh, provavelmente possui um comando criado


no seu sistema operacional que executa esta operação da seguinte forma:

curl -O http://www.pythonlearn.com/cover.jpg

O comando curl é a abreviação de “copiar URL” e, portanto, esses dois exemplos são
habilmente chamados curl1.py e curl2.py em www.pythonlearn.com/code à medida que
implementam funcionalidade semelhante ao comando curl. Há também uma amostra do
programa curl3.py que faz essa tarefa com um pouco mais de eficiência, caso você realmente
queira, use esse padrão em um programa que você está escrevendo.

12.9 Glossário
BeautifulSoup - Uma biblioteca Python para analisar documentos HTML e extrair dados de
documentos HTML que compensam a maioria das imperfeições no HTML que os navegadores
geralmente ignoram. Você pode baixar o código Beautifulsoup em www.crummy.com.

Porta - Um número que geralmente indica qual aplicativo você está contatando quando você
faz uma conexão de soquete com um servidor. Por exemplo, o tráfego da web geralmente usa
a porta 80 enquanto o tráfego de e-mail usa a porta 25.

Scrape - Quando um programa finge ser um navegador da web e recupera uma página da web,
em seguida, examina o conteúdo da página da web. Frequentemente, os programas seguem
os links em uma página para encontrar a próxima página para que possam percorrer uma rede
de páginas ou uma rede social.

Socket - Uma conexão de rede entre dois aplicativos onde os aplicativos podem enviar e
receber dados em qualquer direção.
Spider - O ato de um mecanismo de pesquisa da Web, recuperar uma página e, em seguida,
todas as páginas vinculados de uma página e assim por diante até que tenham quase todas as
páginas da Internet que eles usam para construir seu índice de pesquisa.

12.10 Exercícios
Exercício 1: Altere o programa socket socket1.py para solicitar ao usuário a URL para que ele
possa ler qualquer página da web. Você pode usar split('/') para dividir a URL em duas partes
componentes para que você possa extrair o nome do host para a chamada de conexão de
soquete. Adicione a verificação de erros usando try e except para lidar com a condição em que
o usuário insere uma URL formatando incorretamente ou inexistente.

Exercício 2: Altere seu programa de soquete para que ele conte o número de caracteres dados
que recebeu e para de exibir qualquer texto depois de mostrar 3.000 caracteres. O programa
deve recuperar o documento inteiro e contar o número total de caracteres e exibir a contagem
do número de caracteres no final do documento.

Exercício 3: Use urllib para replicar o exercício anterior de (1) recuperar o documento de uma
URL, (2) exibindo até 3.000 caracteres e (3) contando o número total de caracteres no
documento. Não se preocupe com os cabeçalhos, Neste exercício, basta mostrar os primeiros
3.000 caracteres do conteúdo do documento.

Exercício 4: Altere o programa urllinks.py para extrair e contar o parágrafo (p) tags do
documento HTML recuperado e exibir a contagem dos parágrafos como a saída do seu
programa. Não exiba o texto do parágrafo, apenas conte eles. Teste seu programa em várias
páginas da Web pequenas, bem como em algumas páginas da Web Páginas maiores.

Exercício 5: (Avançado) Altere o programa socket para que mostre apenas dados depois que os
cabeçalhos e uma linha em branco forem recebidos. Lembre-se que recv é recebendo
caracteres (novas linhas e tudo), não linhas.
Capítulo 13
Usando serviços da Web
Depois que ficou fácil recuperar documentos e analisar documentos por HTTP usando
programas, não demorou muito para desenvolver uma abordagem onde começaram a
produzir documentos que foram especificamente concebidos para serem consumidos por
outros programas (ou seja, não HTML para ser exibido em um navegador).

Existem dois formatos comuns que usamos ao trocar dados pela web. A “eXtensible Markup
Language” ou XML que está em uso há muito tempo e é mais adequado para troca de dados
em estilo de documento. Quando os programas só querem trocar dicionários, listas ou outras
informações internas entre si, eles usam JavaScript Object Notation ou JSON (consulte
www.json.org). Vamos olhar para os dois formatos.

13.1 Linguagem de Marcação Extensível - XML


O XML é muito semelhante ao HTML, mas o XML é mais estruturado do que o HTML. Aqui é
uma amostra de um documento XML:

<person>

<name>Chuck</name>

<phone type="intl">

+1 734 303 4456

</phone>

<email hide="yes"/>

</person>

Muitas vezes é útil pensar em um documento XML como uma estrutura de árvore onde é uma
pessoa de marca principal e outras marcas, como telefone, são desenhadas como filhos de
seus pais.
13.2 Analisando XML
Aqui está um aplicativo simples que analisa algum XML e extrai alguns elementos de dados do
XML:

import xml.etree.ElementTree as ET

data = '''

<person>

<name>Chuck</name>

<phone type="intl">

+1 734 303 4456

</phone>

<email hide="yes"/>

</person>'''

tree = ET.fromstring(data)

print('Name:', tree.find('name').text)

print('Attr:', tree.find('email').get('hide'))

# Code: http://www.pythonlearn.com/code3/xml1.py

Chamar fromstring converte a representação de string do XML em uma “árvore” de XML.


Quando o XML está em uma árvore, temos uma série de métodos que podemos chamar para
extrair partes de dados do XML.

A função find pesquisa na árvore XML e recupera um nó que corresponde à tag especificada.
Cada nó pode ter algum texto, alguns atributos (como hide) e alguns nós “filhos”. Cada nó
pode ser o topo de uma árvore de nós.

Name: Chuck

Attr: yes

O uso de um analisador de XML como o ElementTree tem a vantagem de que, embora o XML
neste exemplo é bastante simples, verifica-se que existem muitas regras a respeito XML válido
e usar ElementTree nos permite extrair dados de XML sem preocupando-se com as regras da
sintaxe XML.
13.3 Percorrendo os nós
Muitas vezes, o XML tem vários nós e precisamos escrever um loop para processar todos os
nós. No programa a seguir, percorremos todos os nós do usuário:

import xml.etree.ElementTree as ET

input = '''

<stuff>

<users>

<user x="2">

<id>001</id>

<name>Chuck</name>

</user>

<user x="7">

<id>009</id>

<name>Brent</name>

</user>

</users>

</stuff>'''

stuff = ET.fromstring(input)

lst = stuff.findall('users/user')

print('User count:', len(lst))

for item in lst:

print('Name', item.find('name').text)

print('Id', item.find('id').text)

print('Attribute', item.get("x"))

# Code: http://www.pythonlearn.com/code3/xml2.py

O método findall recupera uma lista Python de subárvores que representam o usuário
estruturas na árvore XML. Então podemos escrever um loop for que olhe para cada um dos
nós do usuário e imprime os elementos de texto name e id, bem como o atributo x do nó do
usuário.
User count: 2

Name Chuck

Id 001

Attribute 2

Name Brent

Id 009

Attribute 7

13.4 Notação de objeto JavaScript - JSON


O formato JSON foi inspirado no formato de objeto e array usado na linguagem JavaScript.
Mas como o Python foi inventado antes do JavaScript, a sintaxe Python para dicionários e listas
influenciou a sintaxe do JSON. Então o formato de JSON é quase idêntico a uma combinação
de listas e dicionários do Python.

Aqui está uma codificação JSON que é aproximadamente equivalente ao XML simples acima:

"name" : "Chuck",

"phone" : {

"type" : "intl",

"number" : "+1 734 303 4456"

},

"email" : {

"hide" : "yes"

Você notará algumas diferenças. Primeiro, em XML, podemos adicionar atributos como “intl”
para a tag "phone". Em JSON, simplesmente temos pares chave-valor. Também o XML a tag
“pessoa” se foi, substituída por um conjunto de chaves externas.

Em geral, estruturas JSON são mais simples que XML porque JSON tem menos capacidades do
que XML. Mas o JSON tem a vantagem de mapear diretamente para algumas combinação de
dicionários e listas. E como quase todas as linguagens de programação têm algo equivalente
aos dicionários e listas do Python, o JSON é uma ural para que dois programas cooperantes
troquem dados.
JSON está rapidamente se tornando o formato de escolha para quase todas as trocas de dados
entre aplicações devido à sua relativa simplicidade em comparação com o XML.

13.5 Analisando JSON


Construímos nosso JSON aninhando dicionários (objetos) e listas conforme necessário. Neste
exemplo, representamos uma lista de usuários onde cada usuário é um conjunto de pares
chave-valor (ou seja, um dicionário). Portanto, temos uma lista de dicionários.

No programa a seguir, usamos a biblioteca json integrada para analisar o JSON e leia os dados.
Compare isso de perto com os dados e códigos XML equivalentes acima. O JSON tem menos
detalhes, então devemos saber com antecedência que estamos obtendo uma list e que a lista
é de usuários e cada usuário é um conjunto de pares chave-valor. o JSON é mais sucinto (uma
vantagem), mas também menos autodescritivo (uma desvantagem).

import json

input = '''

{ "id" : "001",

"x" : "2",

"name" : "Chuck"

},

{ "id" : "009",

"x" : "7",

"name" : "Chuck"

]'''

info = json.loads(input)

print('User count:', len(info))

for item in info:

print('Name', item['name'])

print('Id', item['id'])

print('Attribute', item['x'])

# Code: http://www.pythonlearn.com/code3/json2.py
Se você comparar o código para extrair dados do JSON e XML analisados, você vera que o que
obtemos de json.loads() é uma lista Python que percorremos com um loop for, e cada item
dessa lista é um dicionário Python. Uma vez que o JSON for analisado, podemos usar o
operador de índice do Python para extrair os vários bits de dados para cada usuário. Não
precisamos usar a biblioteca JSON para vasculhar o JSON analisado, já que os dados retornados
são simplesmente estruturas nativas do Python.

A saída desse programa é exatamente a mesma da versão XML acima.

User count: 2

Name Chuck

Id 001

Attribute 2

Name Brent

Id 009

Attribute 7

Em geral, há uma tendência da indústria de afastar-se do XML e em direção ao JSON para web
Serviços. Como o JSON é mais simples e mapeia mais diretamente para estruturas de dados
nativas que já temos em linguagens de programação, a análise e extração de dados, o código
geralmente é mais simples e direto ao usar JSON. Mas o XML é mais auto descritivo do que
JSON e, portanto, há alguns aplicativos em que o XML retém uma vantagem. Por exemplo, a
maioria dos processadores de texto armazena documentos internamente usando XML em vez
de JSON.

13.6 Interfaces de Programação de Aplicativos


Agora temos a capacidade de trocar dados entre aplicativos usando o protocolo de transporte
HyperText (HTTP) e uma maneira de representar dados complexos que estamos enviando
alternando entre esses aplicativos usando eXtensible Markup Language (XML) ou JavaScript
Object Notation (JSON).

O próximo passo é começar a definir e documentar “contratos” entre aplicativos usando essas
técnicas. O nome geral para esses contextos de aplicativo para aplicativo tracts são interfaces
de programas de aplicativos ou APIs. Quando usamos uma API, geralmente um programa
disponibiliza um conjunto de serviços para uso por outros aplicativos e publica as APIs (ou seja,
as “regras”) que devem ser seguidas para acessar os serviços fornecidos pelo programa.

Quando começamos a construir nossos programas onde a funcionalidade do nosso programa


inclui acesso a serviços fornecidos por outros programas, chamamos a abordagem de
Arquitetura Orientada a Serviços ou SOA. Uma abordagem SOA é aquela em que nosso
aplicativo faz uso dos serviços de outros aplicativos. Uma abordagem não-SOA é onde o
aplicativo é um único aplicativo autônomo que contém todos os código necessário para
implementar o aplicativo.

Vemos muitos exemplos de SOA quando usamos a web. Podemos ir para um único web site e
reservar viagens aéreas, hotéis e automóveis, tudo em um único site. Os dados para hotéis não
é armazenado nos computadores das companhias aéreas. Em vez disso, os computadores das
companhias aéreas contata os serviços nos computadores do hotel e recupera os dados do
hotel e apresenta ao usuário. Quando o usuário concorda em fazer uma reserva de hotel
usando o site da companhia aérea, o site da companhia aérea usa outro serviço da web nos
sistemas do hotel para realmente fazer a reserva. E quando chegar a hora de cobrar o valor
total da transação cartão de crédito, ainda outros computadores se envolvem no processo.

Uma arquitetura orientada a serviços tem muitas vantagens, incluindo: (1) sempre manter
apenas uma cópia dos dados (isto é particularmente importante para coisas como hotéis
reservas onde não queremos comprometer em excesso) e (2) os proprietários dos dados
podem definir as regras sobre o uso de seus dados. Com essas vantagens, um SOA o sistema
deve ser cuidadosamente projetado para ter bom desempenho e atender às necessidades do
usuário.

Quando uma aplicação disponibiliza um conjunto de serviços em sua API na web, nós
chamamos esses serviços da web.

13.7 Serviço web de geocodificação do Google


O Google tem um excelente serviço web que nos permite fazer uso de seus grandes bancos de
dados de informações geográficas. Podemos enviar uma string de pesquisa geográfica como
"Ann Arbor, MI" em sua API de geocodificação e fazer com que o Google retorne suas
melhores adivinhas onde em um mapa podemos encontrar nossa string de pesquisa e nos
informar sobre o pontos de referência nas proximidades.

O serviço de geocodificação é gratuito, mas com taxa limitada, portanto você não pode fazer
uso ilimitado da API em um aplicativo comercial. Mas se você tiver alguns dados de pesquisa
em que um usuário final inseriu um local em uma caixa de entrada de formato livre, você pode
usar esta API para limpar seus dados muito bem.

Ao usar uma API gratuita como a API de geocodificação do Google, você precisa ser respeitoso
no uso desses recursos. Se muitas pessoas abusarem do serviço, o Google pode reduzir
significativamente seu serviço gratuito.

Você pode ler a documentação online deste serviço, mas é bastante simples e você pode até
testá-lo usando um navegador digitando o seguinte URL em seu navegador:

http://maps.googleapis.com/maps/api/geocode/json?address=Ann+Arbor%2C+MI

Certifique-se de desempacotar a URL e remover todos os espaços do URL antes de colar no seu
navegador.

O seguinte é um aplicativo simples para solicitar ao usuário uma string de pesquisa, chamar a
API de geocodificação do Google e extrair informações do JSON retornado.
import urllib.request, urllib.parse, urllib.error

import json

serviceurl = 'http://maps.googleapis.com/maps/api/geocode/json?'

while True:

address = input('Enter location: ')

if len(address) < 1: break

url = serviceurl + urllib.parse.urlencode(

{'sensor': 'false', 'address': address})

print('Retrieving', url)

uh = urllib.request.urlopen(url)

data = uh.read().decode()

print('Retrieved', len(data), 'characters')

try:

js = json.loads(data)

except:

js = None

if not js or 'status' not in js or js['status'] != 'OK':

print('==== Failure To Retrieve ====')

print(data)

continue

print(json.dumps(js, indent=4))

lat = js["results"][0]["geometry"]["location"]["lat"]

lng = js["results"][0]["geometry"]["location"]["lng"]

print('lat', lat, 'lng', lng)

location = js['results'][0]['formatted_address']

print(location)

# Code: http://www.pythonlearn.com/code3/geojson.py

O programa pega a string de pesquisa e constrói uma URL com a string de pesquisa como um
parâmetro codificado corretamente e, em seguida, usa urllib para recuperar o texto da API de
geocodificação do Google. Ao contrário de uma página da web fixa, os dados que obtemos
dependem dos parâmetros que enviamos e os dados geográficos armazenados nos servidores
do Google.

Depois de recuperar os dados JSON, nós os analisamos com a biblioteca json e fazemos
algumas verificações para ter certeza de que recebemos bons dados e, em seguida, extraia as
informações que nós estamos procurando.

A saída do programa é a seguinte (alguns dos JSON retornados foram removido):

$ python geojson.py

Enter location: Ann Arbor, MI

Retrieving http://maps.googleapis.com/maps/api/

geocode/json?sensor=false&address=Ann+Arbor%2C+MI

Retrieved 1669 characters

"status": "OK",

"results": [

"geometry": {

"location_type": "APPROXIMATE",

"location": {

"lat": 42.2808256,

"lng": -83.7430378

},

"address_components": [

"long_name": "Ann Arbor",

"types": [

"locality",

"political"

],

"short_name": "Ann Arbor"


}

],

"formatted_address": "Ann Arbor, MI, USA",

"types": [

"locality",

"political"

lat 42.2808256 lng -83.7430378

Ann Arbor, MI, USA

Insira o local:

Você pode baixar www.pythonlearn.com/code3/geoxml.py para explorar a variante XML da


API de geocodificação do Google.

13.8 Segurança e uso da API


É bastante comum que você precise de algum tipo de “chave de API” para fazer uso de um
fornecedor API. A ideia geral é que eles querem saber quem está usando seus serviços e
quanto cada usuário está usando. Talvez eles tenham níveis gratuitos e pagos de seus serviços
ou ter uma política que limite o número de solicitações que um único indivíduo pode fazer
durante um determinado período de tempo.

Às vezes, depois de obter sua chave de API, você simplesmente inclui a chave como parte do
POST dados ou talvez como um parâmetro na URL ao chamar a API.

Outras vezes, o fornecedor deseja maior garantia da origem das solicitações, e eles
acrescentam que esperam que você envie mensagens assinadas criptograficamente usando
arquivos compartilhados, chaves e segredos. Uma tecnologia muito comum usada para assinar
solicitações na Internet é chamado OAuth. Você pode ler mais sobre o protocolo OAuth em
www.oauth.net.

À medida que a API do Twitter se tornava cada vez mais valiosa, o Twitter passou de uma
plataforma aberta e API pública para uma API que exigia o uso de assinaturas OAuth em cada
API solicitada. Felizmente, ainda existem várias bibliotecas OAuth convenientes e gratuitas,
então você pode evitar escrever uma implementação OAuth do zero lendo a especificação.
Essas bibliotecas são de complexidade variável e têm graus variados de riqueza. O site OAuth
tem informações sobre várias bibliotecas OAuth.

Para este próximo programa de amostra, faremos o download dos arquivos twurl.py,
hidden.py, oauth.py e twitter1.py de www.pythonlearn.com/code e coloque-os todos em uma
pasta em seu computador.

Para usar esses programas, você precisará ter uma conta no Twitter e autorizar seu código
Python como um aplicativo, configurar uma chave, segredo, token e segredo do token. Você
editará o arquivo hidden.py e colocará essas quatro strings na variáveis apropriadas no
arquivo:

# Mantenha este arquivo separado

# https://apps.twitter.com/

# Criar novo aplicativo

def oauth():

return {"consumer_key": "h7Lu...Ng",

"consumer_secret" : "dNKenAC3New...mmn7Q",

"token_key" : "10185562-eibxCp9n2...P4GEQQOSGI",

"token_secret" : "H0ycCFemmC4wyf1...qoIpBo"}

# Code: http://www.pythonlearn.com/code3/hidden.py

O serviço da web do Twitter é acessado usando um URL como este:

https://api.twitter.com/1.1/statuses/user_timeline.json

Mas assim que todas as informações de segurança forem adicionadas, a URL parecerá mais
como:

https://api.twitter.com/1.1/statuses/user_timeline.json?count=2

&oauth_version=1.0&oauth_token=101...SGI&screen_name=drchuck

&oauth_nonce=09239679&oauth_timestamp=1380395644

&oauth_signature=rLK...BoD&oauth_consumer_key=h7Lu...GNg

&oauth_signature_method=HMAC-SHA1

Você pode ler a especificação OAuth se quiser saber mais sobre o significado dos vários
parâmetros que são adicionados para atender aos requisitos de segurança do OAuth.
Para os programas que executamos com o Twitter, ocultamos toda a complexidade nos
arquivos oauth.py e twurl.py. Simplesmente definimos os segredos em hidden.py e enviamos a
URL desejada para a função twurl.augment() e o código da biblioteca adiciona todos os
parâmetros necessários para a URL para nós.

Este programa recupera a linha do tempo de um determinado usuário do Twitter e a retorna


para nós no formato JSON em uma string. Simplesmente imprimimos os primeiros 250
caracteres da corda:

import urllib.request, urllib.parse, urllib.error

import twurl

TWITTER_URL = 'https://api.twitter.com/1.1/statuses/user_timeline.json'

while True:

print('')

acct = input('Enter Twitter Account:')

if (len(acct) < 1): break

url = twurl.augment(TWITTER_URL,

{'screen_name': acct, 'count': '2'})

print('Retrieving', url)

connection = urllib.request.urlopen(url)

data = connection.read().decode()

print(data[:250])

headers = dict(connection.getheaders())

# cabeçalhos de impressão

print('Remaining', headers['x-rate-limit-remaining'])

# Code: http://www.pythonlearn.com/code3/twitter1.py

Quando o programa é executado, ele produz a seguinte saída:

Enter Twitter Account:drchuck

Retrieving https://api.twitter.com/1.1/ ...

[{"created_at":"Sat Sep 28 17:30:25 +0000 2013","


id":384007200990982144,"id_str":"384007200990982144",

"text":"RT @fixpert: See how the Dutch handle traffic

intersections: http:\/\/t.co\/tIiVWtEhj4\n#brilliant",

"source":"web","truncated":false,"in_rep

Remaining 178

Enter Twitter Account:fixpert

Retrieving https://api.twitter.com/1.1/ ...

[{"created_at":"Sat Sep 28 18:03:56 +0000 2013",

"id":384015634108919808,"id_str":"384015634108919808",

"text":"3 months after my freak bocce ball accident,

my wedding ring fits again! :)\n\nhttps:\/\/t.co\/2XmHPx7kgX",

"source":"web","truncated":false,

Remaining 177

Entre na conta do Twitter:

Juntamente com os dados da linha do tempo retornados, o Twitter também retorna


metadados sobre a solicitação nos cabeçalhos de resposta HTTP. Um cabeçalho em particular,
x-rate-limit- restante, nos informa quantos pedidos mais podemos fazer antes de fecharmos
desligada por um curto período de tempo. Você pode ver que nossas recuperações restantes
diminuem em um cada vez que fazemos uma solicitação à API.

No exemplo a seguir, recuperamos os amigos do Twitter de um usuário, analisamos o retorno


JSON e extraia algumas informações sobre os amigos. Nós também despejamos o JSON depois
de analisar e "imprimir" com um recuo de quatro caracteres para permitir examinarmos os
dados quando quisermos extrair mais campos.

import urllib.request, urllib.parse, urllib.error

import twurl

import json

TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'

while True:

print('')

acct = input('Enter Twitter Account:')

if (len(acct) < 1): break

url = twurl.augment(TWITTER_URL,
{'screen_name': acct, 'count': '5'})

print('Retrieving', url)

connection = urllib.request.urlopen(url)

data = connection.read().decode()

headers = dict(connection.getheaders())

print('Remaining', headers['x-rate-limit-remaining'])

js = json.loads(data)

print(json.dumps(js, indent=4))

for u in js['users']:

print(u['screen_name'])

s = u['status']['text']

print(' ', s[:50])

# Code: http://www.pythonlearn.com/code3/twitter2.py

Como o JSON se torna um conjunto de listas e dicionários Python aninhados, podemos usar
uma combinação da operação de índice e loops for para percorrer o retorno das estruturas de
dados com muito pouco código Python.

A saída do programa é a seguinte (alguns dos itens de dados são encurtados para caber na
página):

Enter Twitter Account:drchuck

Retrieving https://api.twitter.com/1.1/friends ...

Remaining 14

"next_cursor": 1444171224491980205,

"users": [

"id": 662433,

"followers_count": 28725,

"status": {
"text": "@jazzychad I just bought one .__.",

"created_at": "Fri Sep 20 08:36:34 +0000 2013",

"retweeted": false,

},

"location": "San Francisco, California",

"screen_name": "leahculver",

"name": "Leah Culver",

},

"id": 40426722,

"followers_count": 2635,

"status": {

"text": "RT @WSJ: Big employers like Google ...",

"created_at": "Sat Sep 28 19:36:37 +0000 2013",

},

"location": "Victoria Canada",

"screen_name": "_valeriei",

"name": "Valerie Irvine",

],

"next_cursor_str": "1444171224491980205"

leahculver

@jazzychad I just bought one .__.

_valeriei

RT @WSJ: Big employers like Google, AT&amp;T are h

ericbollens

RT @lukew: sneak peek: my LONG take on the good &a

halherzog

Learning Objects is 10. We had a cake with the LO,

scweeker
@DeviceLabDC love it! Now where so I get that "etc

Entre na conta do Twitter:

O último bit da saída é onde vemos o loop for lendo os cinco mais recentes “amigos” da conta
drchuck no Twitter e imprimir o status mais recente para cada amigo. Há muito mais dados
disponíveis no JSON retornado. Se você olha na saída do programa, você também pode ver
que o “encontre os amigos” de uma conta específica tem uma limitação de taxa diferente do
número de linhas do tempo consultas que podemos executar por período de tempo.

Essas chaves de API seguras permitem que o Twitter tenha uma confiança sólida de que eles
sabem quem está usando sua API e dados e em que nível. A abordagem de limitação de taxa
nos permite fazer recuperações de dados pessoais simples, mas não nos permite construir um
produto que extrai dados de sua API milhões de vezes por dia.

13.9 Glossário
API Application Program Interface - Um contrato entre aplicativos que define os padrões de
interação entre dois componentes do aplicativo.

ElementTree - Uma biblioteca Python integrada usada para analisar dados XML.

Notação de objeto JavaScript JSON - Um formato que permite a marcação de estruturas de


dados estruturados com base na sintaxe de objetos JavaScript.

SOA Arquitetura Orientada a Serviços - Quando um aplicativo é feito de componentes


conectado através de uma rede.

XML eXtensible Markup Language - Um formato que permite a marcação de dados


estruturados.

13.10 Exercícios
Exercício 1: Altere o www.pythonlearn.com/code3/geojson.py ou
www.pythonlearn.com/code3/geoxml.py para imprimir o país de dois código dos dados
recuperados. Adicione verificação de erros para que seu programa não de traceback se o
código do país não estiver lá. Assim que estiver funcionando, pesquise para “Oceano Atlântico”
e certifique-se de que ele pode lidar com locais que não estão em nenhum país.
Capítulo 14
Programação orientada a objetos

14.1 Gerenciando Programas Maiores


No início deste e-book, criamos quatro padrões básicos de programação que usamos para
construir programas:

• Código sequencial

• Código condicional (Instruções)

• Código repetitivo (loops)

• Armazenar e reutilizar (funções)

Em capítulos posteriores, exploramos variáveis simples, bem como estruturas de dados de


coleta como listas, tuplas e dicionários.

À medida que construímos programas, projetamos estruturas de dados e escrevemos códigos


para manipular essas estruturas de dados. Existem muitas maneiras de escrever programas e,
agora, você provavelmente já escreveu alguns programas que são “não tão elegantes” e outros
programas que são “mais elegantes”. Mesmo que seus programas sejam pequenos, você está
começando para ver como há um pouco de “arte” e “estética” para escrever código.

À medida que os programas chegam a ter milhões de linhas, torna-se cada vez mais
importante escrever códigos que são fáceis de entender. Se você estiver trabalhando em um
programa de milhões de linhas, você nunca pode manter todo o programa em sua mente ao
mesmo tempo. Então nós precisamos de maneiras de dividir o programa em várias partes
menores para resolvem um problema, corrigir um bug ou adicionar um novo recurso.

De certa forma, a programação orientada a objetos é uma maneira de organizar seu código
para que você poça ampliar em 500 linhas do código e entendê-lo enquanto ignora as outras
999.500 linhas de código no momento.

14.2 Começando
Como muitos aspectos da programação, é necessário aprender os conceitos de objeto de
programação orientada antes que você possa usá-los efetivamente. Portanto, aproxime-se
deste capítulo como uma forma de aprender alguns termos e conceitos e trabalhar com alguns
exemplos para estabelecer uma base para o aprendizado futuro. Ao longo do resto do e-book
estaremos usando objetos em muitos dos programas, mas não estaremos construindo nosso
novos objetos nos programas.

O principal resultado deste capítulo é ter uma compreensão básica de como os objetos são
construídos e como eles funcionam e, mais importante, como fazemos uso dos recursos dos
objetos que nos são fornecidos pelas bibliotecas Python.

14.3 Usando Objetos


Acontece que temos usado objetos o tempo todo nesta classe. Python fornece a nós com
muitos objetos embutidos. Aqui está um código simples onde as primeiras linhas deve parecer
muito simples e natural para você.

stuff = list()

stuff.append('python')

stuff.append('chuck')

stuff.sort()

print (stuff[0])

print (stuff.__getitem__(0))

print (list.__getitem__(stuff,0))

# Code: http://www.pythonlearn.com/code3/party1.py

Mas, em vez de focar no que essas linhas realizam, vamos ver o que esta realmente
acontecendo do ponto de vista da programação orientada a objetos. Não se preocupe se os
parágrafos a seguir não fizerem sentido na primeira vez que você os ler porque ainda não
definimos todos esses termos.

A primeira linha está construindo um objeto do tipo lista, a segunda e a terceira linhas estão
chamando o método append(), a quarta linha está chamando o método sort() e a quinta linha
está recuperando o item na posição 0.

A sexta linha está chamando o método __getitem__() na lista de coisas com um parâmetro de
zero.

print (stuff.__getitem__(0))

A sétima linha é uma maneira ainda mais detalhada de recuperar o 0º item da lista.

print (list.__getitem__(stuff,0))
Neste código, nos preocupamos em chamar o método __getitem__ na classe lista e passar na
lista (coisas) e o item que queremos recuperar da lista como parâmetros.

As últimas três linhas do programa são completamente equivalentes, mas é mais consistente e
conveniente simplesmente usar a sintaxe de colchetes para procurar um item em uma
determinada posição em uma lista.

Podemos dar uma olhada nas capacidades de um objeto olhando para a saída da função dir():

>>> stuff = list()

>>> dir(stuff)

['__add__', '__class__', '__contains__', '__delattr__',

'__delitem__', '__dir__', '__doc__', '__eq__',

'__format__', '__ge__', '__getattribute__', '__getitem__',

'__gt__', '__hash__', '__iadd__', '__imul__', '__init__',

'__iter__', '__le__', '__len__', '__lt__', '__mul__',

'__ne__', '__new__', '__reduce__', '__reduce_ex__',

'__repr__', '__reversed__', '__rmul__', '__setattr__',

'__setitem__', '__sizeof__', '__str__', '__subclasshook__',

'append', 'clear', 'copy', 'count', 'extend', 'index',

'insert', 'pop', 'remove', 'reverse', 'sort']

>>>

A definição precisa de dir() é que ele lista os métodos e atribuições de um objeto Python.

O restante deste capítulo definirá todos os termos acima, portanto, certifique-se de voltar
depois de terminar o capítulo e releia os parágrafos acima para verificar seu entendimento.

14.4 Iniciando com Programas


Um programa em sua forma mais básica recebe alguma entrada, faz algum processamento e
produz alguma saída. Nosso programa de conversão de elevador demonstra um tempo muito
curto mas é um programa completo mostrando todas essas três etapas.

usf = input('Enter the US Floor Number: ')

wf = int(usf) - 1

print('Non-US Floor Number is',wf)


# Code: http://www.pythonlearn.com/code3/elev.py

Se pensarmos um pouco mais neste programa, existe o “mundo lá fora” e o programa. Os


aspectos de entrada e saída são onde o programa interage com o mundo exterior. Dentro do
programa temos código e dados para realizar a tarefa, o programa é projetado para resolver.]

Quando estamos “dentro” do programa, temos algumas interações definidas com o “outro
lado” do mundo, mas essas interações são bem definidas e geralmente não são algo
concentrados. Enquanto estamos codificando nos preocupamos apenas com os detalhes
“dentro do programa".

Uma maneira de pensar sobre a programação orientada a objetos é que estamos separando
nosso programa em múltiplas “zonas”. Cada “zona” contém algum código e dados (como um
programa) e tendo interações bem definidas com o mundo exterior e as outras zonas dentro
do programa.

Se olharmos para o aplicativo de extração de links onde usamos a biblioteca BeautifulSoup,


podemos ver um programa que é construído conectando diferentes objetos juntos para
realizar uma tarefa:

# Para executar isso, você pode instalar o BeautifulSoup

# https://pypi.python.org/pypi/beautifulsoup4

# Ou baixe o arquivo

# http://www.pythonlearn.com/code3/bs4.zip

# e descompacte-o no mesmo diretório deste arquivo

import urllib.request, urllib.parse, urllib.error

from bs4 import BeautifulSoup

url = input('Enter - ')

html = urllib.request.urlopen(url).read()

soup = BeautifulSoup(html, 'html.parser')

# Retrieve all of the anchor tags

tags = soup('a')

for tag in tags:

print(tag.get('href', None))

# Code: http://www.pythonlearn.com/code3/urllinks.py

Lemos a URL em uma string e, em seguida, passamos para urllib para recuperar os dados da
web. A biblioteca urllib usa a biblioteca socket para fazer a real conexão de rede para
recuperar os dados. Pegamos a corda que recebemos de volta do urllib e entregue-o ao
BeautifulSoup para análise. BeautifulSoup faz uso de outro objeto chamado html.parser1 e
retorna um objeto. Chamamos os método tags(), método no objeto retornado e, em seguida,
obtenha um dicionário de objetos de tag e faça um loop através das tags e chame o método
get() para cada tag para imprimir o~ atributo 'href'.

A chave aqui não é entender completamente como este programa funciona, mas ver como nós
construirmos uma rede de objetos interativos e orquestrar o movimento da informação entre
os objetos para criar um programa. Também é importante observar que quando você olhou
para aquele programa vários capítulos atrás, você pode entender completamente o que estava
acontecendo no programa, mesmo sem perceber que o programa estava “orquestrando o
movimento de dados entre objetos”. Naquela época eram apenas linhas de código que fez o
trabalho.

14.5 Subdividindo um Problema - Encapsulamento


Uma das vantagens da abordagem orientada a objetos é que ela pode ocultar a complexidade.
Por exemplo, enquanto precisamos saber como usar o código urllib e BeautifulSoup, não
precisamos saber como essas bibliotecas funcionam internamente. Isso nos permite focar na
parte do problema que precisamos resolver e ignorar as outras partes do programa.

Essa capacidade de focar em uma parte de um programa que nos interessa e ignorar o resto
do programa também é útil para os desenvolvedores dos objetos. Por exemplo os
programadores que desenvolvem BeautifulSoup não precisam saber ou se preocupar com a
forma de como recuperar nossa página HTML, quais partes queremos ler ou o que planejamos
fazer com os dados que extraímos da página web.

Outra palavra que usamos para captar essa ideia de que ignoramos os detalhes internos de
objetos que usamos é “encapsulamento”. Isso significa que podemos saber como usar um
objeto sem saber como ele realiza internamente o que precisamos fazer.

14.6 Nosso primeiro objeto Python


Na sua forma mais simples, um objeto é um código mais estruturas de dados menores que um
programa inteiro. Definir uma função nos permite armazenar um pouco de código e dar a ele
um nome e, posteriormente, invocar esse código usando o nome da função.

Um objeto pode conter uma série de funções (que chamamos de “métodos”), bem como
dados usados por essas funções. Chamamos itens de dados que fazem parte dos objetos
"atributos".

Usamos a palavra-chave class para definir os dados e o código que comportão cada um dos
objetos. A palavra-chave class inclui o nome da classe e inicia um bloco de código recuado
onde incluímos os atributos (dados) e métodos (código).
class PartyAnimal:

x=0

def party(self) :

self.x = self.x + 1

print("So far",self.x)

an = PartyAnimal()

an.party()

an.party()

an.party()

PartyAnimal.party(an)

# Code: http://www.pythonlearn.com/code3/party2.py

Cada método se parece com uma função, começando com a palavra-chave def e consistindo
de um bloco de código recuado. Este exemplo tem um atributo (x) e um método (party). Os
métodos têm um primeiro parâmetro especial que nomeamos por convenção auto.

Assim como a palavra-chave def não faz com que o código de função seja executado, a
palavra-chave class não cria um objeto. Em vez disso, a palavra-chave class define um modelo
indicando quais dados e códigos estarão contidos em cada objeto do tipo PartyAnimal. A classe
é como um cortador de biscoitos e os objetos criados usando o class são os cookies. Você não
coloca glacê no cortador de biscoitos, você põe glacê nos biscoitos e você pode colocar glacê
diferente em cada biscoito.

Se você continuar com o código de exemplo, veremos a primeira linha de código executável:

an = PartyAnimal()

É aqui que instruímos o Python a construir (por exemplo, criar) um objeto ou “instância da
classe chamada PartyAnimal”. Parece uma chamada de função para a própria classe e o Python
constrói o objeto com os dados e métodos corretos e retorna o objeto que é então atribuído à
variável an. De certa forma, isso é bastante semelhante para a seguinte linha que temos usado
o tempo todo:

counts = dict()

Aqui estamos instruindo o Python a construir um objeto usando o modelo dict (já presente no
Python), retorne a instância do dicionário e atribua-a a contagens variáveis.
Quando a classe PartyAnimal é usada para construir um objeto, a variável an é usada para
apontar para esse objeto. Usamos um para acessar o código e os dados dessa determinada
instância de um objeto PartyAnimal.

Cada objeto/instância Partyanimal contém dentro de si uma variável x e um método/função


chamado party. Chamamos esse método party nesta linha:

an.party()

Quando o método party é chamado, o primeiro parâmetro (que chamamos por convenção
self) aponta para a instância particular do objeto PartyAnimal que a party está chamando de
dentro. Dentro do método party, vemos a linha:

self.x = self.x + 1

Esta sintaxe usando o operador 'dot' está dizendo 'o x dentro de si'. Então cada vez que party()
é chamado, o valor x interno é incrementado em 1 e o valor é impresso fora.

Para ajudar a entender a diferença entre uma função global e um método dentro de uma
classe/objeto, a linha a seguir é outra maneira de chamar o método party dentro de um
objeto:

PartyAnimal.party(an)

Nesta variação, estamos acessando o código de dentro da classe e passamos explicitamente


ing o ponteiro de objeto an como o primeiro parâmetro (ou seja, self dentro do método).
Você pode pensar em an.party() como um atalho para a linha acima.

Quando o programa é executado, ele produz a seguinte saída:

So far 1

So far 2

So far 3

So far 4

O objeto é construído e o método party é chamado quatro vezes, ambas incrementada e


imprimindo o valor de x dentro de um objeto.
14.7 Classes como Tipos
Como vimos, em Python todas as variáveis possuem um tipo. E podemos usar a função built-in
para examinar os recursos de uma variável. Podemos usar type e dir com as classes que
criamos.

class PartyAnimal:

x=0

def party(self) :

self.x = self.x + 1

print("So far",self.x)

an = PartyAnimal()

print ("Type", type(an))

print ("Dir ", dir(an))

print ("Type", type(an.x))

print ("Type", type(an.party))

# Code: http://www.pythonlearn.com/code3/party3.py

Quando este programa é executado, ele produz a seguinte saída:

Type <class '__main__.PartyAnimal'>

Dir ['__class__', '__delattr__', ...

'__sizeof__', '__str__', '__subclasshook__',

'__weakref__', 'party', 'x']

Type <class 'int'>

Type <class 'method'>

Você pode ver que, usando a palavra-chave class, criamos um novo tipo, de saída dir, você
pode ver que o atributo x interage e o método party são disponíveis no objeto.
14.8 Ciclo de Vida do Objeto
Nos exemplos anteriores, estamos definindo uma classe (template) e usando essa classe para
criar uma instância dessa classe (object) e, em seguida, usar a instância. Quando o programa
termina, todas as variáveis são descartadas. Normalmente não pensamos muito sobre a
criação e destruição de variáveis, mas muitas vezes como nossos objetos se tornam mais
complexo, precisamos realizar alguma ação dentro do objeto para definir as coisas como o
objeto está sendo construído e, possivelmente, limpar as coisas enquanto o objeto está sendo
descartado.

Se queremos que nosso objeto esteja ciente desses momentos de construção e destruição,
adicionamos métodos especialmente nomeados ao nosso objeto:

class PartyAnimal:

x=0

def __init__(self):

print('I am constructed')

def party(self) :

self.x = self.x + 1

print('So far',self.x)

def __del__(self):

print('I am destructed', self.x)

an = PartyAnimal()

an.party()

an.party()

an = 42

print('an contains',an)

# Code: http://www.pythonlearn.com/code3/party4.py

Quando este programa é executado, ele produz a seguinte saída:

I am constructed

So far 1

So far 2

I am destructed 2
an contains 42

Conforme o Python está construindo nosso objeto, ele chama nosso método __init__ para nos
fornecer uma chance de configurar alguns valores padrão ou iniciais para o objeto. Quando
Python encontra a linha:

an = 42

Na verdade, "joga nosso objeto fora" para que possa reutilizar uma variável para armazenar o
valor 42. Exatamente no momento em que nosso objeto está sendo 'destruído', nosso código
destruidor (__del__) é chamado. Não podemos impedir que nossa variável seja destruída, mas
podemos fazer qualquer limpeza necessária antes que nosso objeto não exista mais.

Ao desenvolver objetos, é bastante comum adicionar um construtor a um objeto, definido em


valores iniciais no objeto, é relativamente raro precisar de um destruidor para um objeto.

14.9 Muitas Instâncias


Até agora, definimos uma classe, criando um único objeto, usando esse objeto, e, em seguida,
jogando o objeto fora. Mas o verdadeiro poder da orientação a objetos acontece quando
fazemos muitas instâncias de nossa classe.

Quando estamos fazendo vários objetos de nossa classe, podemos querer configurar
diferentes valores iniciais diferentes para cada um dos objetos. Podemos passar dados para os
construtores para dar a cada objeto um valor inicial diferente:

class PartyAnimal:

x=0

name = ''

def __init__(self, nam):

self.name = nam

print(self.name,'constructed')

def party(self) :

self.x = self.x + 1

print(self.name,'party count',self.x)

s = PartyAnimal('Sally')

s.party()
j = PartyAnimal('Jim')

j.party()

s.party()

# Code: http://www.pythonlearn.com/code3/party5.py

O construtor tem um parâmetro self que aponta para a instância do objeto e parâmetros
adicionais que são passados para o construtor conforme o objeto é sendo construído:

s = PartyAnimal('Sally')

Dentro do construtor, a linha:

self.name = nam

Copia o parâmetro passado em (nam) para o atributo name dentro da instância do objeto.

A saída do programa mostra que cada um dos objetos (s e j) contém suas próprias cópias
independentes de x e nam:

Sally constructed

Sally party count 1

Jim constructed

Jim party count 1

Sally party count 2

14.10 Herança
Outro recurso poderoso da programação orientada a objetos é a capacidade de criar uma nova
classe estendendo uma classe existente. Ao estender uma classe, chamamos a classe original
como a 'classe pai' e a nova classe como a 'classe filha'.

Para este exemplo, moveremos nossa classe PartyAnimal para seu próprio arquivo:
class PartyAnimal:

x=0

name = ''

def __init__(self, nam):

self.name = nam

print(self.name,'constructed')

def party(self) :

self.x = self.x + 1

print(self.name,'party count',self.x)

# Code: http://www.pythonlearn.com/code3/party.py

Então, podemos ‘importar’ a classe PartyAnimal em um novo arquivo e estendê-lo da seguinte


forma:

from party import PartyAnimal

class CricketFan(PartyAnimal):

points = 0

def six(self):

self.points = self.points + 6

self.party()

print(self.name,"points",self.points)

s = PartyAnimal("Sally")

s.party()

j = CricketFan("Jim")

j.party()

j.six()

print(dir(j))

# Code: http://www.pythonlearn.com/code3/party6.py
Quando estamos definindo o objeto CricketFan, indicamos que estamos estendendo a classe
PartyAnimal. Isso significa que todas as variáveis (x) e métodos (party) da classe PartyAnimal
são herdados pela classe CricketFan.

Você pode ver que dentro do método six na classe CricketFan, podemos chamar o método
party da classe PartyAnimal. As variáveis e métodos da classe pai são mescladas na classe filha.

À medida que o programa é executado, podemos ver que s e j são instâncias independentes de
PartyAnimal e CricketFan. O objeto j tem recursos adicionais além do objeto s.

Sally constructed

Sally party count 1

Jim constructed

Jim party count 1

Jim party count 2

Jim points 6

['__class__', '__delattr__', ... '__weakref__',

'name', 'party', 'points', 'six', 'x']

Na saída dir para o objeto j (instância da classe CricketFan), você pode ver que ele tem os
atributos e métodos da classe pai, bem como o atributos e métodos que foram adicionados
quando a classe foi estendida para criar o Classe CricketFan.

14.11 Resumo
Esta é uma introdução muito rápida à programação orientada a objetos que se concentra
principalmente na terminologia e na sintaxe de definição e uso de objetos. Vamos
rapidamente revisar o código que examinamos no início do capítulo. Neste ponto você deve
entender completamente o que está acontecendo.

stuff = list()

stuff.append('python')

stuff.append('chuck')

stuff.sort()

print (stuff[0])

print (stuff.__getitem__(0))

print (list.__getitem__(stuff,0))
# Code: http://www.pythonlearn.com/code3/party1.py

A primeira linha constrói um objeto de lista. Quando o Python cria o objeto de lista, ele chama
o método construtor (chamado __init__) para configurar os dados internos atributos que
serão usados para armazenar os dados da lista. Devido ao encapsulamento, nós nem
precisamos saber, nem precisa se preocupar com isso em atributos de dados internos são
organizados.

Não estamos passando nenhum parâmetro para o construtor e quando o construtor retorna,
usamos a variável stuff para apontar para a instância retornada da lista class.

A segunda e terceira linhas estão chamando o método append com um parâmetro para
adicionar um novo item no final da lista atualizando os atributos dentro do material. Então, na
quarta linha, chamamos o método sort sem parâmetros para classificar os dados dentro do
objeto material.

Em seguida, imprimimos o primeiro item da lista usando os colchetes que são um atalho para
chamar o método __getitem__ dentro do objeto stuff. E isso é equivalente a chamar o método
__getitem__ na classe de lista passando o objeto material como o primeiro parâmetro e a
posição que estamos procurando como o segundo parâmetro.

No final do programa, o objeto de material é descartado, mas não antes de chamar o


destruidor (named__del__) para que o objeto possa limpar quaisquer pontas soltas conforme
necessário.

Esses são os fundamentos e a terminologia da programação orientada a objetos. Há muitos


detalhes adicionais sobre como usar melhor as abordagens orientadas a objetos quanto
desenvolvimento de grandes aplicativos e bibliotecas que estão além do escopo deste
capítulo.

14.12 Glossário
Atributo - Uma variável que faz parte de uma classe.

Classe - Um modelo que pode ser usado para construir um objeto. Define os atributos e
métodos que irão compor o objeto.

Classe filha - Uma nova classe criada quando uma classe pai é estendida. a classe filha herda
todos os atributos e métodos da classe pai.

Construtor - Um método opcional especialmente nomeado (__init__) que é chamado em


momentos em que uma classe está sendo usada para construir um objeto. geralmente isso é
usado para configurar valores iniciais para o objeto.

Destruidor - Um método opcional especialmente nomeado (__del__) que é chamado no


momento imediato antes de um objeto ser destruído. Destruidores raramente são usados.

Herança - Quando criamos uma nova classe (filho) estendendo uma classe existente (pai). A
classe filha tem todos os atributos e métodos da classe pai mais os atributos e métodos
adicionais definidos pela classe filha.
Método - Uma função que está contida dentro de uma classe e os objetos que são construídos
a partir da class. Alguns padrões orientados a objetos usam ‘message’ em vez de 'método' para
descrever este conceito.

Objeto - Uma instância construída de uma classe. Um objeto contém todos os atributos e
métodos que foram definidos pela classe. Alguns documentos orientados a objetos usam o
termo ‘instância’ de forma intercambiável com ‘objeto’.

Classe pai - A classe que está sendo estendida para criar uma nova classe filha. A classe pai
contribui com todos os seus métodos e atributos para A nova chaild class. 
Capítulo 15
Usando bancos de dados e SQL
15.1 O que é um banco de dados?
Um banco de dados é um arquivo organizado para armazenar dados. A maioria dos bancos de
dados são organizados como um dicionário no sentido de que eles mapeiam de chaves para
valores. A maior da diferença é que o banco de dados está em disco (ou outro armazenamento
permanente), então ele persiste após o término do programa. Como um banco de dados é
armazenado em armazenamento permanente, ele pode armazenar muito mais dados do que
um dicionário, que é limitado ao tamanho da memória no computador.

Como um dicionário, o software de banco de dados é projetado para manter a inserção e o


acesso de dados muito rápido, mesmo para grandes quantidades de dados. O software de
banco de dados mantém seu desempenho construindo índices à medida que os dados são
adicionados ao banco de dados para permitir que o computador pule rapidamente para uma
entrada específica.

Existem muitos sistemas de banco de dados diferentes que são usados para uma ampla
variedade de propósitos. Incluindo: Oracle, MySQL, Microsoft SQL Server, PostgreSQL e SQLite.
Focamos no SQLite neste e-book, porque é um banco de dados muito comum e é já embutido
no Python. O SQLite foi projetado para ser incorporado a outras aplicações para fornecer
suporte de banco de dados dentro do aplicativo. Por exemplo, o Firefox, o navegador também
usa o banco de dados SQLite internamente, assim como muitos outros produtos.

http://sqlite.org/

O SQLite é adequado para alguns dos problemas de manipulação de dados que vemos em
Informática, como o aplicativo spidering do Twitter que descrevemos neste capítulo.

15.2 Conceitos de banco de dados


Quando você olha pela primeira vez para um banco de dados, ele se parece com uma planilha
vária. As estruturas de dados primárias em um banco de dados são: tabelas, linhas e colunas.

Nas descrições técnicas de bancos de dados relacionais, os conceitos de tabela, linha e coluna
são mais formalmente referidos como relação, tupla e atributo, respectivamente. Usaremos os
termos menos formais neste capítulo.

15.3 Navegador de banco de dados para SQLite


Embora este capítulo se concentre no uso do Python para trabalhar com dados no banco de
dados SQLite, muitas operações podem ser feitas de forma mais convenientes usando um
software chamado Navegador de banco de dados para SQLite que está disponível
gratuitamente em:
http://sqlitebrowser.org/

Usando o navegador, você pode facilmente criar tabelas, inserir dados, editar dados ou
executar tarefas simples, consulte SQL sobre os dados no banco de dados.

De certa forma, o navegador de banco de dados é semelhante a um editor de texto ao


trabalhar com arquivos de textos. Quando você quiser fazer uma ou poucas operações em um
arquivo de texto, basta abrir em um editor de texto e faça as alterações desejadas. Quando
você tem muitas alterações que você precisa fazer em um arquivo de texto, geralmente você
escreve um código Python com um programa simples. Você encontrará o mesmo padrão ao
trabalhar com bancos de dados. Você irá fazer operações simples no gerenciador de banco de
dados e operações mais complexas serão feitos de forma mais conveniente em Python.

15.4 Criando uma tabela de banco de dados


Bancos de dados requerem estrutura mais definidas do que listas ou dicionários Python.

Quando criamos uma tabela de banco de dados, devemos informar ao banco de dados com
antecedência os nomes de cada uma das colunas da tabela e o tipo de dados que pretendemos
armazenar em cada coluna. Quando o software de banco de dados conhece o tipo de dados
em cada coluna, ele pode escolher a maneira mais eficiente de armazenar e consultar os dados
com base no tipo de dados.

Você pode ver os vários tipos de dados suportados pelo SQLite no seguinte URL:

http://www.sqlite.org/datatypes.html

Definir a estrutura de seus dados antecipadamente pode parecer inconveniente no começo,


mas a recompensa é o acesso rápido aos seus dados, mesmo quando o banco de dados
contém uma grande quantidade de dados.

O código para criar um arquivo de banco de dados e uma tabela chamada Tracks com duas
colunas em um banco de dados é o seguinte:

import sqlite3

conn = sqlite3.connect('music.sqlite')

cur = conn.cursor()

cur.execute('DROP TABLE IF EXISTS Tracks')

cur.execute('CREATE TABLE Tracks (title TEXT, plays INTEGER)')

conn.close()

# Code: http://www.pythonlearn.com/code3/db1.py

A operação de conexão faz uma “conexão” com o banco de dados armazenado no arquivo
music.sqlite3 no diretório atual. Se o arquivo não existir, ele será criado. A razão pela qual isso
é chamado de “conexão” é que às vezes o banco de dados é armazenado em um “servidor de
banco de dados” separado do servidor no qual estamos executando nosso aplicativo. Em
nossos exemplos simples, o banco de dados será apenas um arquivo local no mesmo diretório
do código Python que estamos executando.

Um cursor é como um identificador de arquivo que podemos usar para executar operações
nos dados armazenados no banco de dados. Chamar cursor() é muito semelhante
conceitualmente a chamar open() ao lidar com arquivos de texto.

Uma vez que temos o cursor, podemos começar a executar comandos sobre o conteúdo do
banco de dados usando o método execute().

Os comandos do banco de dados são expressos em uma linguagem especial que foi
padronizada em muitos fornecedores de banco de dados diferentes para nos permitir
aprender uma única linguagem de banco de dados. A linguagem do banco de dados é chamada
Linguagem de Consulta Estruturada ou SQL para ser mais curto.

http://en.wikipedia.org/wiki/SQL

Em nosso exemplo, estamos executando dois comandos SQL em nosso banco de dados. Como
uma convenção, mostraremos as palavras-chave SQL em maiúsculas e as partes do comando
que estamos adicionando (como os nomes das tabelas e colunas) será mostrado em
minúsculas.

O primeiro comando SQL remove a tabela Tracks do banco de dados, se ela existir. Este padrão
é simplesmente para nos permitir executar o mesmo programa para criar as faixas de uma
tabela e outra vez sem causar um erro. Observe que o comando DROP TABLE exclui a tabela e
todo o seu conteúdo do banco de dados.

cur.execute('DROP TABLE IF EXISTS Tracks ')

O segundo comando cria uma tabela chamada Tracks com uma coluna de texto chamada title
e uma coluna inteira chamada plays.

cur.execute('CREATE TABLE Tracks (title TEXT, plays INTEGER)')

Agora que criamos uma tabela chamada Tracks, podemos colocar alguns dados na tabela
usando a operação SQL INSERT. Mais uma vez, começamos fazendo uma conexão ao banco de
dados e obtendo o cursor. Podemos então executar comandos SQL usando o cursor.

O comando SQL INSERT indica qual tabela estamos usando e então define uma nova linha
listando os campos que queremos incluir (título, reproduções) seguidos pelo VALUES que
queremos colocar na nova linha. Especificamos os valores como pontos de interrogação (?, ?)
para indicar que os valores reais são passados como uma tupla ( ’My Way’, 15 ) como o
segundo parâmetro para a chamada execute().
import sqlite3

conn = sqlite3.connect('music.sqlite')

cur = conn.cursor()

cur.execute('INSERT INTO Tracks (title, plays) VALUES (?, ?)',

('Thunderstruck', 20))

cur.execute('INSERT INTO Tracks (title, plays) VALUES (?, ?)',

('My Way', 15))

conn.commit()

print('Tracks:')

cur.execute('SELECT title, plays FROM Tracks')

for row in cur:

print(row)

cur.execute('DELETE FROM Tracks WHERE plays < 100')

cur.close()

# Code: http://www.pythonlearn.com/code3/db2.py

Primeiro, INSERT duas linhas em nossa tabela e usamos commit () para forçar os dados a
serem gravado no arquivo de banco de dados.

Em seguida, usamos o comando SELECT para recuperar as linhas que acabamos de inserir do
table. No comando SELECT, indicamos quais colunas desejamos (title, plays) e indicar de qual
tabela queremos recuperar os dados. Depois de executar a instrução SELECT, o cursor é algo
que podemos percorrer em um loop para declaração. Para maior eficiência, o cursor não lê
todos os dados do banco de dados quando executamos a instrução SELECT. Em vez disso, os
dados são lidos em demanda à medida que percorremos as linhas na instrução for.

A saída do programa é a seguinte:

Tracks:

('Thunderstruck', 20)

('My Way', 15)

Nosso loop for encontra duas linhas, e cada linha é uma tupla Python com o primeiro valor
como o título e o segundo valor como o número de jogadas.
Nota: Você pode ver strings começando com u' em outros livros ou na Internet. Essa foi uma
indicação no Python2 de que as strings são strings Unicode capazes de armazenar conjuntos de
caracteres não latinos. No Python3, todas as strings são strings unicode por padrão.

No final do programa, executamos um comando SQL para EXCLUIR as linhas que acabamos de
criar para que possamos executar o programa repetidamente. O comando DELETE mostra o
uso de uma cláusula WHERE que nos permite expressar uma seleção de critérios para que
possamos pedir ao banco de dados para aplicar o comando apenas às linhas que
correspondem ao critério. Neste exemplo, o critério se aplica a todas as linhas, então
esvaziamos a tabela para que possamos executar o programa repetidamente. Depois que o
DELETE for executado, também chamamos commit() para forçar os dados a serem removidos
do banco de dados.

15.5 Resumo da Linguagem de Consulta Estruturada


Até agora, usamos a linguagem de consulta estruturada em nossos exemplos Python e cobriu
muitos dos fundamentos dos comandos SQL. Nesta seção, nós vemos a linguagem SQL em
particular e dê uma visão geral da sintaxe SQL.

Como existem tantos fornecedores de banco de dados diferentes, a linguagem de consulta


estruturada (SQL) foi padronizada para que pudéssemos nos comunicar de maneira portátil
com os sistemas banco de dados de vários fornecedores.

Um banco de dados relacional é composto de tabelas, linhas e colunas. as colunas geralmente


têm um tipo como texto, numéricos ou dados de data. Quando criamos uma tabela, indicamos
os nomes e tipos das colunas:

CREATE TABLE Tracks (title TEXT, plays INTEGER)

Para inserir uma linha em uma tabela, usamos o comando SQL INSERT:

INSERT INTO Tracks (title, plays) VALUES ('My Way', 15)

A instrução INSERT especifica o nome da tabela e, em seguida, uma lista dos campos/colunas
que você gostaria de definir na nova linha e, em seguida, a palavra-chave VALUES, uma lista de
valores correspondentes para cada um dos campos.

O comando SQL SELECT é usado para recuperar linhas e colunas de um banco de dados. A
instrução SELECT permite especificar quais colunas você gostaria de recuperar bem como uma
cláusula WHERE para selecionar quais linhas você gostaria de ver. Também permite uma
cláusula ORDER BY opcional para controlar a classificação das linhas retornadas.

SELECT * FROM Tracks WHERE title = 'My Way'


Usar * indica que você deseja que o banco de dados retorne todas as colunas para cada linha
que corresponde à cláusula WHERE.

Observe que, ao contrário do Python, em uma cláusula SQL WHERE usamos um único sinal de
igual para indicar um teste de igualdade em vez de um sinal de igual duplo. Outras operações
lógicas permitidas em uma cláusula WHERE incluem <, >, <=, >=, !=, bem como AND e OR e
parênteses para construir suas expressões lógicas.

Você pode solicitar que as linhas retornadas sejam classificadas por um dos campos da
seguinte forma:

SELECT title,plays FROM Tracks ORDER BY title

Para remover uma linha, você precisa de uma cláusula WHERE em uma instrução SQL DELETE.
A cláusula WHERE determina quais linhas devem ser excluídas:

DELETE FROM Tracks WHERE title = 'My Way'

É possível ATUALIZAR uma coluna ou colunas dentro de uma ou mais linhas em uma tabela
usando a instrução SQL UPDATE da seguinte forma:

UPDATE Tracks SET plays = 16 WHERE title = 'My Way'

A instrução UPDATE especifica uma tabela e, em seguida, uma lista de campos e valores a
serem alterados após a palavra-chave SET e, em seguida, uma cláusula WHERE opcional para
selecionar as linhas que devem ser atualizadas. Uma única instrução UPDATE mudará todas as
linhas que correspondem à cláusula WHERE. Se uma cláusula WHERE não for especificada, ela
executa o UPDATE em todas as linhas da tabela.

Esses quatro comandos SQL básicos (INSERT, SELECT, UPDATE e DELETE) permitem as quatro
operações básicas necessárias para criar e manter dados.

15.6 Spidering Twitter usando um banco de dados


Nesta seção, criaremos um programa de indexação simples que passará por contas do Twitter
e construira um banco de dados deles.

Nota: Tenha muito cuidado ao correr este programa. Você não deseja extrair muitos dados ou
executar o programa por muito tempo e acabar tendo seu acesso ao Twitter bloqueado.

Um dos problemas de qualquer tipo de programa de indexação é que ele precisa ser capaz de
ser parado e reiniciado muitas vezes e você não quer perder os dados que você recuperou até
agora. Você não quer sempre reiniciar sua recuperação de dados desde o início, então
queremos armazenar os dados à medida que os recuperamos, para que nosso programa
comece de volta e continue de onde parou.

Começaremos recuperando os amigos do Twitter de uma pessoa e seus status, repetindo


através da lista de amigos, e adicionando cada um dos amigos a um banco de dados para ser
recuperada no futuro. Depois de processar os Twitter dos amigos de uma pessoa, verificamos
em nosso banco de dados e recupere um dos amigos do amigo. Nós fazemos isso de novo e
escolhemos uma pessoa “não visitada”, recuperar sua lista de amigos e adicionar amigos.

Também rastreamos quantas vezes vimos um determinado amigo no banco de dados para
obter alguma noção de sua "popularidade".

Ao armazenar nossa lista de contas conhecidas e se recuperamos a conta ou não, e quão


popular é a conta em um banco de dados no disco do computador, podemos parar e reiniciar
nosso programa quantas vezes quisermos.

Este programa é um pouco complexo. É baseado no código do exercício anterior no e-book


que usa a API do Twitter.

Aqui está o código-fonte do nosso aplicativo de indexação do Twitter:

from urllib.request import urlopen

import urllib.error

import twurl

import json

import sqlite3

TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'

conn = sqlite3.connect('spider.sqlite')

cur = conn.cursor()

cur.execute('''

CREATE TABLE IF NOT EXISTS Twitter

(name TEXT, retrieved INTEGER, friends INTEGER)''')

while True:

acct = input('Enter a Twitter account, or quit: ')

if (acct == 'quit'): break

if (len(acct) < 1):

cur.execute('SELECT name FROM Twitter WHERE retrieved = 0 LIMIT 1')


try:

acct = cur.fetchone()[0]

except:

print('No unretrieved Twitter accounts found')

continue

url = twurl.augment(TWITTER_URL, {'screen_name': acct, 'count': '5'})

print('Retrieving', url)

connection = urlopen(url)

data = connection.read().decode()

headers = dict(connection.getheaders())

print('Remaining', headers['x-rate-limit-remaining'])

js = json.loads(data)

# Debugging

# print json.dumps(js, indent=4)

cur.execute('UPDATE Twitter SET retrieved=1 WHERE name = ?', (acct, ))

countnew = 0

countold = 0

for u in js['users']:

friend = u['screen_name']

print(friend)

cur.execute('SELECT friends FROM Twitter WHERE name = ? LIMIT 1',

(friend, ))

try:

count = cur.fetchone()[0]

cur.execute('UPDATE Twitter SET friends = ? WHERE name = ?',

(count+1, friend))

countold = countold + 1

except:
cur.execute('''INSERT INTO Twitter (name, retrieved, friends)

VALUES (?, 0, 1)''', (friend, ))

countnew = countnew + 1

print('New accounts=', countnew, ' revisited=', countold)

conn.commit()

cur.close()

# Code: http://www.pythonlearn.com/code3/twspider.py

Nosso banco de dados está armazenado no arquivo spider.sqlite3 e possui uma tabela
chamada Twitter. Cada linha na tabela do Twitter tem uma coluna para o nome da conta, se
recuperamos os amigos desta conta e quantas vezes isso conta foi "amigo".

No loop principal do programa, solicitamos ao usuário um nome de conta do Twitter ou “quit”


para sair do programa. Se o usuário entrar em uma conta do Twitter, recuperamos a lista de
amigos e status desse usuário e adicione cada amigo ao banco de dados se ainda não estiver
no banco de dados. Se o amigo já estiver na lista, adicionamos 1 ao campo friends na linha do
banco de dados.

Se o usuário pressionar enter, procuramos no banco de dados a próxima conta do Twitter que
ainda não recuperamos, recupere os amigos e status dessa conta, adicione para o banco de
dados ou atualizá-los e aumentar a contagem de amigos.

Depois de recuperar a lista de amigos e status, percorremos todos os usuários e itens no JSON
retornado e recupere o screen_name para cada usuário. Então usamos a instrução SELECT
para ver se já armazenamos esse screen_name no banco de dados e recuperamos a contagem
de amigos (friends) se o registro existe.

countnew = 0

countold = 0

for u in js['users'] :

friend = u['screen_name']

print friend

cur.execute('SELECT friends FROM Twitter WHERE name = ? LIMIT 1',

(friend, ) )

try:

count = cur.fetchone()[0]
cur.execute('UPDATE Twitter SET friends = ? WHERE name = ?',

(count+1, friend) )

countold = countold + 1

except:

cur.execute('''INSERT INTO Twitter (name, retrieved, friends)

VALUES ( ?, 0, 1 )''', ( friend, ) )

countnew = countnew + 1

print 'New accounts=',countnew,' revisited=',countold

conn.commit()

Depois que o cursor executa a instrução SELECT, devemos recuperar as linhas. Nós poderiamos
fazer isso com uma instrução for, mas como estamos recuperando apenas uma linha (LIMIT 1),
podemos usar o método fetchone() para buscar a primeira (e única) linha que é o resultado da
operação SELECT. Como fetchone() retorna a linha como uma tupla (mesmo embora haja
apenas um campo), pegamos o primeiro valor da tupla usando para obter a contagem de
amigos atual na contagem variável.

Se esta recuperação for bem-sucedida, usamos a instrução SQL UPDATE com uma cláusula
WHERE para adicionar 1 à coluna de amigos para a linha que corresponde à conta do amigo.
Observe que há dois espaços reservados (isto é, pontos de interrogação) no SQL, e o segundo
parâmetro para o execute() é uma tupla de dois elementos que contém os valores para ser
substituído no SQL no lugar dos pontos de interrogação.

Se o código no bloco try falhar, provavelmente é porque nenhum registro correspondeu a


cláusula WHERE name = ? na instrução SELECT. Então, no bloco except, nós usamos a instrução
SQL INSERT para adicionar o screen_name do amigo à tabela com uma indicação de que ainda
não recuperamos o screen_name e definimos o amigo contar até zero.

Assim, na primeira vez que o programa é executado e entramos em uma conta do Twitter, o
programa funciona da seguinte forma:

Enter a Twitter account, or quit: drchuck

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 20 revisited= 0

Enter a Twitter account, or quit: quit

Como esta é a primeira vez que executamos o programa, o banco de dados está vazio e crie o
banco de dados no arquivo spider.sqlite3 e adicione uma tabela chamada Twitter ao banco de
dados. Em seguida, recuperamos alguns amigos e os adicionamos ao banco de dados já que o
banco de dados está vazio.
Neste ponto, podemos querer escrever um descarregador de banco de dados simples para dar
uma olhada no que está em nosso arquivo spider.sqlite3:

import sqlite3

conn = sqlite3.connect('spider.sqlite')

cur = conn.cursor()

cur.execute('SELECT * FROM Twitter')

count = 0

for row in cur:

print(row)

count = count + 1

print(count, 'rows.')

cur.close()

# Code: http://www.pythonlearn.com/code3/twdump.py

Este programa simplesmente abre o banco de dados e seleciona todas as colunas de todos as
linhas na tabela Twitter, então percorre as linhas e imprime cada linha.

Se executarmos este programa após a primeira execução do nosso spider do Twitter acima, a
saída será a seguinte:

('opencontent', 0, 1)

('lhawthorn', 0, 1)

('steve_coppin', 0, 1)

('davidkocher', 0, 1)

('hrheingold', 0, 1)

...

20 rows.

Vemos uma linha para cada screen_name, que não recuperamos os dados para isso
screen_name e todos no banco de dados têm um amigo.

Agora nosso banco de dados reflete a recuperação dos amigos de nossa primeira conta no
Twitter (drchuck). Podemos executar o programa novamente e dizer a ele para recuperar os
amigos da próxima conta “não processada” simplesmente pressionando enter em vez de uma
conta do Twitter do seguinte modo:
Enter a Twitter account, or quit:

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 18 revisited= 2

Enter a Twitter account, or quit:

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 17 revisited= 3

Enter a Twitter account, or quit: quit

Como pressionamos enter (ou seja, não especificamos uma conta no Twitter), o seguinte
código é executado:

if ( len(acct) < 1 ) :

cur.execute('SELECT name FROM Twitter WHERE retrieved = 0 LIMIT 1')

try:

acct = cur.fetchone()[0]

except:

print 'No unretrieved twitter accounts found'

continue

Usamos a instrução SQL SELECT para recuperar o nome do primeiro usuário (LIMIT 1) que
ainda tem o valor "recuperamos este usuário" definido como zero. Também usamos o padrão
fetchone()[0] dentro de um bloco try/except para extrair um screen_name dos dados
recuperados ou enviar uma mensagem de erro e fazer o loop de volta.

Se recuperamos com sucesso um screen_name não processado, recuperamos seus dados


como segue:

~~ {.python{ url = twurl.augment(TWITTER_URL, {‘screen_name’: acct,

‘count’: ‘20’} ) print ‘Retrieving’, url connection = urllib.urlopen(url) data =

connection.read() js = json.loads(data)

cur.execute(‘UPDATE Twitter SET retrieved=1 WHERE name = ?’, (acct, ) ) ~~

Depois de recuperar os dados com sucesso, usamos a instrução UPDATE para definir a coluna
recuperada para 1 para indicar que concluímos a recuperação do amigos desta conta. Isso nos
impede de recuperar os mesmos dados repetidamente e nos mantém progredindo através da
rede de amigos do Twitter.

Se executarmos o programa friend e pressionarmos enter duas vezes para recuperar o


próximo não visitado friend's friends, em seguida, execute o programa de dumping, ele nos
dará a seguinte saída:

('opencontent', 1, 1)

('lhawthorn', 1, 1)

('steve_coppin', 0, 1)

('davidkocher', 0, 1)

('hrheingold', 0, 1)

...

('cnxorg', 0, 2)

('knoop', 0, 1)

('kthanos', 0, 2)

('LectureTools', 0, 1)

...

55 rows.

Podemos ver que registramos adequadamente que visitamos lhawthorn e opencontent.


Também as contas cnxorg e kthanos já têm dois seguidores. Como agora recuperamos os
amigos de três pessoas (drchuck, opencontent, e lhawthorn) nossa tabela tem 55 linhas de
amigos para recuperar.

Cada vez que executarmos o programa e pressionarmos enter, ele escolherá a próximo conta
não visitada (por exemplo, a próxima conta será steve_coppin), recupere seus amigos, marque
como recuperados, e para cada um dos amigos de steve_coppin adicione-os ao final do banco
de dados ou atualize sua contagem de amigos se eles já estiverem na base de dados.

Como os dados do programa são todos armazenados em disco em um banco de dados, a


atividade de spidering pode ser suspensa e retomada quantas vezes quiser, sem perda de
dados.
15.7 Modelagem de dados básicos
O verdadeiro poder de um banco de dados relacional é quando criamos várias tabelas e
fazemos links entre essas tabelas. O ato de decidir como dividir seus dados do aplicativo em
várias tabelas e estabelecer as relações entre as tabelas é chamado de modelagem de dados.
O documento de design que mostra as tabelas e seus relacionamentos é chamado de modelo
de dados.

A modelagem de dados é uma habilidade relativamente sofisticada e vamos apenas apresentar


os conceitos mais básicos de modelagem de dados relacionais nesta seção. Para mais detalhes
sobre modelagem de dados com a qual você pode começar:

http://en.wikipedia.org/wiki/Relational_model

Digamos que para nosso aplicativo de spider do Twitter, em vez de apenas contar os amigos,
queríamos manter uma lista de todos os novos relacionamentos para que pudéssemos
encontrar uma lista de todos que estão seguindo uma conta específica.

Como todos terão potencialmente muitas contas que os seguem, não podemos adicionar uma
única coluna à nossa tabela do Twitter. Então criamos uma nova tabela que mantém o
controle de pares de amigos. O seguinte é uma maneira simples de fazer tal table:

CREATE TABLE Pals (from_friend TEXT, to_friend TEXT)

Cada vez que encontramos uma pessoa que drchuck está seguindo, inserimos uma linha no
formulário:

INSERT INTO Pals (from_friend,to_friend) VALUES ('drchuck', 'lhawthorn')

Como estamos processando os 20 amigos do feed do drchuck no Twitter, vamos inserir 20


registros com “drchuck” como primeiro parâmetro então vamos acabar duplicando a string
muitas vezes no banco de dados.

Essa duplicação de dados de string viola uma das melhores práticas para padrões de banco de
dados. Malização que basicamente afirma que nunca devemos colocar os mesmos dados de
string no banco de dados mais de uma vez. Se precisarmos dos dados mais de uma vez,
criamos uma chave numérica para os dados e fazemos referência aos dados reais usando essa
chave.

Em termos práticos, uma string ocupa muito mais espaço do que um inteiro no disco e na
memória do nosso computador, e leva mais tempo do processador para comparar e classificar.
Se tivermos apenas algumas centenas de entradas, o armazenamento e o tempo do
processador pouco importa. Mas se tivermos um milhão de pessoas em nosso banco de dados
e a possibilidade de 100 milhões de links de amigos, é importante poder verificar os dados o
mais rápido possível.
Armazenaremos nossas contas do Twitter em uma tabela chamada Pessoas em vez da tabela
Twitter usada no exemplo anterior. A tabela Pessoas tem uma coluna adicional para
armazenar a chave numérica associada à linha desse usuário do Twitter. SQLite tem um
recurso que adiciona automaticamente o valor da chave para qualquer linha inserida em uma
tabela usando um tipo especial de coluna de dados (INTEGER PRIMARY KEY).

Podemos criar a tabela People com esta coluna id adicional da seguinte forma:

CREATE TABLE People

(id INTEGER PRIMARY KEY, name TEXT UNIQUE, retrieved INTEGER)

Observe que não estamos mais mantendo uma contagem de amigos em cada linha do People
table. Quando selecionamos INTEGER PRIMARY KEY como o tipo de nossa coluna id, estamos
indicando que gostaríamos que o SQLite gerenciasse esta coluna e atribuísse uma única chave
numérica para cada linha que inserimos automaticamente. Também adicionamos a palavra-
chave UNIQUE para indicar que não permitiremos que o SQLite insira duas linhas com o
mesmo valor para name.

Agora ao invés de criar a tabela parceiras acima, criamos uma tabela chamada Follows com
duas colunas inteiras from_id e to_id e uma restrição na tabela que a combinação de from_id
e to_id deve ser única nesta tabela (ou seja, não podemos inserir linhas duplicadas) em nosso
banco de dados.

CREATE TABLE Follows

(from_id INTEGER, to_id INTEGER, UNIQUE(from_id, to_id) )

Quando adicionamos cláusulas UNIQUE às nossas tabelas, estamos comunicando um conjunto


de regras que estamos pedindo ao banco de dados para aplicar quando tentamos inserir
registros. Estamos criando essas regras como uma conveniência em nossos programas, como
veremos em um momento. As regras nos impedem de cometer erros e tornam mais simples
escrever um pouco do nosso código.

Em essência, ao criar esta tabela Follows, estamos modelando um “relacionamento” onde uma
pessoa “segue” outra e a representa com um par de números indicados verificando que (a) as
pessoas estão conectadas e (b) a direção do relacionamento.

15.8 Programação com várias tabelas


Vamos agora refazer o programa spider do Twitter usando duas tabelas, as chaves primárias, e
as principais referências conforme descrito acima. Aqui está o código para a nova versão do
programa:
import urllib.request, urllib.parse, urllib.error

import twurl

import json

import sqlite3

TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'

conn = sqlite3.connect('friends.sqlite')

cur = conn.cursor()

cur.execute('''CREATE TABLE IF NOT EXISTS People

(id INTEGER PRIMARY KEY, name TEXT UNIQUE, retrieved INTEGER)''')

cur.execute('''CREATE TABLE IF NOT EXISTS Follows

(from_id INTEGER, to_id INTEGER, UNIQUE(from_id, to_id))''')

while True:

acct = input('Enter a Twitter account, or quit: ')

if (acct == 'quit'): break

if (len(acct) < 1):

cur.execute('SELECT id, name FROM People WHERE retrieved = 0 LIMIT 1')

try:

(id, acct) = cur.fetchone()

except:

print('No unretrieved Twitter accounts found')

continue

else:

cur.execute('SELECT id FROM People WHERE name = ? LIMIT 1',

(acct, ))

try:
id = cur.fetchone()[0]

except:

cur.execute('''INSERT OR IGNORE INTO People

(name, retrieved) VALUES (?, 0)''', (acct, ))

conn.commit()

if cur.rowcount != 1:

print('Error inserting account:', acct)

continue

id = cur.lastrowid

url = twurl.augment(TWITTER_URL, {'screen_name': acct, 'count': '5'})

print('Retrieving account', acct)

connection = urllib.request.urlopen(url)

data = connection.read().decode()

headers = dict(connection.getheaders())

print('Remaining', headers['x-rate-limit-remaining'])

js = json.loads(data)

# Depuração

# print json.dumps(js, indent=4)

cur.execute('UPDATE People SET retrieved=1 WHERE name = ?', (acct, ))

countnew = 0

countold = 0

for u in js['users']:

friend = u['screen_name']

print(friend)

cur.execute('SELECT id FROM People WHERE name = ? LIMIT 1',

(friend, ))

try:

friend_id = cur.fetchone()[0]
countold = countold + 1

except:

cur.execute('''INSERT OR IGNORE INTO People (name, retrieved)

VALUES (?, 0)''', (friend, ))

conn.commit()

if cur.rowcount != 1:

print('Error inserting account:', friend)

continue

friend_id = cur.lastrowid

countnew = countnew + 1

cur.execute('''INSERT OR IGNORE INTO Follows (from_id, to_id)

VALUES (?, ?)''', (id, friend_id))

print('New accounts=', countnew, ' revisited=', countold)

conn.commit()

cur.close()

# Code: http://www.pythonlearn.com/code3/twfriends.py

Este programa está começando a ficar um pouco complicado, mas ilustra os padrões que
precisamos usar quando estamos usando chaves inteiras para vincular tabelas. O padrões
básicos são:

1. Crie tabelas com chaves primárias e restrições.

2. Quando temos uma chave lógica para uma pessoa (ou seja, nome da conta) e precisamos
do valor id para a pessoa, dependendo se a pessoa já está ou não na tabela Pessoas,
precisamos:

(1) procurar a pessoa na tabela Pessoas e recuperar o valor id para a pessoa ou

2) adicione a pessoa ao People e obter o valor id para a linha recém-adicionada.

3. Insira a linha que captura o relacionamento “follows”.


Abordaremos cada um deles por sua vez.

15.8.1 Restrições nas tabelas do banco de dados


À medida que projetamos nossas estruturas de tabela, podemos dizer ao sistema de banco de
dados que gostaria de impor algumas regras sobre nós. Essas regras nos ajudam a não cometer
erros e introduzindo dados incorretos em nossas tabelas. Quando criamos nossas tabelas:

cur.execute('''CREATE TABLE IF NOT EXISTS People

(id INTEGER PRIMARY KEY, name TEXT UNIQUE, retrieved INTEGER)''')

cur.execute('''CREATE TABLE IF NOT EXISTS Follows

(from_id INTEGER, to_id INTEGER, UNIQUE(from_id, to_id))''')

Indicamos que a coluna nome na tabela people deve ser ÚNICA. Nós também indicamos que a
combinação dos dois números em cada linha da tabela Follows deve ser único. Essas restrições
nos impedem de cometer erros, como adicionar o mesmo relacionamento mais de uma vez.

Podemos aproveitar essas restrições no seguinte código:

cur.execute('''INSERT OR IGNORE INTO People (name, retrieved)

VALUES ( ?, 0)''', ( friend, ) )

Adicionamos a cláusula OR IGNORE à nossa instrução INSERT para indicar que, se INSERT em
particular causara uma violação da regra, o sistema de banco de dados pode ignorar o INSERT.
Estamos usando o banco de dados como uma rede de segurança para garantir que não
façamos algo incorreto inadvertidamente.

Da mesma forma, o código a seguir garante que não adicionemos exatamente o mesmo
Follows duas vezes.

cur.execute('''INSERT OR IGNORE INTO Follows

(from_id, to_id) VALUES (?, ?)''', (id, friend_id) )

Novamente, simplesmente dizemos ao banco de dados para ignorar nossa tentativa de INSERT
se isso violar a restrição de exclusividade que especificamos para as linhas Follows.
15.8.2 Recuperar e/ou inserir um registro
Quando solicitamos ao usuário uma conta no Twitter, se a conta existir, devemos procurar seu
valor id. Se a conta ainda não existir na tabela Pessoas, devemos inserir o registro e obter o
valor id da linha inserida.

Este é um padrão muito comum e é feito duas vezes no programa acima. Este código mostra
como procuramos o id da conta de um amigo quando extraímos um screen_name de um nó de
usuário no Twitter JSON recuperado.

Com o tempo será cada vez mais provável que a conta já esteja em um banco de dados,
primeiro verificamos se o registro people esta usando uma declaração um SELECT.

Se tudo correr bem dentro da seção try, recuperamos o registro usando fetchone() e, em
seguida, recuperamos o primeiro (e único) elemento da tupla retornada e armazene-a em
amigo_id.

Se o SELECT falhar, o código fetchone()[0] falhará e o controle será transferido para a seção
except.

friend = u['screen_name']

cur.execute('SELECT id FROM People WHERE name = ? LIMIT 1',

(friend, ) )

try:

friend_id = cur.fetchone()[0]

countold = countold + 1

except:

cur.execute('''INSERT OR IGNORE INTO People (name, retrieved)

VALUES ( ?, 0)''', ( friend, ) )

conn.commit()

if cur.rowcount != 1 :

print 'Error inserting account:',friend

continue

friend_id = cur.lastrowid

countnew = countnew + 1

Se terminarmos no código except, significa simplesmente que a linha não foi encontrada,
então devemos inserir a linha. Usamos INSERT OR IGNORE apenas para evitar erros e então
chame commit() para forçar o banco de dados a ser realmente atualizado. Após terminar a
gravação, podemos verificar cur.rowcount para ver quantas linhas foram afetadas. Já que
estamos tentando inserir uma única linha, se o número de linhas afetadas for outro de 1, é um
erro.

Se o INSERT for bem-sucedido, podemos examinar cur.lastrowid para descobrir qual valor o
banco de dados atribuído à coluna id em nossa linha recém-criada.

15.8.3 Armazenando o relacionamento de amigo


Assim que soubermos o valor da chave para o usuário do Twitter e o amigo no JSON, é uma
simples questão de inserir os dois números na tabela Follows com o:

cur.execute('INSERT OR IGNORE INTO Follows (from_id, to_id) VALUES (?, ?)',

(id, friend_id) )

Observe que deixamos o banco de dados cuidar de nos impedir de “inserir duas vezes” um
relacionamento criando a tabela com uma restrição de exclusividade e, em seguida,
adicionando OR IGNORE para nossa instrução INSERT.

Aqui está um exemplo de execução deste programa:

Enter a Twitter account, or quit:

No unretrieved Twitter accounts found

Enter a Twitter account, or quit: drchuck

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 20 revisited= 0

Enter a Twitter account, or quit:

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 17 revisited= 3

Enter a Twitter account, or quit:

Retrieving http://api.twitter.com/1.1/friends ...

New accounts= 17 revisited= 3

Enter a Twitter account, or quit: quit

Começamos com a conta drchuck e depois deixamos o programa escolher automaticamente as


próximas duas contas para recuperar e adicionar ao nosso banco de dados.
A seguir estão as primeiras linhas nas tabelas people e Seguidores após esta execução está
completa:

People:

(1, 'drchuck', 1)

(2, 'opencontent', 1)

(3, 'lhawthorn', 1)

(4, 'steve_coppin', 0)

(5, 'davidkocher', 0)

55 rows.

Follows:

(1, 2)

(1, 3)

(1, 4)

(1, 5)

(1, 6)

60 rows.

Você pode ver o id, nome e campos visitados na tabela people e você vê os números de ambas
as extremidades do relacionamento na tabela Follows. Na tabela pessoas, podemos ver que as
três primeiras pessoas foram visitadas e seus dados foram recuperados. Os dados na tabela
Follows indicam que drchuck (usuário 1) é um amigo para todas as pessoas mostradas nas
primeiras cinco linhas. Isso faz sentido porque os primeiros dados que recuperamos e
armazenamos foram os amigos do drchuck no Twitter. Se você fosse imprimir mais linhas da
tabela Follows, você veria os amigos dos usuários 2 e 3 também.

15.9 Três tipos de chaves


Agora que começamos a construir um modelo de dados colocando nossos dados em várias
tabelas vinculadas e vinculando as linhas nessas tabelas usando chaves, precisamos examinar
alguma terminologia sobre chaves. Existem geralmente três tipos de chaves usadas em um
modelo de banco de dados.

• Uma chave lógica é uma chave que o “mundo real” pode usar para procurar uma linha. Em
nosso modelo de dados de exemplo, o campo de nome é uma chave lógica. É o nome da tela
para o usuário e, de fato, procuramos a linha de um usuário várias vezes no programa usando
o campo nome. Frequentemente, você descobrirá que faz sentido adicionar uma ÚNICO
restrição a uma chave lógica. Como a chave lógica é como procuramos uma linha do mundo
exterior, não faz sentido permitir várias linhas com o mesmo valor na tabela.

• Uma chave primária geralmente é um número que é atribuído automaticamente pela base
de dados. Geralmente não tem significado fora do programa e é usada apenas para vincular
linhas de tabelas diferentes. Quando queremos consultar uma linha em uma tabela,
geralmente procuramos a linha usando a chave primária é a maneira mais rápida de encontrar
a linha. Como as chaves primárias são números inteiros, elas ocupam muito pouco
armazenamento e podem ser comparados ou classificados muito rapidamente. Em nossos
dados modelo, o campo id é um exemplo de chave primária.

• Uma chave estrangeira geralmente é um número que aponta para a chave primária de uma
linha associada em uma tabela diferente. Um exemplo de chave estrangeira em nossos dados
modelo é o from_id.

Estamos usando uma convenção de nomenclatura de sempre chamar o id do nome do campo


de chave primária e anexar o suffix _id a qualquer nome de campo que seja uma chave
estrangeira.

15.10 Usando JOIN para recuperar dados


Agora que seguimos as regras de normalização do banco de dados e temos dados separados
em duas tabelas, ligadas entre si usando chaves primárias e estrangeiras, precisamos para
construir um SELECT que reagrupe os dados nas tabelas.

O SQL usa a cláusula JOIN para reconectar essas tabelas. Na cláusula JOIN você especifica os
campos que são usados para reconectar as linhas entre as tabelas.

Veja a seguir um exemplo de SELECT com uma cláusula JOIN:

SELECT * FROM Follows JOIN People

ON Follows.from_id = People.id WHERE People.id = 1

A cláusula JOIN indica que os campos que estamos selecionando cruzam tanto o Follows e
tabelas people. A cláusula ON indica como as duas tabelas devem ser unidas. Pegue as linhas
de Seguidores e anexe a linha de Pessoas onde o campo from_id em Follows é o mesmo valor
de id na tabela People.

O resultado do JOIN é criar “linhas” extralongas que possuem tanto os campos de Pessoas e os
campos correspondentes de Seguidores. Onde há mais de uma correspondência entre o
campo id de People e from_id de People, então JOIN cria uma linha para cada um dos pares de
linhas correspondentes, duplicando dados conforme necessário.
O código a seguir demonstra os dados que teremos no banco de dados após o programa spider
do Twitter (acima) foi executado várias vezes.

import sqlite3

conn = sqlite3.connect('friends.sqlite')

cur = conn.cursor()

cur.execute('SELECT * FROM People')

count = 0

print('People:')

for row in cur:

if count < 5: print(row)

count = count + 1

print(count, 'rows.')

cur.execute('SELECT * FROM Follows')

count = 0

print('Follows:')

for row in cur:

if count < 5: print(row)

count = count + 1

print(count, 'rows.')

cur.execute('''SELECT * FROM Follows JOIN People

ON Follows.to_id = People.id

WHERE Follows.from_id = 2''')

count = 0

print('Connections for id=2:')

for row in cur:

if count < 5: print(row)

count = count + 1

print(count, 'rows.')

cur.close()
# Code: http://www.pythonlearn.com/code3/twjoin.py

Neste programa, primeiro descarta Pessoas e Seguidores e, em seguida, descarta um


subconjunto dos dados nas tabelas unidas.

Aqui está a saída do programa:

python twjoin.py

People:

(1, 'drchuck', 1)

(2, 'opencontent', 1)

(3, 'lhawthorn', 1)

(4, 'steve_coppin', 0)

(5, 'davidkocher', 0)

55 rows.

Follows:

(1, 2)

(1, 3)

(1, 4)

(1, 5)

(1, 6)

60 rows.

Connections for id=2:

(2, 1, 1, 'drchuck', 1)

(2, 28, 28, 'cnxorg', 0)

(2, 30, 30, 'kthanos', 0)

(2, 102, 102, 'SomethingGirl', 0)

(2, 103, 103, 'ja_Pac', 0)

20 rows.

Você vê as colunas das tabelas Pessoas e Seguidores e o último conjunto de linhas é o


resultado do SELECT com a cláusula JOIN.
Na última seleção, procuramos contas amigas de “opencontent” (ou seja, people.id=2).

Em cada uma das “metarows” na última seleção, as duas primeiras colunas são a tabela
seguida pelas colunas de três a cinco da tabela Pessoas. Você também pode ver que a segunda
coluna (Follows.to_id) corresponde à terceira coluna (People.id) em cada uma das “metarows”
unidas.

15.11 Resumo
Este capítulo cobriu muito terreno para lhe dar uma visão geral dos fundamentos usando um
banco de dados em Python. É mais complicado escrever o código para usar um banco de dados
para armazenar dados do que dicionários Python ou arquivos simples, portanto, há poucos
motivos para usar um banco de dados, a menos que seu aplicativo realmente precise dos
recursos de um banco de dados. As situações em que um banco de dados pode ser bastante
útil são:

(1) quando seu aplicativo precisa fazer muitas pequenas atualizações aleatórias dentro de um
grande conjunto de dados.

(2) quando seus dados são tão grandes que não cabem em um dicionário e você precisa
procurar informações repetidamente.

(3) quando você tem um processo de execução longo e que deseja parar e reiniciar e reter os
dados de uma execução.

Você pode construir um banco de dados simples com uma única tabela para atender a muitas
necessidades de aplicativos, mas a maioria dos problemas exigirá várias tabelas e
links/relacionados entre as linhas em tabelas diferentes. Quando você começa a fazer links
entre tabelas, é importante fazer um design cuidadoso e siguir as regras de normalização do
banco de dados para fazer o melhor uso dos recursos do banco de dados. Uma vez que a
principal motivação para o uso de banco de dados é que você tem uma grande quantidade de
dados para lidar, é importante modelar seus dados com eficiência para que seus programas
sejam executados o mais rápido possível.

15.12 Depuração
Um padrão comum quando você está desenvolvendo um programa Python para se conectar a
um banco de dados SQLite será executar um programa Python e verificar os resultados usando
o navegador de banco de dados para SQLite. O navegador permite que você verifique
rapidamente para ver se o seu programa está funcionando corretamente.

Você deve ter cuidado porque o SQLite para evitar que dois programas mudem os mesmos
dados ao mesmo tempo. Por exemplo, se você abrir um banco de dados no navegador e fazer
uma alteração no banco de dados e ainda não pressionou o botão “salvar” no navegador, o
navegador “bloqueia” o arquivo de banco de dados e mantém qualquer outro programa
acessar o arquivo. Em particular, seu programa Python não será capaz de acessar o arquivo se
ele estiver bloqueado.

Portanto, uma solução é certificar-se de fechar o navegador do banco de dados ou usar o


Menu Arquivo para fechar o banco de dados no navegador antes de tentar acessar o banco de
dados do Python para evitar o problema de seu código Python falhar porque o banco de dados
está bloqueado.

15.13 Glossário
Atributo - Um dos valores dentro de uma tupla. Mais comumente chamado de “coluna” ou
"campo".

Restrição - Quando dizemos ao banco de dados para impor uma regra em um campo ou uma
linha em um table. Uma restrição comum é insistir que não pode haver valores duplicados em
um campo específico (ou seja, todos os valores devem ser exclusivos).

Cursor - Um cursor permite executar comandos SQL em um banco de dados e recuperar dados
do banco de dados. Um cursor é semelhante a um soquete ou identificador de arquivo para
conexões de rede e arquivos, respectivamente.

Navegador de banco de dados - Um software que permite conectar-se diretamente a um


banco de dados e manipular o banco de dados diretamente sem escrever um programa.

Chave estrangeira - Uma chave numérica que aponta para a chave primária de uma linha em
outra mesa. Chaves estrangeiras estabelecem relacionamentos entre linhas armazenadas em
diferentes tabelas.

Índice - Dados adicionais que o software de banco de dados mantém como linhas e inserções
em uma tabela para fazer pesquisas muito rapidamente.

Chave lógica - Uma chave que o “mundo externo” usa para procurar uma determinada linha.
exemplo, em uma tabela de contas de usuário, o endereço de e-mail de uma pessoa pode ser
uma boa candidata como a chave lógica para os dados do usuário.

Normalização - Projetar um modelo de dados para que nenhum dado seja replicado. nós
armazenamos cada item de dados em um local no banco de dados e referenciamos em outro
lugar usando uma chave estrangeira.

Chave primária - Uma chave numérica atribuída a cada linha que é usada para se referir a uma
linha em uma mesa de outra mesa. Frequentemente, o banco de dados é configurado para
automatizar chaves primárias conforme as linhas são inseridas.

Relação - Uma área dentro de um banco de dados que contém tuplas e atributos. Mais
normalmente chamado de “tabela”.

Tupla - Uma única entrada em uma tabela de banco de dados que é um conjunto de atributos.
Mais tipicamente chamado de “linha”.
Capítulo 16
Visualizando dados
Até agora, aprendemos a linguagem Python e depois aprendemos como usar o Python, a rede
e bancos de dados para manipular dados.

Neste capítulo, vamos dar uma olhada em três aplicativos completos que trazem todos essas
coisas juntas para gerenciar e visualizar dados. Você pode usar esses aplicativos como código
de amostra para ajudá-lo a começar a resolver um problema do mundo real.

Cada um dos aplicativos é um arquivo ZIP que você pode baixar e extrair para o seu
computador e executa-lo.

16.1 Construindo um mapa do Google a partir de dados


geocodificados
Neste projeto, estamos usando a API de geocodificação do Google para inserir as localizações
geográficas dos nomes das universidades e, em seguida, colocar os dados em um Mapa do
Google.

Para começar, baixe o aplicativo em:

www.pythonlearn.com/code3/geodata.zip

O primeiro problema a resolver é que a API gratuita de geocodificação do Google tem uma
taxa limitada para um determinado número de solicitações por dia. Se você tiver muitos
dados, talvez seja necessário parar e reiniciar o processo de pesquisa várias vezes. Então
dividimos o problema em duas fases.

Na primeira fase, pegamos nossos dados de “pesquisa” de entrada no arquivo where.data e os


lemos uma linha por vez, e recuperamos as informações geocodificadas do Google e
armazenamos em um banco de dados geodata.sqlite. Antes de usarmos a API de
geocodificação para cada usuário inserindo a localização, simplesmente verificamos se já
temos os dados dessa linha específica de entrada. O banco de dados está funcionando como
um “cache” local de nossos dados de geocodificação para certificar de que nunca solicitamos
os mesmos dados ao Google duas vezes.

Você pode reiniciar o processo a qualquer momento removendo o arquivo geodata.sqlite.

Execute o programa geoload.py. Este programa lerá as linhas de entrada em where.data e para
cada linha verifique se ela já está no banco de dados. Se não temos os dados para o local, ele
chamará a API de geocodificação para recuperar os dados e armazenar no banco de dados.

Aqui está uma amostra executada após já haver alguns dados no banco de dados:
Found in database Northeastern University

Found in database University of Hong Kong, ...

Found in database Technion

Found in database Viswakarma Institute, Pune, India

Found in database UMD

Found in database Tufts University

Resolving Monash University

Retrieving http://maps.googleapis.com/maps/api/

geocode/json?sensor=false&address=Monash+University

Retrieved 2063 characters { "results" : [

{'status': 'OK', 'results': ... }

Resolving Kokshetau Institute of Economics and Management

Retrieving http://maps.googleapis.com/maps/api/

geocode/json?sensor=false&address=Kokshetau+Inst ...

Retrieved 1749 characters { "results" : [

{'status': 'OK', 'results': ... }

...

Os cinco primeiros locais já estão no banco de dados e, portanto, são ignorados. O programa
varre até o ponto em que encontra novos locais e começa a recuperá-los.

O programa geoload.py pode ser interrompido a qualquer momento, e há um contador que


você pode usar para limitar o número de chamadas para a API de geocodificação para cada
execução. Dado que where.data tem apenas algumas centenas de itens de dados, você não
deve se deparar com o limite de taxa diária, mas se você tiver mais dados, pode levar várias
execuções em vários dias para que seu banco de dados tenha todos os dados geocodificados
para sua entrada.

Depois de carregar alguns dados em geodata.sqlite, você pode visualizar os dados usando o
programa geodump.py. Este programa lê o banco de dados e escreve o arquivo where.js com a
localização, latitude e longitude na forma executável Código JavaScript.

Uma execução do programa geodump.py é a seguinte:

Northeastern University, ... Boston, MA 02115, USA 42.3396998 -71.08975

Bradley University, 1501 ... Peoria, IL 61625, USA 40.6963857 -89.6160811

...
Technion, Viazman 87, Kesalsaba, 32000, Israel 32.7775 35.0216667

Monash University Clayton ... VIC 3800, Australia -37.9152113 145.134682

Kokshetau, Kazakhstan 53.2833333 69.3833333

...

12 records written to where.js

Open where.html to view the data in a browser

O arquivo where.html consiste em HTML e JavaScript para visualizar um mapa do Google. Ele
lê os dados mais recentes em where.js para obter os dados a serem visualizados. Aqui está o
formato do arquivo where.js:

myData = [

[42.3396998,-71.08975, 'Northeastern Uni ... Boston, MA 02115'],

[40.6963857,-89.6160811, 'Bradley University, ... Peoria, IL 61625, USA'],

[32.7775,35.0216667, 'Technion, Viazman 87, Kesalsaba, 32000, Israel'],

...

];

Esta é uma variável JavaScript que contém uma lista de listas. A sintaxe para listar constantes
no JavaScript é muito semelhante ao Python, então a sintaxe deve ser familiar para você.

Basta abrir where.html em um navegador para ver os locais. Você pode passar o mouse sobre
cada alfinete do mapa para encontrar o local que a API de geocodificação retornou para a
entrada do usuário inserido. Se você não conseguir ver nenhum dado ao abrir o arquivo
where.html, talvez tenha que verificar o JavaScript ou o console do desenvolvedor do seu
navegador.

16.2 Visualização de redes e interconexões


Nesta aplicação, realizaremos algumas das funções de um buscador. Nós primeiro
rastrearemos um pequeno subconjunto da web e executaremos uma versão simplificada do
algoritmo de classificação de página do Google para determinar quais páginas são mais
conectadas, e, em seguida, visualize a classificação da página e a conectividade do nosso
pequeno canto da web. Usaremos a biblioteca de visualização D3 JavaScript http://d3js.org/
para produzir a saída de visualização.

Você pode baixar e extrair este aplicativo de:

www.pythonlearn.com/code3/pagerank.zip
O primeiro programa (spider.py) rastreia um site e extrai uma série de páginas no banco de
dados (spider.sqlite), registrando os links entre as páginas. você pode reiniciar o processo a
qualquer momento removendo o arquivo spider.sqlite e executando novamente spider.py.

Enter web url or enter: http://www.dr-chuck.com/

['http://www.dr-chuck.com']

How many pages:2

1 http://www.dr-chuck.com/ 12

2 http://www.dr-chuck.com/csev-blog/ 57

How many pages:

Neste exemplo de execução, pedimos que ele rastreie um site e recupere duas páginas. Se
você reiniciar o programa e diger a ele para rastrear mais páginas, ele não rastreará
novamente nenhuma página, já está no banco de dados. Ao reiniciar, ele vai para uma página
aleatória não rastreada e começara aí. Portanto, cada execução sucessiva de spider.py é
aditiva.

Enter web url or enter: http://www.dr-chuck.com/

['http://www.dr-chuck.com']

How many pages:3

3 http://www.dr-chuck.com/csev-blog 57

4 http://www.dr-chuck.com/dr-chuck/resume/speaking.htm 1

5 http://www.dr-chuck.com/dr-chuck/resume/index.htm 13

How many pages:

Você pode ter vários pontos de partida no mesmo banco de dados - dentro do programa, estes
são chamados de “teias”. A aranha escolhe aleatoriamente entre todos os links não visitados
em todas as teias como a próxima página para spider.

Se você deseja despejar o conteúdo do arquivo spider.sqlite, pode executar spdump.py do


seguinte modo:

(5, None, 1.0, 3, 'http://www.dr-chuck.com/csev-blog')

(3, None, 1.0, 4, 'http://www.dr-chuck.com/dr-chuck/resume/speaking.htm')

(1, None, 1.0, 2, 'http://www.dr-chuck.com/csev-blog/')


(1, None, 1.0, 5, 'http://www.dr-chuck.com/dr-chuck/resume/index.htm')

4 rows.

Isso mostra o número de links recebidos, a classificação da página antiga, a nova classificação
da página, o id da página e o url da página. O programa spdump.py mostra apenas as páginas
que tenham pelo menos um link de entrada para elas.

Depois de ter algumas páginas no banco de dados, você pode executar o ranking da página nas
páginas usando o programa sprank.py. Você simplesmente informa quantas iterações de
classificação de página correrão.

How many iterations:2

1 0.546848992536

2 0.226714939664

[(1, 0.559), (2, 0.659), (3, 0.985), (4, 2.135), (5, 0.659)]

Você pode despejar o banco de dados novamente para ver se o page rank foi atualizado:

(5, 1.0, 0.985, 3, 'http://www.dr-chuck.com/csev-blog')

(3, 1.0, 2.135, 4, 'http://www.dr-chuck.com/dr-chuck/resume/speaking.htm')

(1, 1.0, 0.659, 2, 'http://www.dr-chuck.com/csev-blog/')

(1, 1.0, 0.659, 5, 'http://www.dr-chuck.com/dr-chuck/resume/index.htm')

4 rows.

Você pode executar sprank.py quantas vezes quiser e ele simplesmente refinará a página rank
cada vez que você executá-lo. Você pode até executar sprank.py algumas vezes e depois ir de
spider mais algumas páginas sith spider.py e, em seguida, execute sprank.py para reconverter
os valores de classificação de página. Um mecanismo de pesquisa geralmente executa o
rastreamento e a classificação programas o tempo todo.

Se você deseja reiniciar os cálculos de classificação de página sem respeitá-los nas páginas da
web, você pode usar spreset.py e reiniciar sprank.py.

How many iterations:50

1 0.546848992536

2 0.226714939664
3 0.0659516187242

4 0.0244199333

5 0.0102096489546

6 0.00610244329379

...

42 0.000109076928206

43 9.91987599002e-05

44 9.02151706798e-05

45 8.20451504471e-05

46 7.46150183837e-05

47 6.7857770908e-05

48 6.17124694224e-05

49 5.61236959327e-05

50 5.10410499467e-05

[(512, 0.0296), (1, 12.79), (2, 28.93), (3, 6.808), (4, 13.46)]

Para cada iteração do algoritmo de classificação de página, ele imprime a alteração média de
classificação página por página. A rede inicialmente é bastante desbalanceada e assim os
valores de classificação da página mudam muito entre as iterações. Mas em algumas iterações
curtas, o page rank converge. Você deve executar o prank.py por tempo suficiente para que a
classificação da página convergam.

Se você deseja visualizar as principais páginas atuais em termos de classificação de página,


execute spjson.py para ler o banco de dados e gravar os dados para as páginas mais vinculadas
em formato JSON para ser visualizada em um navegador da web.

Creating JSON output on spider.json...

How many nodes? 30

Open force.html in a browser to view the visualization

Você pode visualizar esses dados abrindo o arquivo force.html em seu navegador da web. Esse
mostra um layout automático dos nós e links. Você pode clicar e arrastar qualquer nó e você
também pode clicar duas vezes em um nó para encontrar a URL que é representada pelo nó.

Se você executar novamente os outros utilitários, execute novamente spjson.py e pressione


atualizar no navegador para obter os novos dados de spider.json.
16.3 Visualizando dados de e-mail
Até este ponto do e-book, você se familiarizou bastante com nosso mbox-arquivos de dados
short.txt e mbox.txt. Agora é hora de fazer nossa análise de dados de e-mail para o próximo
nível.

No mundo real, às vezes você precisa extrair dados de e-mail dos servidores. Que pode levar
algum tempo e os dados podem ser inconsistentes, cheios de erros e precisa de muita limpeza
ou ajuste. Nesta seção, trabalhamos com um aplicativo que é o mais complexo até agora e
extrai quase um gigabyte de dados e visualize-o.

Você pode baixar este aplicativo em:

www.pythonlearn.com/code3/gmane.zip

Usaremos dados de um serviço gratuito de arquivamento de listas de e-mail chamado


www.gmane.org. Este serviço é muito popular entre os projetos de código aberto porque
fornece um bom arquivo pesquisável de sua atividade de e-mail. Eles também têm uma
política muito liberal sobre acessar seus dados por meio de sua API. Eles não têm limites de
taxa, mas peço que você não sobrecarregue o serviço deles e leve apenas os dados
necessários. Você pode ler os termos e condições do gmane nesta página:

http://gmane.org/export.php

É muito importante que você faça uso responsável dos dados gmane.org adicionando
atrasos no seu acesso aos serviços deles e distribuição de trabalhos de longa duração por um
período de tempo mais longo. Não abuse deste serviço gratuito e estrague-o para o resto de
nós.

Quando os dados de e-mail Sakai foram rastreados usando este software, produziu quase um
Gigabyte de dados e fez várias execuções em vários dias. O arquivo README.txt no ZIP acima
pode ter instruções sobre como você pode baixar um arquivo pre-spidered, cópia do arquivo
content.sqlite para a maioria do corpos de e-mail Sakai para que você não tenha que arranhas
por cinco dias para executar os programas. Se você baixar o pré-conteúdo spidered, você
ainda deve executar o processo de spidering para acompanhar mais mensagens recentes.

O primeiro passo é rastrear o repositório gmane. A URL base é codificada no gmane.py e é


codificado para a lista de desenvolvedores do Sakai. Você pode arranha outro repositório
alterando esse URL base. Certifique-se de excluir o arquivo content.sqlite se você muda o URL
base.

O arquivo gmane.py opera como um spider de cache responsável, pois é executado


lentamente e recupera uma mensagem de e-mail por segundo para evitar ser estrangulado
pelo gmane. Ele armazena todos os seus dados em um banco de dados e pode ser
interrompido e reiniciado quantas vezes for necessário. Pode levar muitas horas para extrair
todos os dados. Então você pode precisar reiniciar várias vezes.

Aqui está uma execução de gmane.py recuperando as últimas cinco mensagens do


desenvolvedor Sakai:
http://download.gmane.org/gmane.comp.cms.sakai.devel/51410/51411 9460

nealcaidin@sakaifoundation.org 2013-04-05 re: [building ...

http://download.gmane.org/gmane.comp.cms.sakai.devel/51411/51412 3379

samuelgutierrezjimenez@gmail.com 2013-04-06 re: [building ...

http://download.gmane.org/gmane.comp.cms.sakai.devel/51412/51413 9903

da1@vt.edu 2013-04-05 [building sakai] melete 2.9 oracle ...

http://download.gmane.org/gmane.comp.cms.sakai.devel/51413/51414 349265

m.shedid@elraed-it.com 2013-04-07 [building sakai] ...

http://download.gmane.org/gmane.comp.cms.sakai.devel/51414/51415 3481

samuelgutierrezjimenez@gmail.com 2013-04-07 re: ...

http://download.gmane.org/gmane.comp.cms.sakai.devel/51415/51416 0

O programa verifica content.sqlite de um até o primeiro número de mensagem não rastreada


e começa a rastrear essa mensagem. Ele continua arranhando até rastrear o número de
mensagens desejada ou chegar a uma página que não pareça ser uma mensagem formatada
corretamente.

Às vezes gmane.org está faltando uma mensagem. Talvez os administradores possam deletar
mensagens ou talvez se percam. Se sua spider parar, e parecer que atingiu uma mensagem
ausente, vá para o SQLite Manager e adicione uma linha com a mensagem ausente deixando
todos os outros campos em branco e reinicie gmane.py. Isso vai descolar o processo de
spidering e permitir que ele continue. Essas mensagens vazias serão ignoradas na próxima fase
do processo.

Uma coisa boa é que depois de ter rastreado todas as mensagens e tê-las em content.sqlite,
você pode executar gmane.py novamente para obter novas mensagens conforme elas são
enviadas à lista.

Os dados content.sqlite são bastante brutos, com um modelo de dados ineficiente, e não
comprimido. Isso é intencional, pois permite que você veja content.sqlite no SQLite Manager
para depurar problemas com o processo de spidering. Seria uma ma ideia executar quaisquer
consultas nesse banco de dados, pois elas seriam muito lentas.

O segundo processo é rodar o programa gmodel.py. Este programa lê o raw dados de


content.sqlite e produz uma versão limpa e bem modelada de dados no arquivo index.sqlite.
Este arquivo será muito menor (geralmente 10X menor) do que content.sqlite porque também
comprime o cabeçalho e o corpo do texto.

Cada vez que gmodel.py é executado, ele exclui e reconstrói index.sqlite, permitindo que você
ajuste seus parâmetros e edite as tabelas de mapeamento em content.sqlite para ajustar os
dados de processo de limpeza. Este é um exemplo de execução de gmodel.py. Imprime uma
linha a cada vez, 250 mensagens de e-mail são processadas para que você possa ver algum
progresso acontecendo, pois isso o programa pode ser executado por um tempo processando
quase um Gigabyte de dados de e-mail.
Loaded allsenders 1588 and mapping 28 dns mapping 1

1 2005-12-08T23:34:30-06:00 ggolden22@mac.com

251 2005-12-22T10:03:20-08:00 tpamsler@ucdavis.edu

501 2006-01-12T11:17:34-05:00 lance@indiana.edu

751 2006-01-24T11:13:28-08:00 vrajgopalan@ucmerced.edu

O programa gmodel.py lida com várias tarefas de limpeza de dados.

Os nomes de domínio são truncados em dois níveis para .com, .org, .edu e .net. Outro nomes
de domínio são truncados em três níveis. Então si.umich.edu se torna umich.edu e
caret.cam.ac.uk torna-se cam.ac.uk. Endereços de e-mail também são forçados a diminuir
caso, e alguns dos endereços @gmane.org como o seguinte:

arwhyte-63aXycvo3TyHXe+LvDLADg@public.gmane.org

São convertidos para o endereço real sempre que houver um endereço de e-mail real
correspondente em outra parte do corpo da mensagem.

No banco de dados content.sqlite existem duas tabelas que permitem mapear ambos nomes
de domínio e endereços de e-mail individuais que mudam ao longo da vida da lista de e-mail.
Por exemplo, Steve Githens usou os seguintes endereços de e-mail ao empregos alterados ao
longo da vida da lista de desenvolvedores Sakai:

s-githens@northwestern.edu

sgithens@cam.ac.uk

swgithen@mtu.edu

Podemos adicionar duas entradas à tabela Mapping em content.sqlite para que gmodel.py
mapeie todos os três para um endereço

s-githens@northwestern.edu -> swgithen@mtu.edu

sgithens@cam.ac.uk -> swgithen@mtu.edu

Você também pode fazer entradas semelhantes na tabela DNSMapping se houver vários
Nomes DNS que você deseja mapear para um único DNS. O seguinte mapeamento foi
adicionado aos dados de Sakai:
iupui.edu -> indiana.edu

portanto, todas as contas dos vários campos da Universidade de Indiana são rastreadas juntas.

Você pode reexecutar o gmodel.py várias vezes enquanto olha para os dados e adicionar
mapeamentos para tornar os dados cada vez mais limpos. Quando terminar, você terá uma
versão bem indexada do e-mail em index.sqlite. Este é o arquivo a ser usado para fazer análise
de dados. Com este arquivo, a análise de dados será muito rápida.

A primeira e mais simples análise de dados é determinar “quem enviou mais e-mails?” e “qual
organização enviou mais correspondências”? Isso é feito usando gbasic.py:

How many to dump? 5

Loaded messages= 51330 subjects= 25033 senders= 1584

Top 5 Email list participants

steve.swinsburg@gmail.com 2657

azeckoski@unicon.net 1742

ieb@tfd.co.uk 1591

csev@umich.edu 1304

david.horwitz@uct.ac.za 1184

Top 5 Email list organizations

gmail.com 7339

umich.edu 6243

uct.ac.za 2451

indiana.edu 2258

unicon.net 2055

Observe como gbasic.py é executado muito mais rapidamente em comparação com gmane.py
ou mesmo gmodel.py. Eles estão todos trabalhando nos mesmos dados, mas gbasic.py está
usando os dados compactados e normalizados em index.sqlite. Se você tiver muitos dados
para gerenciar, um processo de várias etapas como o deste aplicativo pode demorar um pouco
mais tempo para desenvolver, mas economizará muito tempo quando você realmente
começar a explorar e visualizar seus dados.

Você pode produzir uma visualização simples da frequência da palavra nas linhas de assunto
no arquivo gword.py:

Range of counts: 33229 129


Output written to gword.js

Isso produz o arquivo gword.js que você pode visualizar usando gword.htm para produzir uma
nuvem de palavras semelhante à do início desta seção.

Uma segunda visualização é produzida por gline.py. Ele calcula a participação de e-mail por
organizações ao longo do tempo.

Loaded messages= 51330 subjects= 25033 senders= 1584

Top 10 Oranizations

['gmail.com', 'umich.edu', 'uct.ac.za', 'indiana.edu',

'unicon.net', 'tfd.co.uk', 'berkeley.edu', 'longsight.com',

'stanford.edu', 'ox.ac.uk']

Output written to gline.js

Sua saída é gravada em gline.js, que é visualizada usando gline.htm.

Este é um aplicativo relativamente complexo e sofisticado e possui recursos para fazer


algumas recuperação, limpeza e visualizações de dados reais.

Você também pode gostar