Source

aprendacompy / edicao_1.1 / capitulo_14.rst

Full commit

Capítulo 14: Classes e métodos

14.1 Características da orientação a objetos

Python é uma linguagem de programação orientada a objetos, o que significa que ela tem características que suportam a programação orientada a objetos.

Não é fácil definir programação orientada a objetos, mas já vimos algumas de suas características:

  • Programas são construídos sobre definições de objetos e funções, e a maioria das operações é expressa sobre objetos.
  • Cada definição de objeto corresponde a algum objeto ou conceito do mundo real, e as funções que operam com aqueles objetos correspondem à maneira como os objetos do mundo real interagem.

Por exemplo, a classe Horario, definida no capítulo 13, corresponde à maneira como as pessoas denotam as horas do dia, e as funções que definimos correspondem às coisas que a pessoas fazem com uma determinada hora. Do mesmo modo, as classes Ponto e Retangulo correspondem aos conceitos matemáticos de um ponto e de um retângulo, respectivamente.

Até aqui, não tiramos vantagem dos recursos do Python que suportam a programação orientada a objetos. De maneira estrita, estas características não são necessárias. Na maioria das vezes, elas fornecem uma sintaxe alternativa para as coisas que já fizemos, mas em muitos casos a alternativa é mais concisa e faz mais sentido na estrutura do programa.

Por exemplo, no programa Horario, não existe uma conexão óbvia entre a definição da classe e a definição da função que segue. Podemos observar que toda função toma pelo menos um objeto Horaro na lista de parâmetros.

Esta observação é a motivação por trás dos métodos. Já temos visto alguns métodos, tais como keys e values, que foram invocados em dicionários. Cada método é associado a classe e é invocado em instâncias daquela classe.

Métodos são simplesmente como funções, com duas diferenças:

  • Métodos são definidos dentro da definição de uma classe, o que torna explícita a relação entre a classe e o método.
  • A sintaxe para a chamada de um método é diferente da sintaxe para a chamada de uma função.

Nas próximas seções, vamos pegar as funções dos dois capítulos anteriores e transformá-las em métodos. Esta transformação é puramente mecânica: você pode consegui-la simplesmente seguindo uma sequência de passos. Se você se sentir confortável convertendo de uma forma para a outra, você estará apto para escolher a melhor forma para qualquer coisa que você estiver fazendo.

14.2 Método imprimirHorario

No capítulo 13, definimos uma classe chamada Horario e você escreveu uma função chamada imprimirHorario (no primeiro exercício), que deve ter ficado mais ou menos assim:

class Horario:
  pass

def imprimirHorario(horario)
  print str(horario.horas) + ':' + \
    str(horario.minutos) + ':' + \
    str(horario.segundos)

Para chamar esta função, passamos um objeto Horario como um parâmetro:

>>> horaCorrente = Horario()
>>> horaCorrente.horas = 9
>>> horaCorrente.minutos = 14
>>> horaCorrente.segundos = 30
>>> imprimirHorario(horaCorrente)

Para fazer de imprimirHorario um método, tudo o que temos a fazer é mover a definição da função para dentro da definição da classe. Note a mudança na endentação:

class Horario:
  def imprimirHorario(horario):
    print str(horario.horas) + ':' + \
      str(horario.minutos) + ':' + \
      str(horario.segundos)

Agora podemos chamar imprimirHorario usando a natação de ponto:

>>> horaCorrente.imprimirHorario()

Como já vimos, o objeto no qual o método é invocado aparece antes do ponto e o nome do método aparece depois do ponto.

O objeto no qual o método é invocado é atribuído ao primeiro parâmetro, então, neste caso, horaCorrente é atribuído ao parâmetro horario.

Por convenção, o primeiro parâmetro de um método é chamado self. A razão para isto é um pouco confusa, mas é baseada numa metáfora útil.

A sintaxe para uma chamada de função, imprimirHorario(horaCorrente), sugere que a função é um agente ativo. Diz algo como, 'Ei, exibeHora! Aqui está um objeto para você exibir.'.

Na programação orientada a objetos, os objetos são agentes ativos. Uma chamado do tipo horaCorrente.imprimirHorario() diz 'Ei, horaCorrente! Por favor exiba-se a si mesmo!'.

Esta mudança de perspectiva pode ser mais polida e bela, mas não demonstra a utilidade que existe no uso de métodos ao invés de funções. Nos exemplos que vimos até aqui, poder ser que não haja tanta utilidade em usar métodos. Mas às vezes, deslocar a responsabilidade das funções para cima dos objetos torna possível escrever funções mais versáteis, e torna mais fácil manter e reutilizar o código.

