Commits

Francisco Souza committed ae5e0db

Padronização de endentação de código com dois espaços no Capítulo 16.

A endentação do capítulo 16 estava despadrozinada.

Comments (0)

Files changed (1)

edicao_1.1/capitulo_16.rst

 
 Uma das características mais marcantes das linguagens orientadas a objetos é a **herança**. Herança é a habilidade de definir uma nova classe que é uma versão modificada de uma classe existente.
 
-A principal vantagem dessa característica é que você pode adicionar novos métodos a uma classe sem ter que modificar a classe existente. Chama-se "herança" porque a nova classe herda todos os métodos da classe existente. Ampliando a metáfora, podemos dizer que a classe existente é às vezes chamada de classe **mãe** (*parent*). A nova classe pode ser chamada de classe **filha** ou, simplesmente, "subclasse".
+A principal vantagem dessa característica é que você pode adicionar novos métodos a uma classe sem ter que modificar a classe existente. Chama-se "herança" por que a nova classe herda todos os métodos da classe existente. Ampliando a metáfora, podemos dizer que a classe existente é às vezes chamada de classe **mãe** (ou superclasse). A nova classe pode ser chamada de classe **filha** ou, simplesmente, "subclasse".
 
 A herança é uma característica poderosa. Alguns programas que seriam complicados sem herança podem ser escritos de forma simples e concisa graças a ela. E a herança também pode facilitar o reuso do código, uma vez que você pode adaptar o comportamento de classes existentes sem ter que modificá-las. Em alguns casos, a estrutura da herança reflete a natureza real do problema, tornando o programa mais fácil de entender.
 
-Por outro lado, a herança pode tornar um programa seja difícil de ler. Quando um método é invocado, às vezes não está claro onde procurar sua definição. A parte relevante do código pode ser espalhada em vários módulos. E, também, muitas das coisas que podem ser feitas utilizando herança também podem ser feitas de forma igualmente elegante (ou até mais) sem ela. Se a estrutura natural do problema não se presta a utilizar herança, esse estilo de programação pode trazer mais problemas que vantagens.
+Por outro lado, a herança pode tornar um programa difícil de ler. Quando um método é invocado, às vezes não fica claro onde procurar sua definição. A parte relevante do código pode ser espalhada em vários módulos. E, também, muitas das coisas que podem ser feitas utilizando herança também podem ser feitas de forma igualmente elegante (ou até mais) sem ela. Se a estrutura natural do problema não se presta a utilizar herança, esse estilo de programação pode trazer mais problemas que vantagens.
 
-Nesse capítulo, vamos demonstrar o uso de herança como parte de um programa que joga uma variante de Mico. Um dos nossos objetivos é escrever um código que possa ser reutilizado para implementar outros jogos de cartas.
+Neste capítulo, vamos demonstrar o uso de herança como parte de um programa que joga uma variante de Mico. Um dos nossos objetivos é escrever um código que possa ser reutilizado para implementar outros jogos de cartas.
 
 -----------------------
 16.2 Uma mão de cartas
 
 Na definição de classe, o nome da classe pai aparece entre parênteses::
 
- class Mao(Baralho):
-   pass
+  class Mao(Baralho):
+    pass
 
 Esse comando indica que a nova classe ``Mao`` herda da classe existente ``Baralho``.
 
 O construtor de ``Mao`` inicializa os atributos da mão, que são ``nome`` e ``cartas``. A string ``nome`` identifica essa mão, provavelmente pelo nome do jogador que está segurando as cartas. O nome é um parâmetro opcional com a string vazia como valor default. ``cartas`` é a lista de cartas da mão, inicializada com uma lista vazia ::
 
- class Mao(Baralho):
-   def __init__(self, nome=""):
-     self.cartas = []
-     self.nome = nome
+  class Mao(Baralho):
+    def __init__(self, nome=""):
+      self.cartas = []
+      self.nome = nome
 
 Em praticamente todos os jogos de cartas, é necessario adicionar e remover cartas do baralho. Remover cartas já está resolvido, uma vez que ``Mao`` herda ``removerCarta`` de ``Baralho``. Mas precisamos escrever ``adicionarCarta``::
 
