diff --git a/.gitignore b/.gitignore
index 72364f9..b34c01a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,3 +87,5 @@ ENV/
# Rope project settings
.ropeproject
+
+tmp/
diff --git a/ERRATA.md b/ERRATA.md
index e12a5ca..8ce993b 100644
--- a/ERRATA.md
+++ b/ERRATA.md
@@ -42,15 +42,6 @@ Parágrafo 5: "Se quiser usar operadores de lista e métodos", deveria ser "Se q
## Capítulo 14
-## 14.2 - Leitura e escrita
-
-Parágrafo 2: "é preciso abri-lo com o modo `'wt'`", porém em Python 3 o modo correto para escrever arquivos texto é `'wt'`. Esta mudança precisa ser feita também no código logo após este parágrago, que fica assim:
-
-```python
->>> fout = open('output.txt', 'wt')
-```
-
-Nota: este erro existe também no original e eu (@ramalho) já o reportei no site da O'Reilly.
## 14.5 - Captura de exceções
@@ -74,3 +65,17 @@ O título desta seção no original é "Writing modules"; no contexto, a traduç
### 15.1 - Tipos definidos pelos programadores
Parágrafo 2, "(x, y) representa o ponto que está x unidades à direita e y unidades a partir da origem." deveria ser "(x, y) representa o ponto que está x unidades à direita e y unidades acima da origem."
+
+## Capítulo 18
+
+### 18.11 - Glossário
+
+A definição do termo __multiplicidade__ está confusa. Em vez de "quantas referências dela são instâncias de outra classe." é melhor "quantas referências a instâncias da outra classe podem existir."
+
+## Capítulo 19
+
+### 19.3 - Expressões geradoras
+
+Parágrafo 4, a exceção mencionada está como "StopException", mas o correto é "StopIteration".
+
+Nota: este erro existe também no original e eu (@ramalho) já o reportei no site da O'Reilly.
diff --git a/docs/00-prefacio.md b/docs/00-prefacio.md
index 22e7cb8..c3a7a91 100644
--- a/docs/00-prefacio.md
+++ b/docs/00-prefacio.md
@@ -46,9 +46,9 @@ Esta edição de _Pense em Python_ tem as seguintes novidades:
* Acrescentei algumas seções e mais detalhes sobre a web, para ajudar os principiantes a executar o Python em um navegador, e não ter que lidar com a instalação do programa até que seja necessário.
-* Para “Módulo turtle” na página 63 troquei meu próprio pacote gráfico turtle, chamado Swampy, para um módulo Python mais padronizado, turtle, que é mais fácil de instalar e mais eficiente.
+* Para [“Módulo turtle”](./04-caso-interface.md#42---módulo-turtle) troquei meu próprio pacote gráfico turtle, chamado Swampy, para um módulo Python mais padronizado, turtle, que é mais fácil de instalar e mais eficiente.
-* Acrescentei um novo capítulo chamado “Extra”, que introduz alguns recursos adicionais do Python, não estritamente necessários, mas às vezes práticos.
+* Acrescentei um novo capítulo chamado [“Extra”](./19-extra.md), que introduz alguns recursos adicionais do Python, não estritamente necessários, mas às vezes práticos.
Espero que goste de trabalhar com este livro, e que ele o ajude a aprender a programar e pensar, pelo menos um pouquinho, como um cientista da computação.
@@ -81,7 +81,7 @@ Exibe textos que devem ser substituídos por valores fornecidos pelos usuários
## Uso do código dos exemplos (de acordo com a política da O'Reilly)
-Há material suplementar (exemplos de código, exercícios etc.) disponível para baixar em http://www.greenteapress.com/thinkpython2/code.
+Há material suplementar (exemplos de código, exercícios etc.) disponível para baixar em [http://www.greenteapress.com/thinkpython2/code](http://www.greenteapress.com/thinkpython2/code).
Este livro serve para ajudar você a fazer o que precisa. Em geral, se o livro oferece exemplos de código, você pode usá-los nos seus programas e documentação. Não é preciso entrar em contato conosco para pedir permissão, a menos que esteja reproduzindo uma porção significativa do código. Por exemplo, escrever um programa que use vários pedaços de código deste livro não exige permissão. Vender ou distribuir um CD-ROM de exemplos dos livros da O’Reilly exige permissão. Responder a uma pergunta citando este livro e reproduzindo exemplos de código não exige permissão. Incorporar uma quantidade significativa de exemplos de código deste livro na documentação do seu produto exige permissão.
@@ -97,13 +97,13 @@ Temos uma página web para este livro na qual incluímos erratas, exemplos e qua
* Página da edição em português
- http://www.novatec.com.br/catalogo/7522508-pense-em-python
+ [http://www.novatec.com.br/catalogo/7522508-pense-em-python](http://www.novatec.com.br/catalogo/7522508-pense-em-python)
* Página da edição original em inglês
- http://bit.ly/think-python_2E
+ [http://bit.ly/think-python_2E](http://bit.ly/think-python_2E)
-Para obter mais informações sobre os livros da Novatec, acesse nosso site em http://www.novatec.com.br.
+Para obter mais informações sobre os livros da Novatec, acesse nosso site em [http://www.novatec.com.br](http://www.novatec.com.br).
## Agradecimentos
diff --git a/docs/02-vars-expr-instr.md b/docs/02-vars-expr-instr.md
index 4b2daa7..0be2e03 100644
--- a/docs/02-vars-expr-instr.md
+++ b/docs/02-vars-expr-instr.md
@@ -30,7 +30,7 @@ O caractere de sublinhar (\_) pode aparecer em um nome. Muitas vezes é usado em
Se você der um nome ilegal a uma variável, recebe um erro de sintaxe:
-```
+```python
>>> 76trombones = 'big parade'
SyntaxError: invalid syntax
>>> more@ = 1000000
diff --git a/docs/09-caso-palavras.md b/docs/09-caso-palavras.md
index 26ba080..31c13c6 100644
--- a/docs/09-caso-palavras.md
+++ b/docs/09-caso-palavras.md
@@ -88,7 +88,7 @@ Escreva uma função chamada `is_abecedarian` que retorne `True` se as letras nu
## 9.3 - Busca
-Todos os exercícios na seção anterior têm algo em comum; eles podem ser resolvidos com o modelo de busca que vimos em “Buscando”, na página 123. O exemplo mais simples é:
+Todos os exercícios na seção anterior têm algo em comum; eles podem ser resolvidos com o modelo de busca que vimos em [Buscando](./08-strings.md#86---buscando). O exemplo mais simples é:
```python
def has_no_e(word):
diff --git a/docs/14-arquivos.md b/docs/14-arquivos.md
index 4108544..607aa0f 100644
--- a/docs/14-arquivos.md
+++ b/docs/14-arquivos.md
@@ -18,10 +18,10 @@ Uma alternativa é armazenar o estado do programa em um banco de dados. Neste ca
Um arquivo de texto é uma sequência de caracteres armazenados em um meio permanente como uma unidade de disco rígido, pendrive ou CD-ROM. Vimos como abrir e ler um arquivo em “Leitura de listas de palavras” na página 133.
-Para escrever um arquivo texto, é preciso abri-lo com o modo `'wt'` como segundo parâmetro:
+Para escrever um arquivo texto, é preciso abri-lo com o modo `'w'` como segundo parâmetro:
```python
->>> fout = open('output.txt', 'wt')
+>>> fout = open('output.txt', 'w')
```
Se o arquivo já existe, abri-lo em modo de escrita elimina os dados antigos e começa tudo de novo, então tenha cuidado! Se o arquivo não existir, é criado um arquivo novo.
diff --git a/docs/18-heranca.md b/docs/18-heranca.md
index 9cd378e..89b75b4 100644
--- a/docs/18-heranca.md
+++ b/docs/18-heranca.md
@@ -8,7 +8,7 @@ Os exemplos de código deste capítulo estão disponíveis em http://thinkpython
## 18.1 - Objetos Card
-Há 52 cartas em um baralho, cada uma das quais pertencendo a 1 dos 4 naipes e a 1 dos 13 valores. Os naipes são espadas, copas, ouros e paus (no bridge, em ordem descendente). A ordem dos valores é ás, 2, 3, 4, 5, 6, 7, 8, 9, 10, valete, dama e rei. Dependendo do jogo que estiver jogando, um ás pode ser mais alto que o rei ou mais baixo que 2.
+Há 52 cartas em um baralho, e cada uma pertence a 1 dos 4 naipes e a 1 dos 13 valores. Os naipes são espadas, copas, ouros e paus (no bridge, em ordem descendente). A ordem dos valores é ás, 2, 3, 4, 5, 6, 7, 8, 9, 10, valete, dama e rei. Dependendo do jogo que estiver jogando, um ás pode ser mais alto que o rei ou mais baixo que 2.
Se quiséssemos definir um novo objeto para representar uma carta de jogo, os atributos óbvios seriam rank (valor) e suit (naipe). Mas não é tão óbvio qual tipo de atributo deveriam ser. Uma possibilidade é usar strings com palavras como 'Spade' (Espadas) para naipes e 'Queen' (Dama) para valores. Um problema com esta implementação é que não seria fácil comparar cartas para ver qual valor ou naipe tem classificação mais alta em relação aos outros.
@@ -16,187 +16,172 @@ Uma alternativa é usar números inteiros para codificar os valores e os naipes.
Por exemplo, esta tabela mostra os naipes e os códigos de número inteiro correspondentes:
-Spades (Espadas) ↦ 3
-Hearts (Copas) ↦ 2
-
-Diamonds (Ouros) ↦ 1
-
-Clubs (Paus) ↦ 0
+```
+Spades (Espadas) ↦ 3
+Hearts (Copas) ↦ 2
+Diamonds (Ouros) ↦ 1
+Clubs (Paus) ↦ 0
+```
Este código facilita a comparação entre as cartas; como naipes mais altos mapeiam a números mais altos, podemos comparar naipes aos seus códigos.
O mapeamento de valores é até óbvio; cada um dos valores numéricos é mapeado ao número inteiro correspondente, e para cartas com figuras:
-Jack (Valete) ↦ 11
+```
+Jack (Valete) ↦ 11
Queen (Dama) ↦ 12
+King (Rei) ↦ 13
+```
-King (Rei) ↦ 13
-
-Estou usando o símbolo ↦ para deixar claro que esses mapeamentos não são parte do programa em Python. Eles são parte do projeto do programa, mas não aparecem explicitamente no código.
+Estou usando o símbolo `↦` para deixar claro que esses mapeamentos não são parte do programa em Python. Eles são parte do projeto do programa, mas não aparecem explicitamente no código.
A definição de classe para Card (carta) é assim:
+```python
class Card:
-
"""Represents a standard playing card."""
-
- def \_\_init\_\_(self, suit=0, rank=2):
-
+ def __init__(self, suit=0, rank=2):
self.suit = suit
-
self.rank = rank
+```
-Como sempre, o método init recebe um parâmetro opcional de cada atributo. A carta padrão é 2 de paus.
+Como sempre, o método `__init__` recebe um parâmetro opcional de cada atributo. A carta padrão é 2 de paus.
Para criar um Card, você chama Card com o naipe e valor desejados:
-queen\_of\_diamonds = Card(1, 12)
+```python
+queen_of_diamonds = Card(1, 12)
+```
## 18.2 - Atributos de classe
Para exibir objetos Card de uma forma que as pessoas possam ler com facilidade, precisamos de um mapeamento dos códigos de número inteiro aos naipes e valores correspondentes. Uma forma natural de fazer isso é com listas de strings. Atribuímos essas listas a atributos de classe:
-\# dentro da classe Card:
+```python
+# dentro da classe Card:
- suit\_names = \['Clubs', 'Diamonds', 'Hearts', 'Spades'\]
+ suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
+ rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7',
+ '8', '9', '10', 'Jack', 'Queen', 'King']
- rank\_names = \[None, 'Ace', '2', '3', '4', '5', '6', '7',
+ def __str__(self):
+ return '%s of %s' % (Card.rank_names[self.rank],
+ Card.suit_names[self.suit])
+```
- '8', '9', '10', 'Jack', 'Queen', 'King'\]
+Variáveis como `suit_names` e `rank_names`, que são definidas dentro de uma classe, mas fora de qualquer método, chamam-se atributos de classe porque são associadas com o objeto de classe Card.
- def \_\_str\_\_(self):
+Este termo as distingue de variáveis como `suit` e `rank`, chamadas de atributos de instância porque são associados com determinada instância.
- return '%s of %s' % (Card.rank\_names\[self.rank\],
+Ambos os tipos de atributo são acessados usando a notação de ponto. Por exemplo, em `__str__`, `self` é um objeto `Card`, e `self.rank` é o seu valor. De forma semelhante, Card é um objeto de classe, e `Card.rank_names` é uma lista de strings associadas à essa classe.
- Card.suit\_names\[self.suit\])
+Cada carta tem seu próprio suit e rank, mas há só uma cópia de suit_names e rank_names.
-Variáveis como suit\_names e rank\_names, que são definidas dentro de uma classe, mas fora de qualquer método, chamam-se atributos de classe porque são associadas com o objeto de classe Card.
+Juntando tudo, a expressão `Card.rank_names[self.rank]` significa “use o rank (valor) do atributo do objeto self como um índice na lista rank_names da classe Card e selecione a string adequada”.
-Este termo as distingue de variáveis como suit e rank, chamadas de atributos de instância porque são associados com determinada instância.
-
-Ambos os tipos de atributo são acessados usando a notação de ponto. Por exemplo, em \_\_str\_\_, self é um objeto Card, e self.rank é o seu valor. De forma semelhante, Card é um objeto de classe, e Card.rank\_names é uma lista de strings associadas com a classe.
-
-Cada carta tem seu próprio suit e rank, mas há só uma cópia de suit\_names e rank\_names.
-
-Juntando tudo, a expressão Card.rank\_names\[self.rank\] significa “use o rank (valor) do atributo do objeto self como um índice na lista rank\_names da classe Card e selecione a string adequada”.
-
-O primeiro elemento de rank\_names é None, porque não há nenhuma carta com valor zero. Incluindo None para ocupar uma variável, conseguimos fazer um belo mapeamento onde o índice 2 é associado à string '2', e assim por diante. Para evitar ter que usar esse truque, poderíamos usar um dicionário em vez de uma lista.
+O primeiro elemento de rank_names é None, porque não há nenhuma carta com valor zero. Incluindo None para ocupar uma variável, conseguimos fazer um belo mapeamento onde o índice 2 é associado à string '2', e assim por diante. Para evitar ter que usar esse truque, poderíamos usar um dicionário em vez de uma lista.
Com os métodos que temos por enquanto, podemos criar e exibir cartas:
->>> card1 = Card(2, 11)
-
->>> print(card1)
-
+```python
+>>> card1 = Card(2, 11)
+>>> print(card1)
Jack of Hearts
+```
-A Figura 18.1 é um diagrama do objeto de classe Card e uma instância de Card. Card é um objeto de classe; seu tipo é type. card1 é uma instância de Card, então seu tipo é Card. Para economizar espaço, não incluí o conteúdo de suit\_names e rank\_names.
+A Figura 18.1 é um diagrama do objeto de classe Card e uma instância de Card. Card é um objeto de classe; seu tipo é type. card1 é uma instância de Card, então seu tipo é Card. Para economizar espaço, não incluí o conteúdo de suit_names e rank_names.
-Figura 18.1 – Diagrama de objeto.
+
+
_Figura 18.1 – Diagrama de objetos: classe_ `Card` _e_ `card1`, _uma instância de_ `Card`.
## 18.3 - Comparação de cartas
-Para tipos integrados, há operadores relacionais (<, >, == etc.) que comparam valores e determinam quando um é maior, menor ou igual a outro. Para tipos definidos pelo programador, podemos ignorar o comportamento dos operadores integrados fornecendo um método denominado \_\_lt\_\_, que representa “menos que”.
+Para tipos integrados, há operadores relacionais (`<`, `>`, `==` etc.) que comparam valores e determinam quando um é maior, menor ou igual a outro. Para tipos definidos pelo programador, podemos ignorar o comportamento dos operadores integrados fornecendo um método denominado `__lt__`, que representa “menos que”.
-\_\_lt\_\_ recebe dois parâmetros, self e other, e True se self for estritamente menor que other.
+`__lt__` recebe dois parâmetros, `self` e `other`, e `True` se self for estritamente menor que `other`.
A ordem correta das cartas não é óbvia. Por exemplo, qual é melhor, o 3 de paus ou o 2 de ouros? Uma tem o valor mais alto, mas a outra tem um naipe mais alto. Para comparar cartas, é preciso decidir o que é mais importante, o valor ou o naipe.
A resposta pode depender de que jogo você está jogando, mas, para manter a simplicidade, vamos fazer a escolha arbitrária de que o naipe é mais importante, então todas as cartas de espadas são mais importantes que as de ouros, e assim por diante.
-Com isto decidido, podemos escrever \_\_lt\_\_:
+Com isto decidido, podemos escrever `__lt__`:
-\# dentro da classe Card:
+```python
+# dentro da classe Card:
- def \_\_lt\_\_(self, other):
+ def __lt__(self, other):
+ # conferir os naipes
+ if self.suit < other.suit: return True
+ if self.suit > other.suit: return False
- \# conferir os naipes
-
- if self.suit < other.suit: return True
-
- if self.suit > other.suit: return False
-
- \# os naipes são os mesmos... conferir valores
-
- return self.rank < other.rank
+ # os naipes são os mesmos... conferir valores
+ return self.rank < other.rank
+```
Você pode escrever isso de forma mais concisa usando uma comparação de tuplas:
-\# dentro da classe Card:
-
- def \_\_lt\_\_(self, other):
+```python
+# dentro da classe Card:
+ def __lt__(self, other):
t1 = self.suit, self.rank
-
t2 = other.suit, other.rank
+ return t1 < t2
+```
- return t1 < t2
-
-Como exercício, escreva um método \_\_lt\_\_ para objetos Time. Você pode usar uma comparação de tuplas, mas também pode usar a comparação de números inteiros.
+Como exercício, escreva um método `__lt__` para objetos Time. Você pode usar uma comparação de tuplas, mas também pode usar a comparação de números inteiros.
## 18.4 - Baralhos
Agora que temos Card, o próximo passo é definir Deck (baralho). Como um baralho é composto de cartas, é natural que um baralho contenha uma lista de cartas como atributo.
-Veja a seguir uma definição de classe para Deck. O método init cria o atributo cards e gera o conjunto padrão de 52 cartas:
-
-class Deck:
-
- def \_\_init\_\_(self):
+Veja a seguir uma definição de classe para `Deck`. O método init cria o atributo cards e gera o conjunto padrão de 52 cartas:
- self.cards = \[\]
+```python
+class Deck:
+ def __init__(self):
+ self.cards = []
for suit in range(4):
-
for rank in range(1, 14):
-
card = Card(suit, rank)
-
self.cards.append(card)
+```
A forma mais fácil de preencher o baralho é com um loop aninhado. O loop exterior enumera os naipes de 0 a 3. O loop interior enumera os valores de 1 a 13. Cada iteração cria um novo Card com o naipe e valor atual, e a acrescenta a self.cards.
## 18.5 - Exibição do baralho
-Aqui está um método \_\_str\_\_ para Deck:
-
-\# dentro da classe Deck:
+Aqui está um método __str__ para Deck:
- def \_\_str\_\_(self):
-
- res = \[\]
+```python
+# dentro da classe Deck:
+ def __str__(self):
+ res = []
for card in self.cards:
-
res.append(str(card))
+ return '\n'.join(res)
+```
- return '\\n'.join(res)
-
-Este método demonstra uma forma eficiente de acumular uma string grande: a criação de uma lista de strings e a utilização do método de string join. A função integrada str invoca o método \_\_str\_\_ em cada carta e retorna a representação da string.
+Este método demonstra uma forma eficiente de acumular uma string grande: a criação de uma lista de strings e a utilização do método de string join. A função integrada str invoca o método `__str__` em cada carta e retorna a representação da string.
Como invocamos join em um caractere newline, as cartas são separadas por quebras de linha. O resultado é esse:
->>> deck = Deck()
-
->>> print(deck)
-
+```python
+>>> deck = Deck()
+>>> print(deck)
Ace of Clubs
-
2 of Clubs
-
3 of Clubs
-
...
-
10 of Spades
-
Jack of Spades
-
Queen of Spades
-
King of Spades
+```
Embora o resultado apareça em 52 linhas, na verdade ele é uma string longa com quebras de linha.
@@ -204,37 +189,41 @@ Embora o resultado apareça em 52 linhas, na verdade ele é uma string longa com
Para lidar com as cartas, gostaríamos de ter um método que removesse uma carta do baralho e a devolvesse. O método de lista pop oferece uma forma conveniente de fazer isso:
-\# dentro da classe Deck:
-
- def pop\_card(self):
+```python
+# dentro da classe Deck:
+ def pop_card(self):
return self.cards.pop()
+```
Como pop retira a última carta na lista, estamos lidando com o fundo do baralho.
Para adicionar uma carta, podemos usar o método de lista append:
-\# dentro da classe Deck:
-
- def add\_card(self, card):
+```python
+# dentro da classe Deck:
+ def add_card(self, card):
self.cards.append(card)
+```
Um método como esse, que usa outro método sem dar muito trabalho, às vezes é chamado de folheado. A metáfora vem do trabalho em madeira, onde o folheado é uma camada fina de madeira de boa qualidade colada à superfície de uma madeira mais barata para melhorar a aparência.
-Nesse caso, add\_card é um método “fino” que expressa uma operação de lista em termos adequados a baralhos. Ele melhora a aparência ou interface da implementação.
+Nesse caso, `add_card` é um método “fino” que expressa uma operação de lista em termos adequados a baralhos. Ele melhora a aparência ou interface da implementação.
Em outro exemplo, podemos escrever um método Deck denominado shuffle, usando a função shuffle do módulo random:
-\# dentro da classe Deck:
- def shuffle(self):
+```python
+# dentro da classe Deck:
+ def shuffle(self):
random.shuffle(self.cards)
+```
Não se esqueça de importar random.
-Como exercício, escreva um método de Deck chamado sort, que use o método de lista sort para classificar as cartas em um Deck. sort usa o método \_\_lt\_\_ que definimos para determinar a ordem.
+Como exercício, escreva um método de Deck chamado sort, que use o método de lista sort para classificar as cartas em um Deck. sort usa o método `__lt__` que definimos para determinar a ordem.
## 18.7 - Herança
@@ -244,63 +233,60 @@ Uma mão também é diferente de um baralho; há operações que queremos para m
Essa relação entre classes – semelhante, mas diferente – adequa-se à herança. Para definir uma nova classe que herda algo de uma classe existente, basta colocar o nome da classe existente entre parênteses:
+```python
class Hand(Deck):
-
"""Represents a hand of playing cards."""
+```
Esta definição indica que Hand herda de Deck; isso significa que podemos usar métodos como pop\_card e add\_card para Hand bem como para Deck.
Quando uma nova classe herda de uma existente, a existente chama-se pai e a nova classe chama-se filho.
-Neste exemplo, Hand herda \_\_init\_\_ de Deck, mas na verdade não faz o que queremos: em vez de preencher a mão com 52 cartas novas, o método init de Hand deve inicializar card com uma lista vazia.
+Neste exemplo, Hand herda `__init__` de Deck, mas na verdade não faz o que queremos: em vez de preencher a mão com 52 cartas novas, o método init de Hand deve inicializar card com uma lista vazia.
Se fornecermos um método init na classe Hand, ele ignora o da classe Deck:
-\# dentro da classe Hand:
-
- def \_\_init\_\_(self, label=''):
-
- self.cards = \[\]
+```python
+# dentro da classe Hand:
+ def __init__(self, label=''):
+ self.cards = []
self.label = label
+```
Ao criar Hand, o Python invoca este método init, não o de Deck.
->>> hand = Hand('new hand')
-
->>> hand.cards
-
-\[\]
-
->>> hand.label
-
+```python
+>>> hand = Hand('new hand')
+>>> hand.cards
+[]
+>>> hand.label
'new hand'
+```
-Outros métodos são herdados de Deck, portanto podemos usar pop\_card e add\_card para lidar com uma carta:
-
->>> deck = Deck()
-
->>> card = deck.pop\_card()
-
->>> hand.add\_card(card)
-
->>> print(hand)
+Outros métodos são herdados de Deck, portanto podemos usar `pop_card` e `add_card` para lidar com uma carta:
+```python
+>>> deck = Deck()
+>>> card = deck.pop_card()
+>>> hand.add_card(card)
+>>> print(hand)
King of Spades
+```
-Um próximo passo natural seria encapsular este código em um método chamado move\_cards:
+Um próximo passo natural seria encapsular este código em um método chamado `move_cards`:
-\# dentro da classe Deck:
-
- def move\_cards(self, hand, num):
+```python
+# dentro da classe Deck:
+ def move_cards(self, hand, num):
for i in range(num):
+ hand.add_card(self.pop_card())
+```
- hand.add\_card(self.pop\_card())
-
-move\_cards recebe dois argumentos, um objeto Hand e o número de cartas com que vai lidar. Ele altera tanto self como hand e retorna None.
+`move_cards` recebe dois argumentos, um objeto Hand e o número de cartas com que vai lidar. Ele altera tanto self como hand e retorna None.
-Em alguns jogos, as cartas são movidas de uma mão a outra, ou de uma mão de volta ao baralho. É possível usar move\_cards para algumas dessas operações: self pode ser um Deck ou Hand, e hand, apesar do nome, também pode ser um Deck.
+Em alguns jogos, as cartas são movidas de uma mão a outra, ou de uma mão de volta ao baralho. É possível usar `move_cards` para algumas dessas operações: self pode ser um Deck ou Hand, e hand, apesar do nome, também pode ser um Deck.
A herança é um recurso útil. Alguns programas que poderiam ser repetitivos sem herança podem ser escritos de forma mais elegante com ela. A herança pode facilitar a reutilização de código, já que você pode personalizar o comportamento de classes pais sem ter que alterá-las. Em alguns casos, a estrutura de herança reflete a estrutura natural do problema, o que torna o projeto mais fácil de entender.
@@ -314,7 +300,7 @@ Eles também são altamente detalhados; para alguns objetivos, detalhados demais
Há vários tipos de relações entre as classes:
-* Os objetos de uma classe podem conter referências a objetos em outra classe. Por exemplo, cada Rectangle contém uma referência a um Point, e cada Deck contém referências a muitos Cards. Esse tipo de relação chama-se HAS-A (tem um), com a ideia de “um Rectangle tem um Point”.
+* Os objetos de uma classe podem conter referências a objetos em outra classe. Por exemplo, cada Rectangle contém uma referência a um Point, e cada Deck contém referências a muitos Cards. Esse tipo de relação chama-se composição. É uma relação do tipo HAS-A (tem um), com a ideia de “um Rectangle tem um Point”.
* Uma classe pode herdar de outra. Esta relação chama-se IS-A (é um), com a ideia de “um Hand é um tipo de Deck”.
@@ -322,13 +308,14 @@ Há vários tipos de relações entre as classes:
Um diagrama de classe é uma representação gráfica dessas relações. Por exemplo, a Figura 18.2 mostra as relações entre Card, Deck e Hand.
-Figura 18.2 – Diagrama de classe.
+
+
_Figura 18.2 – Diagrama de classes._
A flecha com um triângulo oco representa uma relação IS-A; nesse caso, indica que Hand herda de Deck.
A ponta de flecha padrão representa uma relação HAS-A; nesse caso, um Deck tem referências a objetos Card.
-Uma estrela (\*) perto da ponta de flecha é uma multiplicidade; ela indica quantos Cards um Deck tem. Uma multiplicidade pode ser um número simples como 52, um intervalo como 5..7 ou uma estrela, que indica que um Deck pode ter qualquer número de Cards.
+A estrela __*__ perto da ponta de flecha indica a multiplicidade; ela indica quantos Cards um Deck tem. Uma multiplicidade pode ser um número simples como 52, um intervalo como 5..7 ou uma estrela, que indica que um Deck pode ter qualquer número de Cards.
Não há nenhuma dependência neste diagrama. Elas normalmente apareceriam com uma flecha tracejada. Ou, se houver muitas dependências, às vezes elas são omitidas.
@@ -342,43 +329,37 @@ Mas, às vezes, é menos óbvio quais objetos você precisa e como eles devem in
A análise de Markov, de “Análise de Markov”, na página 200, apresenta um bom exemplo. Se baixar o meu código em http://thinkpython2.com/code/markov.py, você vai ver que ele usa duas variáveis globais – suffix\_map e prefix – que são lidas e escritas a partir de várias funções.
-suffix\_map = {}
-
+```python
+suffix_map = {}
prefix = ()
+```
Como essas variáveis são globais, só podemos executar uma análise de cada vez. Se lermos dois textos, seus prefixos e sufixos seriam acrescentados às mesmas estruturas de dados (o que geraria textos interessantes).
Para executar análises múltiplas e guardá-las separadamente, podemos encapsular o estado de cada análise em um objeto. É assim que fica:
+```python
class Markov:
-
- def \_\_init\_\_(self):
-
- self.suffix\_map = {}
-
+ def __init__(self):
+ self.suffix_map = {}
self.prefix = ()
+```
-Em seguida, transformamos as funções em métodos. Por exemplo, aqui está process\_word:
-
-def process\_word(self, word, order=2):
-
- if len(self.prefix) < order:
+Em seguida, transformamos as funções em métodos. Por exemplo, aqui está process_word:
+```python
+def process_word(self, word, order=2):
+ if len(self.prefix) < order:
self.prefix += (word,)
-
return
-
try:
-
- self.suffix\_map\[self.prefix\].append(word)
-
+ self.suffix_map[self.prefix].append(word)
except KeyError:
-
- \# se não houver entradas deste prefixo, crie uma.
-
- self.suffix\_map\[self.prefix\] = \[word\]
+ # se não houver entradas deste prefixo, crie uma.
+ self.suffix_map[self.prefix] = [word]
self.prefix = shift(self.prefix, word)
+```
Transformar um programa como esse – alterando o projeto sem mudar o comportamento – é outro exemplo de refatoração (veja “Refatoração”, na página 70).
@@ -406,25 +387,24 @@ A qualquer momento em que não esteja seguro a respeito do fluxo de execução d
Uma alternativa é usar esta função, que recebe um objeto e um nome de método (como uma string) e retorna a classe que fornece a definição do método:
-def find\_defining\_class(obj, meth\_name):
-
+```python
+def find_defining_class(obj, meth_name):
for ty in type(obj).mro():
-
- if meth\_name in ty.\_\_dict\_\_:
-
+ if meth_name in ty.__dict__:
return ty
+```
Aqui está um exemplo:
->>> hand = Hand()
-
->>> find\_defining\_class(hand, 'shuffle')
-
-<class 'Card.Deck'>
+```python
+>>> hand = Hand()
+>>> find_defining_class(hand, 'shuffle')
+
+```
Então o método shuffle deste Hand é o de Deck.
-find\_defining\_class usa o método mro para obter a lista de objetos de classe (tipos) onde os métodos serão procurados. “MRO” significa “ordem de resolução do método”, que é a sequência de classes que o Python pesquisa para “descobrir” um nome de método.
+`find_defining_class` usa o método mro para obter a lista de objetos de classe (tipos) onde os métodos serão procurados. “MRO” significa “ordem de resolução do método”, que é a sequência de classes que o Python pesquisa para “descobrir” um nome de método.
Aqui está uma sugestão de projeto: quando você ignora um método, a interface do novo método deve ser a mesma que a do antigo. Ela deve receber os mesmos parâmetros, retornar o mesmo tipo e obedecer às mesmas precondições e pós-condições. Se seguir esta regra, você descobrirá que qualquer função projetada para funcionar com uma instância de uma classe pai, como Deck, também funcionará com instâncias de classes filho como Hand e PokerHand.
@@ -432,144 +412,127 @@ Se violar esta regra, o que se chama de “princípio de substituição de Lisko
## 18.11 - Glossário
-__codificar__
-Representar um conjunto de valores usando outro conjunto de valores construindo um mapeamento entre eles.
+
+- codificar
+- Representar um conjunto de valores usando outro conjunto de valores construindo um mapeamento entre eles.
+
+- atributo de classe
+- Atributo associado a um objeto de classe. Os atributos de classe são definidos dentro de uma definição de classe, mas fora de qualquer método.
-__atributo de classe__
-Atributo associado a um objeto de classe. Os atributos de classe são definidos dentro de uma definição de classe, mas fora de qualquer método.
+- atributo de instância
+- Atributo associado a uma instância de uma classe.
-__atributo de instância__
-Atributo associado a uma instância de uma classe.
+- folheado
+- Método ou função que apresenta uma interface diferente para outra função sem fazer muitos cálculos.
-__folheado__
-Método ou função que apresenta uma interface diferente para outra função sem fazer muitos cálculos.
+- herança
+- Capacidade de definir uma nova classe que seja uma versão modificada de uma classe definida anteriormente.
-__herança__
-Capacidade de definir uma nova classe que seja uma versão modificada de uma classe definida anteriormente.
+- classe-pai
+- Classe da qual uma classe-filho herda.
-__classe-pai__
-Classe da qual uma classe-filho herda.
+- classe-filho
+- Nova classe criada por herança de uma classe existente; também chamada de “subclasse”.
-__classe-filho__
-Nova classe criada por herança de uma classe existente; também chamada de “subclasse”.
+- relação IS-A
+- Relação entre uma classe-filho e sua classe-pai. Também chamada de herança.
-__relação IS-A__
-Relação entre uma classe-filho e sua classe-pai.
+- relação HAS-A
+- Relação entre duas classes onde as instâncias de uma classe contêm referências a instâncias da outra. Também chamada de composição.
-__relação HAS-A__
-Relação entre duas classes onde as instâncias de uma classe contêm referências a instâncias da outra.
+- dependência
+- Relação entre duas classes onde as instâncias de uma classe usam instâncias de outra classe, mas não as guardam como atributos.
-__dependência__
-Relação entre duas classes onde as instâncias de uma classe usam instâncias de outra classe, mas não as guardam como atributos.
+- diagrama de classe
+- Diagrama que mostra as classes em um programa e as relações entre elas.
-__diagrama de classe__
-Diagrama que mostra as classes em um programa e as relações entre elas.
+- multiplicidade
+- Notação em um diagrama de classe que mostra, para uma relação HAS-A, quantas referências a instâncias da outra classe podem existir.
-__multiplicidade__
-Notação em um diagrama de classe que mostra, para uma relação HAS-A, quantas referências dela são instâncias de outra classe.
+- encapsulamento de dados
+- Plano de desenvolvimento de programa que envolve um protótipo usando variáveis globais e uma versão final que transforma as variáveis globais em atributos de instância.
-__encapsulamento de dados__
-Plano de desenvolvimento de programa que envolve um protótipo usando variáveis globais e uma versão final que transforma as variáveis globais em atributos de instância.
+
## 18.12 - Exercícios
### Exercício 18.1
-Para o seguinte programa, projete um diagrama de classe UML que mostre estas classes e as relações entre elas.
+Para o seguinte programa, desenhe um diagrama de classe UML que mostre estas classes e as relações entre elas.
+```python
class PingPongParent:
-
pass
class Ping(PingPongParent):
-
- def \_\_init\_\_(self, pong):
-
+ def __init__(self, pong):
self.pong = pong
class Pong(PingPongParent):
-
- def \_\_init\_\_(self, pings=None):
-
+ def __init__(self, pings=None):
if pings is None:
-
- self.pings = \[\]
-
+ self.pings = []
else:
-
self.pings = pings
-
- def add\_ping(self, ping):
-
+ def add_ping(self, ping):
self.pings.append(ping)
pong = Pong()
-
ping = Ping(pong)
-
-pong.add\_ping(ping)
+pong.add_ping(ping)
+```
### Exercício 18.2
-Escreva um método Deck chamado deal\_hands que receba dois parâmetros: o número de mãos e o número de cartas por mão. Ele deve criar o número adequado de objetos Hand, lidar com o número adequado de cartas por mão e retornar uma lista de Hands.
+Escreva um método Deck chamado `deal_hands` que receba dois parâmetros: o número de mãos e o número de cartas por mão. Ele deve criar o número adequado de objetos Hand, lidar com o número adequado de cartas por mão e retornar uma lista de Hands.
### Exercício 18.3
A seguir, as mãos possíveis no pôquer, em ordem crescente de valor e ordem decrescente de probabilidade:
-par:
-
-Duas cartas com o mesmo valor.
-
-dois pares:
-
-Dois pares de cartas com o mesmo valor.
-
-trinca:
+
+- par
+- Duas cartas com o mesmo valor.
-Três cartas com o mesmo valor.
+- dois pares
+- Dois pares de cartas com o mesmo valor.
-sequência:
+- trinca
+- Três cartas com o mesmo valor.
-Cinco cartas com valores em sequência (os ases podem ser altos ou baixos, então Ace-2-3-4-5 é uma sequência, assim como 10-Jack-Queen-King-Ace, mas Queen-King-Ace-2-3 não é.)
+- sequência
+- Cinco cartas com valores em sequência (os ases podem ser altos ou baixos, então Ace-2-3-4-5 é uma sequência, assim como 10-Jack-Queen-King-Ace, mas Queen-King-Ace-2-3 não é.)
-flush:
+- flush
+- Cinco cartas com o mesmo naipe.
-Cinco cartas com o mesmo naipe.
+- full house
+- Três cartas com um valor, duas cartas com outro.
-full house:
+- quadra
+- Quatro cartas com o mesmo valor.
-Três cartas com um valor, duas cartas com outro.
+- straight flush
+- Cinco cartas em sequência (como definido acima) e com o mesmo naipe.
-quadra:
-
-Quatro cartas com o mesmo valor.
-
-straight flush:
-
-Cinco cartas em sequência (como definido acima) e com o mesmo naipe.
+
A meta desses exercícios é estimar a probabilidade de ter estas várias mãos.
1. Baixe os seguintes arquivos de http://thinkpython2.com/code:
-Card.py:
+ * `Card.py`: Versão completa das classes Card, Deck e Hand deste capítulo.
-Versão completa das classes Card, Deck e Hand deste capítulo.
-
-PokerHand.py:
-
-Uma implementação incompleta de uma classe que representa uma mão de pôquer e código para testá-la.
+ * `PokerHand.py`: Uma implementação incompleta de uma classe que representa uma mão de pôquer e código para testá-la.
2. Se executar PokerHand.py, você verá que o programa cria mãos de pôquer com 7 cartas e verifica se alguma delas contém um flush. Leia este código com atenção antes de continuar.
-3. Acrescente métodos a PokerHand.py chamados has\_pair, has\_twopair, etc. que retornem True ou False conforme a mão cumpra os critérios em questão. Seu código deve funcionar corretamente para “mãos” que contenham qualquer número de cartas (embora 5 e 7 sejam as quantidades mais comuns).
+3. Acrescente métodos a PokerHand.py chamados `has_pair`, `has_twopair`, etc. que retornem True ou False conforme a mão cumpra os critérios em questão. Seu código deve funcionar corretamente para “mãos” que contenham qualquer número de cartas (embora 5 e 7 sejam as quantidades mais comuns).
4. Escreva um método chamado classify que descubra a classificação do valor mais alto para uma mão e estabeleça o atributo label em questão. Por exemplo, uma mão de 7 cartas poderia conter um flush e um par; ela deve ser marcada como “flush”.
5. Quando se convencer de que os seus métodos de classificação estão funcionando, o próximo passo deve ser estimar as probabilidades de várias mãos. Escreva uma função em PokerHand.py que embaralhe cartas, divida-as em mãos, classifique as mãos e conte o número de vezes em que várias classificações aparecem.
-6. Exiba uma tabela das classificações e suas probabilidades. Execute seu programa com números cada vez maiores de mãos até que os valores de saída convirjam a um grau razoável de exatidão. Compare seus resultados com os valores em http://en.wikipedia.org/wiki/Hand\_rankings.
+6. Exiba uma tabela das classificações e suas probabilidades. Execute seu programa com números cada vez maiores de mãos até que os valores de saída convirjam a um grau razoável de exatidão. Compare seus resultados com os valores em http://en.wikipedia.org/wiki/Hand_rankings.
Solução: http://thinkpython2.com/code/PokerHandSoln.py.
-
diff --git a/docs/19-extra.md b/docs/19-extra.md
index 32b5548..26b4c91 100644
--- a/docs/19-extra.md
+++ b/docs/19-extra.md
@@ -8,59 +8,57 @@ Agora quero voltar a algumas coisas boas que ficaram para trás. O Python oferec
Vimos instruções condicionais em “Execução condicional”, na página 78. As instruções condicionais muitas vezes são usadas para escolher um entre dois valores; por exemplo:
-if x > 0:
-
+```python
+if x > 0:
y = math.log(x)
-
else:
-
y = float('nan')
+```
Esta instrução verifica se x é positivo. Nesse caso, ela calcula math.log. Do contrário, math.log causaria um ValueError. Para evitar interromper o programa, geramos um “NaN”, que é um valor de ponto flutuante especial que representa um “Não número”.
Podemos escrever essa instrução de forma mais concisa usando uma expressão condicional:
-y = math.log(x) if x > 0 else float('nan')
+```python
+y = math.log(x) if x > 0 else float('nan')
+```
Você quase pode ler esta linha como se tivesse sido escrita em inglês: “y recebe log-x se x for maior que 0; do contrário, ele recebe NaN”.
As funções recursivas por vezes podem ser reescritas usando expressões condicionais. Por exemplo, aqui está uma versão recursiva de factorial:
+```python
def factorial(n):
-
if n == 0:
-
return 1
-
else:
-
- return n \* factorial(n-1)
+ return n * factorial(n-1)
+```
Podemos reescrevê-la assim:
+```python
def factorial(n):
-
- return 1 if n == 0 else n \* factorial(n-1)
-
+ return 1 if n == 0 else n * factorial(n-1)
+```
Outro uso de expressões condicionais é lidar com argumentos opcionais. Por exemplo, aqui está o método init de GoodKangaroo (veja o Exercício 17.2):
-def \_\_init\_\_(self, name, contents=None):
+```python
+def __init__(self, name, contents=None):
self.name = name
-
if contents == None:
-
- contents = \[\]
-
- self.pouch\_contents = contents
+ contents = []
+ self.pouch_contents = contents
+```
Podemos reescrevê-lo assim:
-def \_\_init\_\_(self, name, contents=None):
-
+```python
+def __init__(self, name, contents=None):
self.name = name
-
- self.pouch\_contents = \[\] if contents == None else contents
+ self.pouch_contents = [] if contents == None else contents
+```
Em geral, é possível substituir uma instrução condicional por uma expressão condicional se ambos os ramos contiverem expressões simples que sejam retornadas ou atribuídas à mesma variável.
@@ -68,21 +66,20 @@ Em geral, é possível substituir uma instrução condicional por uma expressão
Em “Mapeamento, filtragem e redução”, na página 147, vimos os padrões de filtragem e mapeamento. Por exemplo, esta função toma uma lista de strings, mapeia o método de string capitalize aos elementos, e retorna uma nova lista de strings:
-def capitalize\_all(t):
-
- res = \[\]
-
+```python
+def capitalize_all(t):
+ res = []
for s in t:
-
res.append(s.capitalize())
-
return res
+```
Podemos escrever isso de forma mais concisa usando abrangência de listas (list comprehension):
-def capitalize\_all(t):
-
- return \[s.capitalize() for s in t\]
+```python
+def capitalize_all(t):
+ return [s.capitalize() for s in t]
+```
Os operadores de colchete indicam que estamos construindo uma nova lista. A expressão dentro dos colchetes especifica os elementos da lista, e a cláusula for indica qual sequência estamos atravessando.
@@ -90,23 +87,21 @@ A sintaxe da abrangência de listas é um pouco esquisita porque a variável de
Abrangências de listas também podem ser usadas para filtragem. Por exemplo, esta função só seleciona os elementos de t que são maiúsculos, e retorna uma nova lista:
-def only\_upper(t):
-
- res = \[\]
-
+```python
+def only_upper(t):
+ res = []
for s in t:
-
if s.isupper():
-
res.append(s)
-
return res
+```
Podemos reescrevê-la usando abrangência de listas:
-def only\_upper(t):
-
- return \[s for s in t if s.isupper()\]
+```python
+def only_upper(t):
+ return [s for s in t if s.isupper()]
+```
Abrangências de listas são concisas e fáceis de ler, pelo menos para expressões simples. E são normalmente mais rápidas que os loops for equivalentes, às vezes muito mais rápidas. Então, se você ficar irritado comigo por não ter mencionado isso antes, eu entendo.
@@ -116,87 +111,86 @@ Porém, em minha defesa, as abrangências de listas são mais difíceis de depur
Expressões geradoras são semelhantes às abrangências de listas, mas com parênteses em vez de colchetes:
->>> g = (x\*\*2 for x in range(5))
-
->>> g
-
-<generator object <genexpr> at 0x7f4c45a786c0>
+```python
+>>> g = (x**2 for x in range(5))
+>>> g
+ at 0x7f4c45a786c0>
+```
O resultado é um objeto gerador que sabe como fazer iterações por uma sequência de valores. No entanto, ao contrário de uma abrangência de listas, ele não calcula todos os valores de uma vez; espera pelo pedido. A função integrada next recebe o próximo valor do gerador:
->>> next(g)
-
+```python
+>>> next(g)
0
-
->>> next(g)
-
+>>> next(g)
1
+```
Quando você chega no fim da sequência, next cria uma exceção StopIteration. Também é possível usar um loop for para fazer a iteração pelos valores:
->>> for val in g:
-
+```python
+>>> for val in g:
... print(val)
-
4
-
9
-
16
+```
O objeto gerador monitora a posição em que está na sequência, portanto o loop for continua de onde next parou. Uma vez que o gerador se esgotar, ele continua criando StopException:
->>> next(g)
-
+```python
+>>> next(g)
StopIteration
+```
As expressões geradoras muitas vezes são usadas com funções como sum, max e min:
->>> sum(x\*\*2 for x in range(5))
-
+```python
+>>> sum(x**2 for x in range(5))
30
+```
## 19.4 - any e all
O Python tem uma função integrada, any, que recebe uma sequência de valores booleanos e retorna True se algum dos valores for True. Ela funciona em listas:
->>> any(\[False, False, True\])
-
+```python
+>>> any([False, False, True])
True
+```
Entretanto, muitas vezes é usada com expressões geradoras:
->>> any(letter == 't' for letter in 'monty')
-
+```python
+>>> any(letter == 't' for letter in 'monty')
True
+```
Esse exemplo não é muito útil porque faz a mesma coisa que o operador in. Porém, podemos usar any para reescrever algumas das funções de pesquisa que escrevemos em “Busca”, na página 136. Por exemplo, poderíamos escrever avoids dessa forma:
+```python
def avoids(word, forbidden):
-
return not any(letter in forbidden for letter in word)
+```
A função quase pode ser lida como uma frase em inglês: “word evita forbidden se não houver nenhuma letra proibida em word”.
-Usar any com uma expressão geradora é eficiente porque o programa é interrompido imediatamente se encontrar um valor True, então não é preciso avaliar a sequência inteira.
+Usar `any` com uma expressão geradora é eficiente porque ela retorna imediatamente se encontrar um valor True, então não é preciso avaliar a sequência inteira.
-O Python oferece outra função integrada, all, que retorna True se todos os elementos da sequência forem True. Como exercício, use all para reescrever uses\_all de “Busca”, na página 136.
+O Python oferece outra função integrada, `all`, que retorna True se todos os elementos da sequência forem True. Como exercício, use all para reescrever `uses_all` de “Busca”, na página 136.
## 19.5 - Conjuntos
Na seção “Subtração de dicionário”, da página 198, uso dicionários para encontrar as palavras que aparecem em um documento, mas não numa lista de palavras. A função que escrevi recebe d1, que contém as palavras do documento como chaves e d2, que contém a lista de palavras. Ela retorna um dicionário que contém as chaves de d1 que não estão em d2:
+```python
def subtract(d1, d2):
-
res = dict()
-
for key in d1:
-
if key not in d2:
-
- res\[key\] = None
-
+ res[key] = None
return res
+```
Em todos esses dicionários, os valores não são None porque nunca os usamos. O resultado é que desperdiçamos espaço de armazenamento.
@@ -204,253 +198,233 @@ O Python fornece outro tipo integrado, chamado set (conjunto), que se comporta c
Por exemplo, a subtração de conjuntos está disponível como um método chamado difference ou como um operador, -. Portanto, podemos reescrever subtract desta forma:
+```python
def subtract(d1, d2):
-
return set(d1) - set(d2)
+```
O resultado é um conjunto em vez de um dicionário, mas, para operações como iteração, o comportamento é o mesmo.
Alguns exercícios neste livro podem ser feitos de forma concisa e eficiente com conjuntos. Por exemplo, aqui está uma solução para has\_duplicates, do Exercício 10.7, que usa um dicionário:
-def has\_duplicates(t):
-
+```python
+def has_duplicates(t):
d = {}
-
for x in t:
-
if x in d:
-
return True
-
- d\[x\] = True
-
+ d[x] = True
return False
+```
Quando um elemento aparece pela primeira vez, ele é acrescentado ao dicionário. Se o mesmo elemento aparece novamente, a função retorna True.
Usando conjuntos, podemos escrever a mesma função dessa forma:
-def has\_duplicates(t):
-
- return len(set(t)) < len(t)
+```python
+def has_duplicates(t):
+ return len(set(t)) < len(t)
+```
Um elemento só pode aparecer em um conjunto uma vez, portanto, se um elemento em t aparecer mais de uma vez, o conjunto será menor que t. Se não houver duplicatas, o conjunto terá o mesmo tamanho que t.
Também podemos usar conjuntos para fazer alguns exercícios no Capítulo 9. Por exemplo, aqui está uma versão de uses\_only com um loop:
-def uses\_only(word, available):
-
+```python
+def uses_only(word, available):
for letter in word:
-
if letter not in available:
-
return False
-
return True
+```
-uses\_only verifica se todas as cartas em word estão em available. Podemos reescrevê-la assim:
-
-def uses\_only(word, available):
+`uses_only` verifica se todas as cartas em word estão em available. Podemos reescrevê-la assim:
- return set(word) <= set(available)
+```python
+def uses_only(word, available):
+ return set(word) <= set(available)
+```
-O operador <= verifica se um conjunto é um subconjunto ou outro, incluindo a possibilidade de que sejam iguais, o que é verdade se todas as letras de word aparecerem em available.
+O operador `<=` verifica se um conjunto é um subconjunto ou outro, incluindo a possibilidade de que sejam iguais, o que é verdade se todas as letras de word aparecerem em available.
Como exercício, reescreva avoids usando conjuntos.
## 19.6 - Contadores
-Um contador é como um conjunto, exceto que se um elemento aparecer mais de uma vez, o contador acompanha quantas vezes ele aparece. Se tiver familiaridade com a ideia matemática de um multiconjunto, um contador é uma forma natural de representar um multiconjunto.
+Um contador é como um conjunto, exceto que se um elemento aparecer mais de uma vez, o contador registra quantas vezes ele aparece. Se tiver familiaridade com a ideia matemática de um multiconjunto (multiset), um contador é uma forma natural de representar um multiconjunto.
-Contadores são definidos em um módulo-padrão chamado collections, portanto é preciso importá-lo. Você pode inicializar um contador com uma string, lista ou alguma outra coisa que seja compatível com iteração:
-
->>> from collections import Counter
-
->>> count = Counter('parrot')
-
->>> count
+Contadores são definidos em um módulo padrão chamado `collections`, portanto é preciso importá-lo. Você pode inicializar um contador com uma string, lista ou alguma outra coisa que seja compatível com iteração:
+```python
+>>> from collections import Counter
+>>> count = Counter('parrot')
+>>> count
Counter({'r': 2, 't': 1, 'o': 1, 'p': 1, 'a': 1})
+```
Os contadores comportam-se como dicionários de muitas formas; eles mapeiam cada chave ao número de vezes que aparece. Como em dicionários, as chaves têm de ser hashable.
Ao contrário de dicionários, os contadores não causam uma exceção se você acessar um elemento que não aparece. Em vez disso, retornam 0:
->>> count\['d'\]
-
+```python
+>>> count['d']
0
+```
-Podemos usar contadores para reescrever is\_anagram do Exercício 10.6:
-
-def is\_anagram(word1, word2):
+Podemos usar contadores para reescrever `is_anagram` do Exercício 10.6:
+```python
+def is_anagram(word1, word2):
return Counter(word1) == Counter(word2)
+```
Se duas palavras forem anagramas, elas contêm as mesmas letras com as mesmas contagens, então seus contadores são equivalentes.
-Os contadores oferecem métodos e operadores para executar operações similares às dos conjuntos, incluindo adição, subtração, união e intersecção. E eles fornecem um método muitas vezes útil, most\_common, que retorna uma lista de pares frequência-valor, organizados do mais ao menos comum:
-
->>> count = Counter('parrot')
-
->>> for val, freq in count.most\_common(3):
+Os contadores oferecem métodos e operadores para executar operações similares às dos conjuntos, incluindo adição, subtração, união e intersecção. E eles fornecem um método muitas vezes útil, most_common, que retorna uma lista de pares frequência-valor, organizados do mais ao menos comum:
+```python
+>>> count = Counter('parrot')
+>>> for val, freq in count.most_common(3):
... print(val, freq)
-
r 2
-
p 1
-
a 1
+```
## 19.7 - defaultdict
-O módulo collections também tem defaultdict, que se parece com um dicionário, exceto pelo fato de que se você acessar uma chave que não existe, um novo valor pode ser gerado automaticamente.
+O módulo `collections` também tem `defaultdict`, que se parece com um dicionário, exceto pelo fato de que se você acessar uma chave que não existe, um novo valor pode ser gerado automaticamente.
Quando você cria um defaultdict, fornece uma função usada para criar valores. Uma função usada para criar objetos às vezes é chamada de factory (fábrica). As funções integradas que criam listas, conjuntos e outros tipos podem ser usadas como fábricas:
->>> from collections import defaultdict
-
->>> d = defaultdict(list)
+```python
+>>> from collections import defaultdict
+>>> d = defaultdict(list)
+```
Note que o argumento é list, que é um objeto de classe, não list(), que é uma nova lista. A função que você fornece não é chamada a menos que você acesse uma chave que não existe:
->>> t = d\['new key'\]
-
->>> t
-
-\[\]
+```python
+>>> t = d['new key']
+>>> t
+[]
+```
A nova lista, que estamos chamando de t, também é adicionada ao dicionário. Então, se alterarmos t, a mudança aparece em d:
->>> t.append('new value')
-
->>> d
+```python
+>>> t.append('new value')
+>>> d
+defaultdict(, {'new key': ['new value']})
+```
-defaultdict(<class 'list'>, {'new key': \['new value'\]})
-
-Se estiver fazendo um dicionário de listas, você pode escrever um código mais simples usando defaultdict. Na minha solução para o Exercício 12.2, que você pode ver em http://thinkpython2.com/code/anagram\_sets.py, faço um dicionário que mapeia uma string organizada de letras a uma lista de palavras que pode ser soletrada com essas letras. Por exemplo, 'opst' mapeia para a lista \['opts', 'post', 'pots', 'spot', 'stop', 'tops'\].
+Se estiver fazendo um dicionário de listas, você pode escrever um código mais simples usando defaultdict. Na minha solução para o Exercício 12.2, que você pode ver em http://thinkpython2.com/code/anagram\_sets.py, faço um dicionário que mapeia uma string organizada de letras a uma lista de palavras que pode ser soletrada com essas letras. Por exemplo, 'opst' mapeia para a lista `['opts', 'post', 'pots', 'spot', 'stop', 'tops']`.
Aqui está o código original:
-def all\_anagrams(filename):
-
+```python
+def all_anagrams(filename):
d = {}
-
for line in open(filename):
-
word = line.strip().lower()
-
t = signature(word)
-
if t not in d:
-
- d\[t\] = \[word\]
-
+ d[t] = [word]
else:
-
- d\[t\].append(word)
-
+ d[t].append(word)
return d
+```
Isso pode ser simplificado usando setdefault, que você poderia ter usado no Exercício 11.2:
-def all\_anagrams(filename):
-
+```python
+def all_anagrams(filename):
d = {}
-
for line in open(filename):
-
word = line.strip().lower()
-
t = signature(word)
-
- d.setdefault(t, \[\]).append(word)
-
+ d.setdefault(t, []).append(word)
return d
+```
O problema dessa solução é que ela faz uma lista nova a cada vez, mesmo que não seja necessário. Para listas, isso não é grande coisa, mas se a função fábrica for complicada, poderia ser.
Podemos evitar este problema e simplificar o código usando um defaultdict:
-def all\_anagrams(filename):
-
+```python
+def all_anagrams(filename):
d = defaultdict(list)
-
for line in open(filename):
-
word = line.strip().lower()
-
t = signature(word)
-
- d\[t\].append(word)
-
+ d[t].append(word)
return d
+```
-A minha solução para o Exercício 18.3, que você pode baixar em http://thinkpython2.com/code/PokerHandSoln.py, usa setdefault na função has\_straightflush. O problema dessa solução é criar um objeto Hand cada vez que passa pelo loop, seja ele necessário ou não. Como exercício, reescreva-a usando um defaultdict.
+A minha solução para o Exercício 18.3, que você pode baixar em http://thinkpython2.com/code/PokerHandSoln.py, usa setdefault na função `has_straightflush`. O problema dessa solução é criar um objeto Hand cada vez que passa pelo loop, seja ele necessário ou não. Como exercício, reescreva-a usando um defaultdict.
## 19.8 - Tuplas nomeadas
Muitos objetos simples são basicamente coleções de valores relacionados. Por exemplo, o objeto Point, definido no Capítulo 15, contém dois números, x e y. Ao definir uma classe como essa, normalmente você começa com um método init e um método str:
+```python
class Point:
-
- def \_\_init\_\_(self, x=0, y=0):
-
+ def __init__(self, x=0, y=0):
self.x = x
-
self.y = y
-
- def \_\_str\_\_(self):
-
+ def __str__(self):
return '(%g, %g)' % (self.x, self.y)
+```
É muito código para transmitir pouca informação. O Python tem uma forma mais concisa de dizer a mesma coisa:
+```python
from collections import namedtuple
-
-Point = namedtuple('Point', \['x', 'y'\])
+Point = namedtuple('Point', ['x', 'y'])
+```
O primeiro argumento é o nome da classe que você quer criar. O segundo é uma lista dos atributos que o objeto Point deve ter, como strings. O valor de retorno de namedtuple é um objeto de classe:
->>> Point
+```python
+>>> Point
+
+```
-<class '\_\_main\_\_.Point'>
-
-Point fornece automaticamente métodos como \_\_init\_\_ e \_\_str\_\_ então não é preciso escrevê-los.
+Point fornece automaticamente métodos como `__init__` e `__str__` então não é preciso escrevê-los.
Para criar um objeto Point, você usa a classe Point como uma função:
->>> p = Point(1, 2)
-
->>> p
-
+```python
+>>> p = Point(1, 2)
+>>> p
Point(x=1, y=2)
+```
-O método init atribui os argumentos a atributos usando os nomes que você forneceu. O método str exibe uma representação do objeto Point e seus atributos.
+O método `__init__` atribui os argumentos a atributos usando os nomes que você forneceu. O método `__str__` exibe uma representação do objeto Point e seus atributos.
Você pode acessar os elementos da tupla nomeada pelo nome:
->>> p.x, p.y
-
+```python
+>>> p.x, p.y
(1, 2)
+```
Mas também pode tratar uma tupla nomeada como uma tupla:
->>> p\[0\], p\[1\]
-
+```python
+>>> p[0], p[1]
(1, 2)
-
->>> x, y = p
-
->>> x, y
-
+>>> x, y = p
+>>> x, y
(1, 2)
+```
Tuplas nomeadas fornecem uma forma rápida de definir classes simples. O problema é que classes simples não ficam sempre simples. Mais adiante você poderá decidir que quer acrescentar métodos a uma tupla nomeada. Nesse caso, você poderá definir uma nova classe que herde da tupla nomeada:
+```python
class Pointier(Point):
-
- \# adicionar mais métodos aqui
+ # adicionar mais métodos aqui
+```
Ou poderá mudar para uma definição de classe convencional.
@@ -458,72 +432,78 @@ Ou poderá mudar para uma definição de classe convencional.
Em “Tuplas com argumentos de comprimento variável”, na página 181, vimos como escrever uma função que reúne seus argumentos em uma tupla:
-def printall(\*args):
-
+```python
+def printall(*args):
print(args)
+```
Você pode chamar esta função com qualquer número de argumentos posicionais (isto é, argumentos que não têm palavras-chave):
->>> printall(1, 2.0, '3')
-
+```python
+>>> printall(1, 2.0, '3')
(1, 2.0, '3')
+```
-Porém, o operador \* não reúne argumentos de palavra-chave:
-
->>> printall(1, 2.0, third='3')
+Porém, o operador `*` não reúne argumentos de palavra-chave:
+```python
+>>> printall(1, 2.0, third='3')
TypeError: printall() got an unexpected keyword argument 'third'
+```
-Para reunir argumentos de palavra-chave, você pode usar o operador \*\*:
-
-def printall(\*args, \*\*kwargs):
+Para reunir argumentos de palavra-chave, você pode usar o operador `**`:
+```python
+def printall(*args, **kwargs):
print(args, kwargs)
+```
-Você pode chamar o parâmetro de coleta de palavra-chave, como quiser, mas kwargs é uma escolha comum. O resultado é um dicionário que mapeia palavras-chave a valores:
-
->>> printall(1, 2.0, third='3')
+Você pode chamar o parâmetro de coleta de palavra-chave, como quiser, mas `kwargs` é uma escolha comum. O resultado é um dicionário que mapeia palavras-chave a valores:
+```python
+>>> printall(1, 2.0, third='3')
(1, 2.0) {'third': '3'}
+```
-Se tiver um dicionário de palavras-chave e valores, pode usar o operador de dispersão, \*\*, para chamar uma função:
-
->>> d = dict(x=1, y=2)
-
->>> Point(\*\*d)
+Se tiver um dicionário de palavras-chave e valores, pode usar o operador de dispersão, `**`, para chamar uma função:
+```python
+>>> d = dict(x=1, y=2)
+>>> Point(**d)
Point(x=1, y=2)
+```
Sem o operador de dispersão, a função trataria d como um único argumento posicional, e então atribuiria d a x e se queixaria porque não há nada para atribuir a y:
->>> d = dict(x=1, y=2)
-
->>> Point(d)
-
+```python
+>>> d = dict(x=1, y=2)
+>>> Point(d)
Traceback (most recent call last):
+ File "", line 1, in
+TypeError: __new__() missing 1 required positional argument: 'y'
+```
- File "<stdin>", line 1, in <module>
-
-TypeError: \_\_new\_\_() missing 1 required positional argument: 'y'
-
-Quando estiver trabalhando com funções com um grande número de parâmetros, muitas vezes é útil criar e usar dicionários que especifiquem as opções usadas frequentemente.
+Quando estiver trabalhando com funções com um grande número de parâmetros, muitas vezes é útil criar dicionários e passá-los como argumentos para especificar as opções usadas com maior frequência.
## 19.10 - Glossário
-__expressão condicional__
-Expressão que contém um de dois valores, dependendo de uma condição.
+
+- expressão condicional
+- Expressão que contém um de dois valores, dependendo de uma condição.
-__abrangência de listas__
-Expressão com um loop for entre colchetes que produz uma nova lista.
+- abrangência de lista (list comprehension)
+- Expressão com um loop for entre colchetes que produz uma nova lista.
-__expressão geradora__
-Uma expressão com um loop for entre parênteses que produz um objeto gerador.
+- expressão geradora
+- Uma expressão com um loop for entre parênteses que produz um objeto gerador.
-__multiconjunto__
-Entidade matemática que representa um mapeamento entre os elementos de um conjunto e o número de vezes que aparecem.
+- multiconjunto
+- Entidade matemática que representa um mapeamento entre os elementos de um conjunto e o número de vezes que aparecem.
-__fábrica (factory)__
-Função normalmente passada como parâmetro, usada para criar objetos.
+- fábrica (factory)
+- Função normalmente passada como parâmetro, usada para criar objetos.
+
+
## 19.11 - Exercícios
@@ -531,31 +511,24 @@ Função normalmente passada como parâmetro, usada para criar objetos.
Esta é uma função que calcula o coeficiente binominal recursivamente:
-def binomial\_coeff(n, k):
-
+```python
+def binomial_coeff(n, k):
"""Compute the binomial coefficient "n choose k".
n: number of trials
-
k: number of successes
-
returns: int
"""
-
if k == 0:
-
return 1
-
if n == 0:
-
return 0
-
- res = binomial\_coeff(n-1, k) + binomial\_coeff(n-1, k-1)
+ res = binomial_coeff(n-1, k) + binomial_coeff(n-1, k-1)
return res
+```
Reescreva o corpo da função usando expressões condicionais aninhadas.
Uma observação: esta função não é muito eficiente porque acaba calculando os mesmos valores várias vezes. Você pode torná-lo mais eficiente com memos (veja “Memos”, na página 169). No entanto, vai ver que é mais difícil usar memos se escrevê-la usando expressões condicionais.
-
diff --git a/docs/A-depuracao.md b/docs/A-depuracao.md
index 49445d8..92ccdef 100644
--- a/docs/A-depuracao.md
+++ b/docs/A-depuracao.md
@@ -30,9 +30,9 @@ Aqui estão algumas formas de evitar os erros de sintaxe mais comuns:
4. Se tiver strings com várias linhas com aspas triplas (simples ou duplas), confira se fechou a string adequadamente. Uma string não fechada pode causar um erro de invalid token no fim do seu programa, ou pode tratar a parte seguinte do programa como uma string até chegar à string seguinte. No segundo caso, o programa pode nem produzir uma mensagem de erro!
-5. Um operador inicial aberto – (, { ou \[ – faz o Python continuar até a linha seguinte, como se esta fosse parte da instrução atual. Geralmente, um erro ocorre quase imediatamente na linha seguinte.
+5. Um operador inicial aberto – `(`, `{` ou `[` – faz o Python continuar até a linha seguinte, como se esta fosse parte da instrução atual. Geralmente, um erro ocorre quase imediatamente na linha seguinte.
-6. Confira se há o clássico = em vez do == dentro de uma condicional.
+6. Confira se há o clássico `=` em vez do `==` dentro de uma condicional.
7. Verifique a endentação para ter certeza de que está alinhada como deveria. O Python pode lidar com espaços e tabulações, mas, se misturá-los, isso pode causar problemas. A melhor forma de evitar esse problema é usar um editor de texto que identifique o Python e gere endentação consistente.
@@ -75,46 +75,41 @@ Se não for intencional, tenha certeza de que há uma chamada de função no pro
Se um programa parar e parecer que não está fazendo nada, ele está “travado”. Muitas vezes isso significa que está preso em um loop ou recursão infinita.
* Se houver determinado loop que você suspeita ser o problema, acrescente uma instrução print imediatamente antes do loop que diga “entrando no loop”, e outra imediatamente depois que diga “saindo do loop”.
-
- Execute o programa. Se receber a primeira mensagem e a segunda não, você tem um loop infinito. Vá à seção “Loop infinito” mais adiante.
+
Execute o programa. Se receber a primeira mensagem e a segunda não, você tem um loop infinito. Vá à seção “Loop infinito” mais adiante.
* Na maior parte do tempo, a recursividade infinita fará com que o programa seja executado durante algum tempo e então gere um erro “RuntimeError: Maximum recursion depth exceeded”. Se isso acontecer, vá à seção “Recursividade infinita” mais adiante.
-
- Se não estiver recebendo este erro, mas suspeita que há um problema com um método ou função recursiva, você ainda pode usar as técnicas da seção “Recursividade infinita”.
+
Se não estiver recebendo este erro, mas suspeita que há um problema com um método ou função recursiva, você ainda pode usar as técnicas da seção “Recursividade infinita”.
* Se nenhum desses passos funcionar, comece a testar outros loops e outras funções e métodos recursivos.
* Se isso não funcionar, então é possível que você não entenda o fluxo de execução do seu programa. Vá à seção “Fluxo de execução” mais adiante.
-Loop infinito
+#### Loop infinito
Se você acha que há um loop infinito e talvez saiba qual loop está causando o problema, acrescente uma instrução print no fim do loop que exiba os valores das variáveis na condição e o valor da condição.
Por exemplo:
-while x > 0 and y < 0 :
-
- \# faz algo com x
-
- \# faz algo com y
-
+```python
+while x > 0 and y < 0 :
+ # faz algo com x
+ # faz algo com y
print('x: ', x)
-
print('y: ', y)
-
- print("condition: ", (x > 0 and y < 0))
+ print("condition: ", (x > 0 and y < 0))
+```
Agora, quando executar o programa, você verá três linhas de saída para cada vez que o programa passar pelo loop. Na última vez que passar pelo loop, a condição deve ser False. Se o loop continuar, você poderá ver os valores de x e y, e compreender porque não estão sendo atualizados corretamente.
-Recursividade infinita
+#### Recursividade infinita
-Na maioria das vezes, a recursividade infinita faz o programa rodar durante algum tempo e então produzir um erro de Maximum recursion depth exceeded.
+Na maioria das vezes, a recursividade infinita faz o programa rodar durante algum tempo e então produzir um erro de "Maximum recursion depth exceeded".
Se suspeitar que uma função está causando recursividade infinita, confira se há um caso-base. Deve haver alguma condição que faz a função retornar sem fazer uma invocação recursiva. Do contrário, você terá que repensar o algoritmo e identificar um caso-base.
Se houver um caso-base, mas o programa não parece alcançá-lo, acrescente uma instrução print no início da função para exibir os parâmetros. Agora, quando executar o programa, você verá algumas linhas de saída cada vez que a função for invocada, e verá os valores dos parâmetros. Se os parâmetros não estiverem se movendo em direção ao caso-base, você terá algumas ideias sobre a razão disso.
-Fluxo de execução
+#### Fluxo de execução
Se não tiver certeza de como o fluxo de execução está se movendo pelo seu programa, acrescente instruções print ao começo de cada função com uma mensagem como “entrada na função foo”, onde foo é o nome da função.
@@ -128,11 +123,11 @@ O traceback identifica a função que está rodando atualmente, e a função que
O primeiro passo é examinar o lugar no programa onde o erro ocorreu e ver se consegue compreender o que aconteceu. Esses são alguns dos erros de tempo de execução mais comuns:
-NameError:
+#### NameError
Você está tentando usar uma variável que não existe no ambiente atual. Confira se o nome está escrito corretamente e de forma consistente. E lembre-se de que as variáveis locais são locais; você não pode se referir a elas a partir do exterior da função onde são definidas.
-TypeError:
+#### TypeError
Há várias causas possíveis:
@@ -142,11 +137,11 @@ Há várias causas possíveis:
* Você está passando o número incorreto de argumentos a uma função. Para métodos, olhe para a definição do método e verifique se o primeiro parâmetro é self. Então olhe para a invocação do método; confira se está invocando o método a um objeto com o tipo correto e fornecendo os outros argumentos corretamente.
-KeyError:
+#### KeyError
Você está tentando acessar um elemento de um dicionário usando uma chave que o dicionário não contém. Se as chaves forem strings, lembre-se de que letras maiúsculas são diferentes de minúsculas.
-AttributeError:
+#### AttributeError
Você está tentando acessar um atributo ou método que não existe. Verifique a ortografia! Você pode usar a função integrada vars para listar os atributos que existem mesmo.
@@ -154,7 +149,7 @@ Se um AttributeError indicar que um objeto é do tipo NoneType, fica subentendid
Pode ser que o objeto seja none porque você se esqueceu de retornar um valor de uma função; se chegar ao fim de uma função sem chegar a uma instrução return, ela retorna None. Outra causa comum é usar o resultado de um método de lista, como sort, que retorne None.
-IndexError:
+#### IndexError
O índice que você está usando para acessar uma lista, string ou tupla é maior que o seu comprimento menos um. Imediatamente antes do local do erro, acrescente uma instrução print para exibir o valor do índice e o comprimento do array. O array é do tamanho certo? O índice tem o valor certo?
@@ -204,27 +199,32 @@ Escrever expressões complexas é ótimo enquanto são legíveis, mas elas podem
Por exemplo:
-self.hands\[i\].addCard(self.hands\[self.findNeighbor(i)\].popCard())
-
+```python
+self.hands[i].addCard(self.hands[self.findNeighbor(i)].popCard())
+```
A expressão pode ser reescrita assim:
+```python
neighbor = self.findNeighbor(i)
-
-pickedCard = self.hands\[neighbor\].popCard()
-
-self.hands\[i\].addCard(pickedCard)
+pickedCard = self.hands[neighbor].popCard()
+self.hands[i].addCard(pickedCard)
+```
A versão explícita é mais fácil de ler porque os nomes das variáveis oferecem documentação adicional, e é mais fácil de depurar porque você pode verificar os tipos das variáveis intermediárias e exibir seus valores.
Outro problema que pode ocorrer com grandes expressões é que a ordem da avaliação pode não ser o que você espera. Por exemplo, se estiver traduzindo a expressão  para o Python, poderia escrever:
-y = x / 2 \* math.pi
+```python
+y = x / 2 * math.pi
+```
Isso não está correto porque a multiplicação e a divisão têm a mesma precedência e são avaliadas da esquerda para a direita. Então, é assim que essa expressão é calculada: xπ/2.
Uma boa forma de depurar expressões é acrescentar parênteses para tornar a ordem da avaliação explícita:
-y = x / (2 \* math.pi)
+```python
+y = x / (2 * math.pi)
+```
Sempre que não tiver certeza sobre a ordem da avaliação, use parênteses. Além de o programa ficar correto (quanto à execução do que era pretendido), ele também será mais legível para outras pessoas que não memorizaram a ordem de operações.
@@ -232,13 +232,16 @@ Sempre que não tiver certeza sobre a ordem da avaliação, use parênteses. Al
Se tiver uma instrução return com uma expressão complexa, não há possibilidade de exibir o resultado antes do retorno. Novamente, você pode usar uma variável temporária. Por exemplo, em vez de:
-return self.hands\[i\].removeMatches()
+```python
+return self.hands[i].removeMatches()
+```
você poderia escrever:
-count = self.hands\[i\].removeMatches()
-
+```python
+count = self.hands[i].removeMatches()
return count
+```
Agora você tem a oportunidade de exibir o valor de count antes do retorno.
diff --git a/docs/B-analise-algorit.md b/docs/B-analise-algorit.md
index 235ae51..e738ec7 100644
--- a/docs/B-analise-algorit.md
+++ b/docs/B-analise-algorit.md
@@ -2,13 +2,13 @@
Este apêndice é um excerto editado de Think Complexity, por Allen B. Downey, também publicado pela O’Reilly Media (2012). Depois de ler este livro aqui, pode ser uma boa ideia lê-lo também.
-Análise de algoritmos é um ramo da Ciência da Computação que estuda o desempenho de algoritmos, especialmente suas exigências de tempo de execução e requisitos de espaço. Veja http://en.wikipedia.org/wiki/Analysis\_of\_algorithms.
+Análise de algoritmos é um ramo da Ciência da Computação que estuda o desempenho de algoritmos, especialmente suas exigências de tempo de execução e requisitos de espaço. Veja http://en.wikipedia.org/wiki/Analysis_of_algorithms.
A meta prática da análise de algoritmos é prever o desempenho de algoritmos diferentes para guiar decisões de projeto.
Durante a campanha presidencial dos Estados Unidos de 2008, pediram ao candidato Barack Obama para fazer uma entrevista de emprego improvisada quando visitou a Google. O diretor executivo, Eric Schmidt, brincou, pedindo a ele “a forma mais eficiente de classificar um milhão de números inteiros de 32 bits”. Aparentemente, Obama tinha sido alertado porque respondeu na hora: “Creio que a ordenação por bolha (bubble sort) não seria a escolha certa”. Veja http://bit.ly/1MpIwTf.
-Isso é verdade: a ordenação por bolha é conceitualmente simples, mas lenta para grandes conjuntos de dados. A resposta que Schmidt procurava provavelmente é “ordenação radix” (radix sort) (http://en.wikipedia.org/wiki/Radix\_sort)[2].
+Isso é verdade: a ordenação por bolha é conceitualmente simples, mas lenta para grandes conjuntos de dados. A resposta que Schmidt procurava provavelmente é “ordenação radix” (radix sort) (http://en.wikipedia.org/wiki/Radix_sort)[2].
A meta da análise de algoritmos é fazer comparações significativas entre algoritmos, mas há alguns problemas:
@@ -40,7 +40,7 @@ Ao chegar em n=10, o algoritmo A parece bem ruim; ele é quase dez vezes mais lo
A razão fundamental é que para grandes valores de n, qualquer função que contenha um termo n2 será mais rápida que uma função cujo termo principal seja n. O termo principal é o que tem o expoente mais alto.
-Para o algoritmo A, o termo principal tem um grande coeficiente, 100, que é a razão de B ser melhor que A para um valor pequeno de n. Entretanto, apesar dos coeficientes, sempre haverá algum valor de n em que an2 > bn, para valores de a e b.
+Para o algoritmo A, o termo principal tem um grande coeficiente, 100, que é a razão de B ser melhor que A para um valor pequeno de n. Entretanto, apesar dos coeficientes, sempre haverá algum valor de n em que an2 > bn, para valores de a e b.
O mesmo argumento se aplica aos termos que não são principais. Mesmo se o tempo de execução do algoritmo A fosse n+1000000, ainda seria melhor que o algoritmo B para um valor suficientemente grande de n.
@@ -68,7 +68,7 @@ Para os termos logarítmicos, a base do logaritmo não importa; a alteração de
### Exercício B.1
-Leia a página da Wikipédia sobre a notação Grande-O (Big-Oh notation) em http://en.wikipedia.org/wiki/Big\_O\_notation e responda às seguintes perguntas:
+Leia a página da Wikipédia sobre a notação Grande-O (Big-Oh notation) em http://en.wikipedia.org/wiki/Big_O_notation e responda às seguintes perguntas:
1. Qual é a ordem de crescimento de n3 + n2? E de 1000000n3 + n2? Ou de n3 + 1000000n2?
@@ -94,11 +94,11 @@ Operações de indexação – ler ou escrever elementos em uma sequência ou di
Um loop for que atravesse uma sequência ou dicionário é normalmente linear, desde que todas as operações no corpo do loop sejam de tempo constante. Por exemplo, somar os elementos de uma lista é linear:
+```python
total = 0
-
for x in t:
-
total += x
+```
A função integrada sum também é linear porque faz a mesma coisa, mas tende a ser mais rápida porque é uma implementação mais eficiente; na linguagem da análise algorítmica, tem um coeficiente principal menor.
@@ -168,200 +168,163 @@ Para explicar como hashtables funcionam e por que o seu desempenho é tão bom,
Uso o Python para demonstrar essas implementações, mas, na vida real, eu não escreveria um código como esse no Python; bastaria usar um dicionário! Assim, para o resto deste capítulo, você tem que supor que os dicionários não existem e que quer implementar uma estrutura de dados que faça o mapa de chaves a valores. As operações que precisa implementar são:
-add(k, v):
-
-Adiciona um novo item que mapeia a chave k ao valor v. Com um dicionário de Python, d, essa operação é escrita d\[k\] = v.
+
+- add(k, v)
+- Insere um novo item que mapeia a chave k ao valor v. Com um dicionário de Python, `d`, essa operação é escrita `d[k] = v`.
-get(k):
-
-Procura e retorna o valor que corresponde à chave k. Com um dicionário de Python, d, esta operação é escrita d \[k\] ou d.get(k).
+- get(k)
+- Procura e devolve o valor que corresponde à chave k. Com um dicionário de Python, `d`, esta operação é escrita `d[k]` ou `d.get(k)`.
+
Por enquanto, vou supor que cada chave só apareça uma vez. A implementação mais simples desta interface usa uma lista de tuplas, onde cada tupla é um par chave-valor:
+```python
class LinearMap:
- def \_\_init\_\_(self):
-
- self.items = \[\]
+ def __init__(self):
+ self.items = []
def add(self, k, v):
-
self.items.append((k, v))
def get(self, k):
-
for key, val in self.items:
-
if key == k:
-
return val
-
raise KeyError
+```
-add acrescenta uma tupla chave-valor à lista de itens, o que tem tempo constante.
+`add` acrescenta uma tupla chave-valor à lista de itens, o que tem tempo constante.
-get usa um loop for para buscar na lista: se encontrar a chave-alvo, retorna o valor correspondente; do contrário, exibe um KeyError. Então get é linear.
+`get` usa um loop for para buscar na lista: se encontrar a chave-alvo, retorna o valor correspondente; do contrário, exibe um KeyError. Então get é linear.
-Uma alternativa é manter uma lista ordenada por chaves. Assim, get poderia usar uma busca por bisseção, que é O(log n). Porém, inserir um novo item no meio de uma lista é linear, então isso pode não ser a melhor opção. Há outras estruturas de dados que podem implementar add e get em tempo logarítmico, mas isso não é tão bom como tempo constante, então vamos continuar.
+Uma alternativa é manter uma lista ordenada por chaves. Assim, get poderia usar uma busca por bisseção, que é O(log n). Porém, inserir um novo item no meio de uma lista é linear, então isso pode não ser a melhor opção. Há outras estruturas de dados que podem implementar `add` e `get` em tempo logarítmico, mas isso não é tão bom como tempo constante, então vamos continuar.
-Uma forma de melhorar LinearMap é quebrar a lista de pares chave-valor em listas menores. Aqui está uma implementação chamada BetterMap, que é uma lista de cem LinearMaps. Como veremos em um segundo, a ordem de crescimento para get ainda é linear, mas BetterMap é um passo no caminho em direção a hashtables:
+Uma forma de melhorar `LinearMap` é quebrar a lista de pares chave-valor em listas menores. Aqui está uma implementação chamada `BetterMap`, que é uma lista de cem LinearMaps. Como veremos em um segundo, a ordem de crescimento para get ainda é linear, mas `BetterMap` é um passo no caminho em direção a hashtables:
+```python
class BetterMap:
- def \_\_init\_\_(self, n=100):
-
- self.maps = \[\]
-
+ def __init__(self, n=100):
+ self.maps = []
for i in range(n):
-
self.maps.append(LinearMap())
- def find\_map(self, k):
-
+ def find_map(self, k):
index = hash(k) % len(self.maps)
-
- return self.maps\[index\]
+ return self.maps[index]
def add(self, k, v):
-
- m = self.find\_map(k)
-
+ m = self.find_map(k)
m.add(k, v)
def get(self, k):
-
- m = self.find\_map(k)
-
+ m = self.find_map(k)
return m.get(k)
-\_\_init\_\_ cria uma lista de n LinearMaps.
+```
+
+`__init__` cria uma lista de n LinearMaps.
-find\_map é usada por add e get para saber em qual mapa o novo item deve ir ou em qual mapa fazer a busca.
+`find_map` é usada por add e get para saber em qual mapa o novo item deve ir ou em qual mapa fazer a busca.
-find\_map usa a função integrada hash, que recebe quase qualquer objeto do Python e retorna um número inteiro. Uma limitação desta implementação é que ela só funciona com chaves hashable. Tipos mutáveis como listas e dicionários não são hashable.
+`find_map` usa a função integrada hash, que recebe quase qualquer objeto do Python e retorna um número inteiro. Uma limitação desta implementação é que ela só funciona com chaves hashable. Tipos mutáveis como listas e dicionários não são hashable.
Objetos hashable considerados equivalentes retornam o mesmo valor hash, mas o oposto não é necessariamente verdade: dois objetos com valores diferentes podem retornar o mesmo valor hash.
-find\_map usa o operador módulo para manter os valores hash no intervalo de 0 a len(self.maps), então o resultado é um índice legal na lista. Naturalmente, isso significa que muitos valores hash diferentes serão reunidos no mesmo índice. Entretanto, se a função hash dispersar as coisas de forma consistente (que é o que as funções hash foram projetadas para fazer), então esperamos ter n/100 itens por LinearMap.
+`find_map` usa o operador módulo para manter os valores hash no intervalo de 0 a len(self.maps), então o resultado é um índice legal na lista. Naturalmente, isso significa que muitos valores hash diferentes serão reunidos no mesmo índice. Entretanto, se a função hash dispersar as coisas de forma consistente (que é o que as funções hash foram projetadas para fazer), então esperamos ter n/100 itens por `LinearMap`.
-Como o tempo de execução de LinearMap.get é proporcional ao número de itens, esperamos que BetterMap seja aproximadamente cem vezes mais rápido que LinearMap. A ordem de crescimento ainda é linear, mas o coeficiente principal é menor. Isto é bom, mas não tão bom quanto uma hashtable.
+Como o tempo de execução de `LinearMap.get` é proporcional ao número de itens, esperamos que `BetterMap` seja aproximadamente cem vezes mais rápido que `LinearMap`. A ordem de crescimento ainda é linear, mas o coeficiente principal é menor. Isto é bom, mas não tão bom quanto uma hashtable.
-Aqui (finalmente) está a ideia crucial que faz hashtables serem rápidas: se puder limitar o comprimento máximo de LinearMaps, LinearMap.get é de tempo constante. Tudo o que você precisa fazer é rastrear o número de itens e quando o número de itens por LinearMap exceder o limite, alterar o tamanho da hashtable acrescentando LinearMaps.
+Aqui (finalmente) está a ideia crucial que faz hashtables serem rápidas: se puder limitar o comprimento máximo de LinearMaps, `LinearMap.get` é de tempo constante. Tudo o que você precisa fazer é rastrear o número de itens e quando o número de itens por `LinearMap` exceder o limite, alterar o tamanho da hashtable acrescentando LinearMaps.
Aqui está uma implementação de uma hashtable:
+```python
class HashMap:
-
- def \_\_init\_\_(self):
-
+ def __init__(self):
self.maps = BetterMap(2)
-
self.num = 0
-
def get(self, k):
-
return self.maps.get(k)
-
def add(self, k, v):
-
if self.num == len(self.maps.maps):
-
self.resize()
-
self.maps.add(k, v)
-
self.num += 1
-
def resize(self):
-
- new\_maps = BetterMap(self.num \* 2)
-
+ new_maps = BetterMap(self.num * 2)
for m in self.maps.maps:
-
for k, v in m.items:
+ new_maps.add(k, v)
+ self.maps = new_maps
+```
- new\_maps.add(k, v)
-
- self.maps = new\_maps
-
-Cada HashMap contém um BetterMap; \_\_init\_\_ inicia com apenas dois LinearMaps e inicializa num, que monitora o número de itens.
+Cada `HashMap` contém um `BetterMap`; `__init__` inicia com apenas dois LinearMaps e inicializa num, que monitora o número de itens.
-get apenas despacha para BetterMap. O verdadeiro trabalho acontece em add, que verifica o número de itens e o tamanho de BetterMap: se forem iguais, o número médio de itens por LinearMap é um, então resize é chamada.
+get apenas despacha para `BetterMap`. O verdadeiro trabalho acontece em add, que verifica o número de itens e o tamanho de `BetterMap`: se forem iguais, o número médio de itens por `LinearMap` é um, então resize é chamada.
-resize faz um novo BetterMap duas vezes maior que o anterior, e então “redispersa” os itens do mapa antigo no novo.
+resize faz um novo `BetterMap` duas vezes maior que o anterior, e então “redispersa” os itens do mapa antigo no novo.
-A redispersão é necessária porque alterar o número de LinearMaps muda o denominador do operador módulo em find\_map. Isso significa que alguns objetos que costumavam ser dispersos no mesmo LinearMap serão separados (que é o que queríamos, certo?).
+A redispersão é necessária porque alterar o número de LinearMaps muda o denominador do operador módulo em `find_map`. Isso significa que alguns objetos que costumavam ser dispersos no mesmo `LinearMap` serão separados (que é o que queríamos, certo?).
A redispersão é linear, então resize é linear, o que pode parecer ruim, já que prometi que add seria de tempo constante. Entretanto, lembre-se de que não temos que alterar o tamanho a cada vez, então add normalmente é de tempo constante e só ocasionalmente linear. O volume total de trabalho para executar add n vezes é proporcional a n, então o tempo médio de cada add é de tempo constante!
-Para ver como isso funciona, pense em começar com uma HashTable vazia e adicione uma sequência de itens. Começamos com dois LinearMaps, então as duas primeiras adições são rápidas (não é necessário alterar o tamanho). Digamos que elas tomem uma unidade do trabalho cada uma. A próxima adição exige uma alteração de tamanho, então temos de redispersar os dois primeiros itens (vamos chamar isso de mais duas unidades de trabalho) e então acrescentar o terceiro item (mais uma unidade). Acrescentar o próximo item custa uma unidade, então o total, por enquanto, é de seis unidades de trabalho para quatro itens.
+Para ver como isso funciona, pense como seria começar com uma HashTable vazia e inserir uma série de itens. Começamos com dois LinearMaps, então as duas primeiras inserções são rápidas (não é necessário alterar o tamanho). Digamos que elas tomem uma unidade do trabalho cada uma. A próxima inserção exige uma alteração de tamanho, então temos de redispersar os dois primeiros itens (vamos chamar isso de mais duas unidades de trabalho) e então acrescentar o terceiro item (mais uma unidade). Acrescentar o próximo item custa uma unidade, então o total, por enquanto, é de seis unidades de trabalho para quatro itens.
-O próximo add custa cinco unidades, mas os três seguintes são só uma unidade cada um, então o total é de 14 unidades para as primeiras oito adições.
+O próximo add custa cinco unidades, mas os três seguintes são só uma unidade cada um, então o total é de 14 unidades para as primeiras oito inserções.
-O próximo add custa nove unidades, mas então podemos adicionar mais sete antes da próxima alteração de tamanho, então o total é de 30 unidades para as primeiras 16 adições.
+O próximo add custa nove unidades, mas então podemos inserir mais sete antes da próxima alteração de tamanho, então o total é de 30 unidades para as primeiras 16 inserções.
-Depois de 32 adições, o custo total é de 62 unidades, e espero que você esteja começando a ver um padrão. Depois de n adições, nas quais n é uma potência de dois, o custo total é de 2n-2 unidades, então o trabalho médio por adição é um pouco menos de duas unidades. Quando n é uma potência de dois, esse é o melhor caso; para outros valores de n, o trabalho médio é um pouco maior, mas isso não é importante. O importante é que seja O(1).
+Depois de 32 inserções, o custo total é de 62 unidades, e espero que você esteja começando a ver um padrão. Depois de n inserções, nas quais n é uma potência de dois, o custo total é de 2n-2 unidades, então o trabalho médio por inserção é um pouco menos de duas unidades. Quando n é uma potência de dois, esse é o melhor caso; para outros valores de n, o trabalho médio é um pouco maior, mas isso não é importante. O importante é que seja O(1).
-A Figura 21.1 mostra graficamente como isso funciona. Cada bloco representa uma unidade de trabalho. As colunas mostram o trabalho total para cada adição na ordem da esquerda para a direita: os primeiros dois adds custam uma unidade, o terceiro custa três unidades etc.
+A Figura 21.1 mostra graficamente como isso funciona. Cada bloco representa uma unidade de trabalho. As colunas mostram o trabalho total para cada inserção na ordem da esquerda para a direita: os primeiros dois adds custam uma unidade, o terceiro custa três unidades etc.
-Figura 21.1 – O custo de adições em uma hashtable.
+
+
_Figura B.1 – O custo de inserções em uma hashtable._
-O trabalho extra de redispersão aparece como uma sequência de torres cada vez mais altas com um aumento de espaço entre elas. Agora, se derrubar as torres, espalhando o custo de alterar o tamanho por todas as adições, poderá ver graficamente que o custo total depois de n adições é de 2n − 2.
+O trabalho extra de redispersão aparece como uma sequência de torres cada vez mais altas com um aumento de espaço entre elas. Agora, se derrubar as torres, espalhando o custo de alterar o tamanho por todas as inserções, poderá ver graficamente que o custo total depois de n inserções é de 2n − 2.
Uma característica importante deste algoritmo é que quando alteramos o tamanho da HashTable, ela cresce geometricamente; isto é, multiplicamos o tamanho por uma constante. Se você aumentar o tamanho aritmeticamente – somando um número fixo de cada vez – o tempo médio por add é linear.
-Você pode baixar minha implementação de HashMap em http://thinkpython2.com/code/Map.py, mas lembre-se de que não há razão para usá-la; se quiser um mapa, basta usar um dicionário do Python.
+Você pode baixar minha implementação de `HashMap` em http://thinkpython2.com/code/Map.py, mas lembre-se de que não há razão para usá-la; se quiser um mapa, basta usar um dicionário do Python.
## B.5 - Glossário
-__análise de algoritmos__
-Forma de comparar algoritmos quanto às suas exigências de espaço e/ou tempo de execução.
-
-__modelo de máquina__
-Representação simplificada de um computador usada para descrever algoritmos.
-
-__pior caso__
-Entrada que faz um dado algoritmo rodar mais lentamente (ou exigir mais espaço).
-
-__termo principal__
-Em um polinômio, o termo com o expoente mais alto.
-
-__ponto de desvio__
-Tamanho do problema em que dois algoritmos exigem o mesmo tempo de execução ou espaço.
-
-__ordem de crescimento__
-Conjunto de funções em que todas crescem em uma forma considerada equivalente para os propósitos da análise de algoritmos. Por exemplo, todas as funções que crescem linearmente pertencem à mesma ordem de crescimento.
-
-__notação Grande-O (Big-Oh notation)__
-Notação para representar uma ordem de crescimento; por exemplo, O(n) representa o conjunto de funções que crescem linearmente.
-
-__linear__
-Algoritmo cujo tempo de execução é proporcional ao tamanho do problema, pelo menos para grandes tamanhos de problema.
-
-__quadrático__
-Algoritmo cujo tempo de execução é proporcional a n2, onde n é uma medida de tamanho do problema.
+
+- análise de algoritmos
+- Forma de comparar algoritmos quanto às suas exigências de espaço e/ou tempo de execução.
-__busca__
-Problema de localizar um elemento de uma coleção (como uma lista ou dicionário) ou de decidir que não está presente.
+- modelo de máquina
+- Representação simplificada de um computador usada para descrever algoritmos.
-__hashtable__
-Estrutura de dados que representa uma coleção de pares chave-valor e executa buscas em tempo constante.
+- pior caso
+- Entrada que faz um dado algoritmo rodar mais lentamente (ou exigir mais espaço).
-## B.6 - Sobre o autor
+- termo principal
+- Em um polinômio, o termo com o expoente mais alto.
-Allen Downey é professor de Ciência da Computação no Olin College of Engineering. Ele já ensinou no Wellesley College, Colby College e na U.C. Berkeley. É doutor em Ciência da Computação pela U.C. Berkeley e mestre e graduado pelo MIT.
+- ponto de desvio
+- Tamanho do problema em que dois algoritmos exigem o mesmo tempo de execução ou espaço.
-Colofão
+- ordem de crescimento
+- Conjunto de funções em que todas crescem em uma forma considerada equivalente para os propósitos da análise de algoritmos. Por exemplo, todas as funções que crescem linearmente pertencem à mesma ordem de crescimento.
-O animal na capa de Pense em Python é um papagaio-da-carolina, também conhecido como periquito-da-carolina (Conuropsis carolinensis). Este papagaio habitava o sudeste dos Estados Unidos e foi o único papagaio continental a habitar regiões acima do norte do México. Um dia, vivia no norte, em locais tão distantes quanto Nova Iorque e os Grandes Lagos, embora fosse encontrado principalmente na região da Flórida às Carolinas.
+- notação Grande-O (Big-Oh notation)
+- Notação para representar uma ordem de crescimento; por exemplo, O(n) representa o conjunto de funções que crescem linearmente.
-O papagaio-da-carolina era quase todo verde com a cabeça amarela e, na maturidade, tinha uma coloração laranja na testa e na face. Seu tamanho médio variava de 31 a 33 cm. Tinha uma voz alta, ruidosa e palrava constantemente enquanto se alimentava. Habitava troncos de árvores ocos perto de brejos e barrancos. O papagaio-da-carolina era um animal muito gregário, que vivia em pequenos grupos os quais podiam chegar a várias centenas quando se alimentavam.
+- linear
+- Algoritmo cujo tempo de execução é proporcional ao tamanho do problema, pelo menos para grandes tamanhos de problema.
-Infelizmente, essas áreas de alimentação muitas vezes eram as plantações de agricultores, que disparavam nos pássaros para mantê-los longe das plantas. A natureza social dos pássaros fazia com que eles voassem ao resgate de qualquer papagaio ferido, permitindo aos agricultores derrubar bandos inteiros de cada vez. Além disso, suas penas eram usadas para embelezar chapéus de senhoras, e alguns papagaios eram mantidos como mascotes. Uma combinação desses fatores levou o papagaio-da-carolina a tornar-se raro no fim dos anos 1800, e as doenças de aves domésticas podem ter contribuído para diminuir seu número. Pelos anos 1920, a espécie estava extinta.
+- quadrático
+- Algoritmo cujo tempo de execução é proporcional a n2, onde n é uma medida de tamanho do problema.
-Hoje, há mais de 700 espécimes de papagaios-da-carolina conservados em museus no mundo inteiro.
+- busca
+- Problema de localizar um elemento de uma coleção (como uma lista ou dicionário) ou de decidir que não está presente.
-Muitos dos animais nas capas de livros da O’Reilly estão em perigo de extinção; todos eles são importantes para o mundo. Para saber mais sobre como você pode ajudar, acesse animals.oreilly.com. A imagem da capa é do livro Johnson’s Natural History.
+- hashtable
+- Estrutura de dados que representa uma coleção de pares chave-valor e executa buscas em tempo constante.
+
[1] popen foi descartado, ou seja, devemos parar de usá-lo e começar a usar o módulo subprocess. Entretanto, para casos simples, eu considero subprocess mais complicado que o necessário. Então vou continuar usando popen até que o removam.
diff --git a/docs/C-colofao-autor.md b/docs/C-colofao-autor.md
new file mode 100644
index 0000000..3e849a2
--- /dev/null
+++ b/docs/C-colofao-autor.md
@@ -0,0 +1,17 @@
+# Colofão
+
+
+
+O animal na capa de Pense em Python é um papagaio-da-carolina, também conhecido como periquito-da-carolina (Conuropsis carolinensis). Este papagaio habitava o sudeste dos Estados Unidos e foi o único papagaio continental a habitar regiões acima do norte do México. Um dia, vivia no norte, em locais tão distantes quanto Nova Iorque e os Grandes Lagos, embora fosse encontrado principalmente na região da Flórida às Carolinas.
+
+O papagaio-da-carolina era quase todo verde com a cabeça amarela e, na maturidade, tinha uma coloração laranja na testa e na face. Seu tamanho médio variava de 31 a 33 cm. Tinha uma voz alta, ruidosa e palrava constantemente enquanto se alimentava. Habitava troncos de árvores ocos perto de brejos e barrancos. O papagaio-da-carolina era um animal muito gregário, que vivia em pequenos grupos os quais podiam chegar a várias centenas quando se alimentavam.
+
+Infelizmente, essas áreas de alimentação muitas vezes eram as plantações de agricultores, que disparavam nos pássaros para mantê-los longe das plantas. A natureza social dos pássaros fazia com que eles voassem ao resgate de qualquer papagaio ferido, permitindo aos agricultores derrubar bandos inteiros de cada vez. Além disso, suas penas eram usadas para embelezar chapéus de senhoras, e alguns papagaios eram mantidos como mascotes. Uma combinação desses fatores levou o papagaio-da-carolina a tornar-se raro no fim dos anos 1800, e as doenças de aves domésticas podem ter contribuído para diminuir seu número. Pelos anos 1920, a espécie estava extinta.
+
+Hoje, há mais de 700 espécimes de papagaios-da-carolina conservados em museus no mundo inteiro.
+
+Muitos dos animais nas capas de livros da O’Reilly estão em perigo de extinção; todos eles são importantes para o mundo. Para saber mais sobre como você pode ajudar, acesse animals.oreilly.com. A imagem da capa é do livro Johnson’s Natural History.
+
+## Sobre o autor
+
+Allen Downey é professor de Ciência da Computação no Olin College of Engineering. Ele já ensinou no Wellesley College, Colby College e na U.C. Berkeley. É doutor em Ciência da Computação pela U.C. Berkeley e mestre e graduado pelo MIT.
diff --git a/docs/index.md b/docs/index.md
index 8eca42d..d196313 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -38,7 +38,7 @@ Este livro ensina programação para quem nunca programou, usando [Python 3](htt
* [Apêndice A: Depuração](A-depuracao.md)
* [Apêndice B: Análise de algoritmos](B-analise-algorit.md)
-
+* [Colofão / Sobre o Autor](C-colofao-autor.md)
## Créditos da edição brasileira
diff --git a/ebooks/PenseEmPython2e.epub b/ebooks/PenseEmPython2e.epub
new file mode 100644
index 0000000..98f9ee6
Binary files /dev/null and b/ebooks/PenseEmPython2e.epub differ
diff --git a/ebooks/metadata.xml b/ebooks/metadata.xml
new file mode 100644
index 0000000..898792d
--- /dev/null
+++ b/ebooks/metadata.xml
@@ -0,0 +1,4 @@
+Pense em Python
+Allen Downey
+Creative Commons
+pt-BR
\ No newline at end of file
diff --git a/img/Capa_PenseEmPython332x461-borda.png b/img/Capa_PenseEmPython332x461-borda.png
new file mode 100644
index 0000000..7efda66
Binary files /dev/null and b/img/Capa_PenseEmPython332x461-borda.png differ
diff --git a/util/epub.py b/util/epub.py
new file mode 100755
index 0000000..29a767b
--- /dev/null
+++ b/util/epub.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+
+from glob import glob
+import shutil
+import subprocess
+import os
+
+
+URL_REPO = 'https://github.com/PenseAllen/PensePython2e/raw/master'
+
+
+def _project_path():
+ return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+def _get_path(p):
+ return os.path.join(_project_path(), p)
+
+
+def _fix_images_url(files, tmp_dir):
+ """ Corrige as urls das imagens.
+ Por algum motivo o pandoc não estava conseguindo baixar as imagens
+ do site para embedar no epub. Com esse código eu gero arquivos
+ temporários com as essas referências acertadas para a mesma imagem
+ dentro do projeto.
+ """
+ os.makedirs(tmp_dir)
+ fixed_files = []
+ fixed_path = os.path.relpath(_project_path())
+ for fp in files:
+ file_name = os.path.split(fp)[1]
+ output = os.path.join(tmp_dir, file_name)
+ with open(fp) as input_file:
+ fixed_str = input_file.read().replace(URL_REPO, fixed_path)
+ with open(output, 'w') as output_file:
+ output_file.write(fixed_str)
+ fixed_files.append(output)
+ return fixed_files
+
+
+def main():
+ glob_filter = _get_path('docs/*.md')
+ capa = _get_path('img/Capa_PenseEmPython332x461-borda.png')
+ metadata = _get_path('ebooks/metadata.xml')
+ output = _get_path('ebooks/PenseEmPython2e.epub')
+ files = glob(glob_filter)
+ files.sort()
+ files.pop()
+ tmp_dir = _get_path('tmp')
+ files = _fix_images_url(files, tmp_dir)
+ # Monta os argumentos do pandoc
+ args = ['pandoc', '--epub-cover-image=' + capa, '--epub-metadata=' + metadata]
+ args.extend(files)
+ args.append('-o')
+ args.append(output)
+ # Executa o pandoc
+ subprocess.call(args)
+ # Remove arquivos temporários
+ shutil.rmtree(tmp_dir)
+ print('Gerado epub em', output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/util/marcar_listagens.py b/util/marcar_listagens.py
index ab9b0d0..85d620b 100755
--- a/util/marcar_listagens.py
+++ b/util/marcar_listagens.py
@@ -47,7 +47,8 @@ def main():
caminho_arq = sys.argv[1]
with open(caminho_arq, encoding='utf8') as fp:
md = fp.read()
- md = marcar_listagens(marcar_verbetes(md))
+ #md = marcar_verbetes(md)
+ md = marcar_listagens(md)
with open(caminho_arq.replace('-', 'list-', 1), 'wt', encoding='utf8') as fp:
fp.write(md)