14.3 Um outro exemplo

Vamos converter incrementar (da Seção 13.3) em um método. Para poupar espaço, deixaremos de fora métodos definidos previamente, mas você deve mantê-los em sua versão:

class Horario:
  # Métodos definidos previamente devem estar aqui

  def incrementar(self, segundos):
    self.segundos = segundos + self.segundos

    while self.segundos >= 60:
      self.segundos = self.segundos - 60
      self.minutos = self.minutos + 1

    while self.minutos >= 60:
      self.minutos = self.minutos - 60
      self.horas = self.horas + 1

A transformação é puramente manual e mecânica: movemos a definição do método para dentro da definição da classe e mudamos o nome do primeiro parâmetro.

Agora podemos chamar incrementar como um método:

horaCorrente.incrementar(500)

De novo, o objeto no qual o método é chamado é atribuído ao primeiro parâmetro, self. O segundo parâmetro, segundos recebe o valor 500.

Como exercício, converta 'converterParaSegundos' (da Seção 13.5) para um método na classe Horario.

14.4 Um exemplo mais complicado

A função vemDepois (feita por você no segundo exercício do Capítulo 13) é um pouco mais complicada, já que ela trabalha com dois objetos do tipo Horario, e não apenas um. Quando isto ocorre, nós chamaos apenas o primeiro parâmetro de self, o outro continua o mesmo:

class Horario:
  # Métodos definidos previamente devem ficar aqui

  def vemDepois(self, h2):
    if self.horas > h2.horas:
      return True

    if self.horas < h2.horas:
      return False

    if self.minutos > h2.minutos:
      return True

    if self.minutos < h2.minutos:
      return False

    if self.segundos > h2.segundos:
      return True

    return False

Nós invocamos este método através da notação do ponto em um objeto, passando o outro objeto Horario por parâmetro:

if horaDeAcabar.vemDepois(horaAtual):
  print "Ainda não acabou"

Você pode ler isso facilmente em linguagem humana: "Se a hora de acabar vem depois da hora atual, então...".

14.5 Argumentos opcionais

Vimos algumas funções do Python que recebiam um número variável de argumentos. Por exemplo, string.find pode receber dois, três ou quatro argumentos.

Também podemos escrever nossas funções com argumentos opcionais. Por exemplo, podemos melhorar nossa função find para agir de forma semelhante à função string.find.

Esta é a versão original, que criamos na seção 7.7:

def find(str, ch):
  indice = 0
  while indice < len(str):
    if str[indice] == ch:
      return indice
    indice = indice + 1
  return -1

Esta é a nova versão, melhorada:

def find(str, ch, inicio = 0):

indice = inicio while indice < len(str):

if str[indice] == ch:
return indice

indice = indice + 1

return -1

O terceiro parâmetro, chamado inicio, é opcional por possuir um valor padrão 0. Se invocarmos a nossa função find com apenas dois parâmetros, o valor padrão 0 será atribuído à variável inicio, fazendo com que a busca inicie do começo da string informada.

14.10 Glossário

linguagem orientada a objetos
Uma linguagem que provê características tais como classes definidas pelo usuário e herança, que facilitam a programação orientada a objetos.
programação orientada a objetos
Um estilo de programação na qual os dados e as operações que os manipulam estão organizados em classes e métodos.
método
Uma função que é definida dentro de uma definição de classe e é chamada em instâncias desta classe.
override (sem traducao; termo consagrado)
Substituir uma definição já pronta. Exemplos incluem substituir um parâmetro padrão por um argumento particular e substituir um método padrão, fornecendo um novo método com o mesmo nome.
método de inicialização (tambem chamado de construtor)
Um método especial que é invocado automaticamente quando um novo objeto é criado e que inicializa os atributos deste objeto.
sobrecarga de operador
Estender a funcionalidade dos operadores nativos (+, -, *, >, <, etc.) de forma que eles funcionem também com tipos definidos pelo usuário.
produto escalar
Operação definida na álgebra linear que multiplica dois pontos (com coordenadas (x,y,z)) e retorna um valor numérico.
multiplicação por escalar
Operação definida na álgebra linear que multiplica cada uma das coordenadas de um ponto por um valor numérico.
polimórfica
Uma função que pode operar com mais de um tipo. Se todas as operações de uma função pode ser aplicadas a um certo tipo, então a função pode ser aplicada a este tipo.