- class Mao(Baralho):
-   #...
-   def adicionarCarta(self,carta):
-     self.cartas.append(carta)
+  class Mao(Baralho):
+    #...
+    def adicionarCarta(self,carta):
+      self.cartas.append(carta)
 
-De novo, a elipse indica que omitimos outros métodos. O método de listas ``append`` adiciona a nova carta no final da lista de cartas. 
+De novo, omitimos outros métodos definidos anteriormente. O método ``append`` adiciona a nova carta no final da lista de cartas. 
 
 ---------------------
 16.3 Dando as cartas
 
 ``distribuir`` recebe dois argumentos, uma lista (ou tupla) de mãos e o numero total de cartas a serem dadas. Se não houver cartas suficientes no baralho, o método dá todas as cartas e pára::
 
- class Baralho:
-   #...
-   def distribuir(self, maos, nCartas=999):
-     nMaos = len(maos)
-     for i in range(nCartas):
-       if self.estahVazia(): break    # interromper se acabaram as cartas
-       carta = self.pegarCarta()      # pegar a carta do topo
-       mao = maos[i % nMaos]       # quem deve receber agora?
-       mao.adicionarCarta(carta)   # adicionar a carta à mao
+  class Baralho:
+    #...
+    def distribuir(self, maos, nCartas=999):
+      nMaos = len(maos)
+      for i in range(nCartas):
+        if self.estahVazia(): break    # interromper se acabaram as cartas
+        carta = self.pegarCarta()      # pegar a carta do topo
+        mao = maos[i % nMaos]       # quem deve receber agora?
+        mao.adicionarCarta(carta)   # adicionar a carta à mao
 
 O segundo parâmetro, ``nCartas``, é opcional; o default é um número grande, o que na prática significa que todas as cartas do baralho serão dadas se este parâmetro for omitido.
 
 O operador módulo (%) permite dar cartas em ao redor da mesa (uma carta de cada vez para cada mão). Quando ``i`` é igual ao numero de mãos na lista, a expressão ``i % nMaos`` volta para o começo da lista (índice 0).
 
 -----------------------
-16.4 Exibindo a mao
+16.4 Exibindo a ``Mao``
 -----------------------
 
 Para exibir o conteúdo de uma mão, podemos tirar vantagem dos métodos ``exibirBaralho`` e ``__str__`` herdados de ``Baralho``. Por exemplo::
 
 Embora seja conveniente herdar os métodos existentes, há outras informacoes num objeto ``Mao`` que podemos querer incluir quando ao exibí-lo. Para fazer isso, podemos fornecer um método ``__str__`` para a classe ``Mao`` que sobrescreva o da classe ``Baralho``::
 
- class Mao(Baralho)
-   #...
-   def __str__(self):
-     s = "Mao " + self.nome
-     if self.estahVazia():
-       return s + " está vazia\n"
-     else:
-       return s + " contém\n" + Baralho.__str__(self)
+  class Mao(Baralho)
+    #...
+    def __str__(self):
+      s = "Mao " + self.nome
+      if self.estahVazia():
+        return s + " está vazia\n"
+      else:
+        return s + " contém\n" + Baralho.__str__(self)
 
 Inicialmente, ``s`` é uma string que identifica a mão. Se a mão estiver vazia, o programa acrescenta as palavras ``está vazia`` e retorna o resultado.
 
 
 Pode parecer estranho enviar ``self``, que se refere à ``Mao`` corrente, para um método ``Baralho``, mas isso só até voce se lembrar que um ``Mao`` é um tipo de ``Baralho``. Objetos ``Mao`` podem fazer tudo que os objetos ``Baralho`` fazem, entao, é permitido passar uma instância de ``Mao`` para um método ``Baralho``.
 
-Em geral, sempre é permitido usar uma instância de uma subclasse no lugar de uma instância de uma classe mãe.
+Em geral, sempre é permitido usar uma instância de uma subclasse no lugar de uma instância de uma superclasse.
 
 ------------------------------
 16.5 A classe ``JogoDeCartas``
 ------------------------------
 
-A classe ``JogoDeCartas`` toma conta de algumas tarefas básicas comuns a todos os jogos, como, criar o baralho e embaralhá-lo::
+A classe ``JogoDeCartas`` toma conta de algumas tarefas básicas comuns a todos os jogos, como criar o baralho e embaralhá-lo::
 
- class JogoDeCartas:
-   def __init__(self):
-     self.baralho = Baralho()
-     self.baralho.embaralhar()
+  class JogoDeCartas:
+    def __init__(self):
+      self.baralho = Baralho()
+      self.baralho.embaralhar()
 
 Este é o primeiro dos casos que vimos até agora em que o método de inicialização realiza uma computação significativa, para além de inicializar atributos.
 
 
 Uma mão para jogar Mico requer algumas habilidades para alem das habilidades gerais de uma ``Mao``. Vamos definir uma nova classe, ``MaoDeMico``, que herda de ``Mao`` e provê um método adicional chamado ``descartarCasais``::
 
- class MaoDeMico(Mao):
-   def descartarCasais(self):
-     conta = 0
-     cartasIniciais = self.cartas[:]
-     for carta in cartasIniciais:
-       casal = Carta(3 - carta.naipe, carta.valor)
-       if casal in self.cartas:
-         self.cartas.remove(carta)
-         self.cartas.remove(casal)
-         print "Mao %s: %s casais %s" % (self.nome,carta,casal)
-         conta = conta + 1
-     return conta
+  class MaoDeMico(Mao):
+    def descartarCasais(self):
+      conta = 0
+      cartasIniciais = self.cartas[:]
+      for carta in cartasIniciais:
+        casal = Carta(3 - carta.naipe, carta.valor)
+        if casal in self.cartas:
+          self.cartas.remove(carta)
+          self.cartas.remove(casal)
+          print "Mao %s: %s casais %s" % (self.nome,carta,casal)
+          conta = conta + 1
+      return conta
 
 Começamos fazendo uma cópia da lista de cartas, para poder percorrer a cópia enquanto removemos cartas do original. Uma vez que ``self.cartas`` é modificada no laço, não queremos usá-la para controlar o percurso. Python pode ficar bem confuso se estiver percorrendo uma lista que está mudando!
 
 
 Já que ``__init__`` é herdado de ``JogoDeCartas``, um novo objeto ``Mico`` contém um novo baralho embaralhado::
 
- class Mico(JogoDeCartas):
-   def jogar(self, nomes):
-     # remover a Dama de paus
-     self.baralho.removerCarta(Carta(0,12))
+  class Mico(JogoDeCartas):
+    def jogar(self, nomes):
+      # remover a Dama de paus
+      self.baralho.removerCarta(Carta(0,12))
 
-     # fazer uma mão para cada jogador
-     self.maos = []
-     for nome in nomes :
-       self.maos.append(MaoDeMico(nome))
+      # fazer uma mão para cada jogador
+      self.maos = []
+      for nome in nomes :
+        self.maos.append(MaoDeMico(nome))
 
-     # distribuir as cartas
-     self.baralho.distribuir(self.maos)
-     print "---------- As cartas foram dadas"
-     self.exibirMaos()
+      # distribuir as cartas
+      self.baralho.distribuir(self.maos)
+      print "---------- As cartas foram dadas"
+      self.exibirMaos()
 
-     # remover casais iniciais
-     casais = self.removerTodosOsCasais()
-     print "---------- Os pares foram descartados, o jogo começa"
-     self.exibirMaos()
+      # remover casais iniciais
+      casais = self.removerTodosOsCasais()
+      print "---------- Os pares foram descartados, o jogo começa"
+      self.exibirMaos()
 
-     # jogar até que 25 casais se formem
-     vez = 0
-     numMaos = len(self.maos)
-     while casais < 25:
-       casais = casais + self.jogarVez(vez)
-       vez = (vez + 1) % numMaos
+      # jogar até que 25 casais se formem
+      vez = 0
+      numMaos = len(self.maos)
+      while casais < 25:
+        casais = casais + self.jogarVez(vez)
+        vez = (vez + 1) % numMaos
 
-     print "---------- Fim do jogo"
-     self.exibirMaos()
+      print "---------- Fim do jogo"
+      self.exibirMaos()
 
 Algumas etapas do jogo foram separadas em métodos. ``removerTodosOsCasais`` percorre a lista de mãos e invoca ``descartarCasais`` em cada uma::
 
- class Mico(JogoDeCartas):
-   #...
-   def removerTodosOsCasais(self):
-     conta = 0
-     for mao in self.maos:
-       conta = conta + mao.descartarCasais()
-     return conta
-
-    Como exercício, escreva ``exibirMaos`` que percorre ``self.maos`` e exibe cada mão. 
+  class Mico(JogoDeCartas):
+    #...
+    def removerTodosOsCasais(self):
+      conta = 0
+      for mao in self.maos:
+        conta = conta + mao.descartarCasais()
+      return conta
 
 ``conta`` é uma acumulador que soma o número de pares em cada mão e retorna o total.
 
+  Como exercício, escreva ``exibirMaos`` que percorre ``self.maos`` e exibe cada mão. 
+
 Quando o número total de pares alcança 25, 50 cartas foram removidas das mãos, o que significa que sobrou só uma carta e o jogo chegou ao fim.
 
 A variável ``vez`` mantém controle sobre de quem é a vez de jogar. Começa em 0 e incrementa de um em um; quando atinge ``numMaos``, o operador módulo faz ela retornar para 0.
 
 O método ``jogarVez`` recebe um argumento que indica de quem é a vez de jogar. O valor de retorno é o número de pares feitos durante essa rodada::
 
- class Mico(JogoDeCartas):
-   #...
-   def jogarVez(self, i):
-     if self.maos[i].estahVazia():
-       return 0
-     vizinho = self.buscarVizinho(i)
-     novaCarta = self.maos[vizinho].pegarCarta()
-     self.maos[i].adicionarCarta(novaCarta)
-     print "Mao", self.maos[i].nome, "pegou", novaCarta
-     conta = self.maos[i].descartarCasais()
-     self.maos[i].embaralhar()
-     return conta
+  class Mico(JogoDeCartas):
+    #...
+    def jogarVez(self, i):
+      if self.maos[i].estahVazia():
+        return 0
+      vizinho = self.buscarVizinho(i)
+      novaCarta = self.maos[vizinho].pegarCarta()
+      self.maos[i].adicionarCarta(novaCarta)
+      print "Mao", self.maos[i].nome, "pegou", novaCarta
+      conta = self.maos[i].descartarCasais()
+      self.maos[i].embaralhar()
+      return conta
 
 Se a mão de um jogador estiver vazia, ele está fora do jogo, então, ele não faz nada e retorna 0.
 
 
 O método ``buscarVizinho`` começa com o jogador imediatamente à esquerda e continua ao redor da mesa até encontrar um jogador que ainda tenha cartas::
 
- class Mico(JogoDeCartas):
-   #...
-   def buscarVizinho(self, i):
-     numMaos = len(self.maos)
-     for next in range(1,numMaos):
-       vizinho = (i + next) % numMaos
-       if not self.maos[vizinho].estahVazia():
-         return vizinho
+  class Mico(JogoDeCartas):
+    #...
+    def buscarVizinho(self, i):
+      numMaos = len(self.maos)
+      for next in range(1,numMaos):
+        vizinho = (i + next) % numMaos
+        if not self.maos[vizinho].estahVazia():
+          return vizinho
 
 Se ``buscarVizinho`` alguma vez circulasse pela mesa sem encontrar cartas, retornaria ``None`` e causaria um erro em outra parte do programa. Felizmente, podemos provar que isso nunca vai acontecer (desde que o fim do jogo seja detectado corretamente).