Programação orientada a objetos - Parte 1#
Na evolução das linguagens de programação, a necessidade de se representar e manipular informações complexas resultou no conceito de classes e objetos, onde classes servem como abstrações (representação) e objetos seriam instâncias de classes que mantém e permitem a manipulação da informação.
Em Python, toda informação que usamos é representada na forma de um objeto. Assim, o número 6 é um objeto da classe int, o número 3.14 é um objeto da classe float, e assim por diante.
print(type(6)) # type mostra o tipo do objeto passado como argumento
print(id(6)) # mostra o local na memória (endereço) onde o objeto está armazenado
i = 6
j = 6
print(id(i)) # note que i e j correspondem ao mesmo objeto 6
print(id(j))
Em computação, criar um objeto significa criar uma instância de uma classe.
Linguagens orientadas a objetos permitem a definição de novas classes.
Uma classe é uma abstração de alguma “coisa”, que possui um estado e comportamento.
Um estado é definido por um conjunto de variáveis chamadas de atributos.
Esses estados podem ser alterados por meio de “ações” sobre o objeto, que definem seu comportamento.
Essas ações são funções chamadas de “métodos”.
Por exemplo, para representar um carro podemos definir:
uma classe Carro:
com os atributos: ano, modelo, cor, e vel
e os métodos:
acelera(vel): acelera até vel
pare(): vel = 0
class Carro:
pass
# teste da classe Carro
fusca = Carro() # cria um objeto referenciado pela variavel fusca
brasilia = Carro()
fusca.ano = 1968
fusca.modelo = 'Fusca'
fusca.cor = 'preto'
brasilia.ano = 1981
brasilia.modelo = 'Brasilia'
brasilia.cor = 'amarela'
novo_fusca = fusca
print("Fusca igual a brasilia? > ", fusca == brasilia)
print("Fusca igual a novo_fusca? > ", fusca == novo_fusca)
fusca.ano += 10
print("fusca.ano = ", fusca.ano)
print("novo_fusca.ano = ", novo_fusca.ano)
O método especial init (construtor)#
Como sabemos que todos os carros que queremos representar possuem as propriedades “cor”, “ano”, e “modelo”, podemos deixar essas propriedades como atributos da classe e inicializá-las quando um objeto é instanciado (criado).
Para isso, vamos utilizar o método especial init, conhecido como construtor da classe.
class Carro:
def __init__(self, m, a, c):
self.modelo = m
self.ano = a
self.cor = c
brasilia = Carro('Brasilia', 1968, 'amarela')
class Fusca(Carro):
pass
fusca_padrao = Fusca('Fusca', 1981, 'preto')
novo_fusca = Fusca('Novo_fusca',1981,'azul')
novo_fusca.ano += 10 # observe que podemos acessar atributos de fusca
print(fusca_padrao.ano)
print(novo_fusca.ano)
1981
1991
Mas o que é esse self?#
Todo método de uma classe recebe como primeiro parâmetro uma referência à instância que chama o método, permitindo assim que o objeto acesse os seus próprios atributos e métodos.
Por convenção, chamamos esse primeiro parâmetro de self, mas qualquer outro nome poderia ser utilizado.
O método especial init é chamado automaticamente após a criação de um objeto, e no caso de um Carro, na nova instância são criados os atributos ‘modelo’, ‘ano’ e ‘cor’ com os valores dados como argumentos do construtor (no caso Carro(“Brasilia”, 1968, “amarela”)).
Assim, no corpo de qualquer método, um atributo pode ser criado, acessado ou modificado usando self.nome_do_atributo.
Para exemplificar a criação de outros métodos e entender melhor o papel do self vamos criar os métodos imprima, acelere e pare. Por ser um exemplo mais elaborado, vamos também colocar os testes dentro da função main.
def main():
brasilia = Carro('brasilia', 1968, 'amarela', 80)
fuscao = Carro('fuscao', 1981, 'preto', 95)
#Na função main, brasilia e fuscao são instâncias de Carro. Para chamar o método acelere de Carro, usamos a notação:
# Carro.acelere(brasilia, 40)
# Carro.acelere(fuscao, 50)
# Carro.acelere(brasilia, 80)
# Carro.pare(brasilia)
Carro.acelere(fuscao, 100)
class Carro:
def __init__(self, m, a, c, vm):
self.modelo = m
self.ano = a
self.cor = c
self.vel = 0
self.maxV = vm # velocidade maxima
def imprima(self):
if self.vel == 0: # parado da para ver o ano
print( "%s %s %d"%(self.modelo, self.cor, self.ano) )
elif self.vel < self.maxV:
print( "%s %s indo a %d Km/h"%(self.modelo, self.cor, self.vel) )
else:
print( "%s %s indo muito raaaaaapiiiiiiiidoooooo!"%(self.modelo, self.cor))
def acelere(self, v):
self.vel = v
if self.vel > self.maxV:
self.vel = self.maxV
Carro.imprima(self)
def pare(self):
self.vel = 0
Carro.imprima(self)
main()
fuscao preto indo muito raaaaaapiiiiiiiidoooooo!
Essa notação explicita a passagem dos objetos como primeiro parâmetro em cada método, mas é redundante visto que todo objeto sabe a que classe ele pertence. Uma notação mais comum e enxuta é chamar os métodos usando a notação com . de forma semelhante aos atributos e, como o primeiro argumento é sempre ele mesmo, ele pode ser evitado. Assim a chamada:
Carro.acelere.(brasilia, 40)
pode ser escrita como:
brasilia.acelere(40)
def main():
brasilia = Carro('brasilia', 1968, 'amarela', 80)
fuscao = Carro('fuscao', 1981, 'preto', 95)
brasilia.acelere(40)
fuscao.acelere(50)
brasilia.acelere(80)
brasilia.pare()
fuscao.acelere(100)
class Carro:
def __init__(self, m, a, c, vm):
self.modelo = m
self.ano = a
self.cor = c
self.vel = 0
self.maxV = vm # velocidade maxima
def imprima(self):
if self.vel == 0: # parado da para ver o ano
print( "%s %s %d"%(self.modelo, self.cor, self.ano) )
elif self.vel < self.maxV:
print( "%s %s indo a %d Km/h"%(self.modelo, self.cor, self.vel) )
else:
print( "%s %s indo muito raaaaaapiiiiiiiidoooooo!"%(self.modelo, self.cor))
def acelere(self, v):
self.vel = v
if self.vel > self.maxV:
self.vel = self.maxV
self.imprima()
def pare(self):
self.vel = 0
self.imprima()
main()
brasilia amarela indo a 40 Km/h
fuscao preto indo a 50 Km/h
brasilia amarela indo muito raaaaaapiiiiiiiidoooooo!
brasilia amarela 1968
fuscao preto indo muito raaaaaapiiiiiiiidoooooo!
O método especial str#
O método imprima é muito útil para qualquer tipo de objeto mas não é a forma mais comum utilizando objetos. Em muitos casos, é desejável simplesmente utilizar a função print para imprimir uma “representação textual” do objeto.
Na verdade, quando executamos o comando print(6), como o 6 é um objeto da classe int, a função print converte o inteiro 6 no string 6 antes de ser impresso, algo como print( str(6) ). Lembre-se que, dentre as funções para conversão de tipos (lembra do int em int(input(“Digite um número: ”))?), a função str converte o inteiro 6 para o string 6.
Para exibir então o conteúdo de um objeto usando print, devemos definir o método especial str, e devolver um string com a representação textual do objeto. A função str também chama e retorna o valor desse método (se existir).
O código abaixo é basicamente o mesmo que o anterior, mas substituimos o método imprima pelo método especial str. No corpo desse método, ao invés de chamar a função print, carrega-se um string apropriado em s, que é retornado pelo método para a função print ou str. Veja que nos métodos acelere e pare, basta chamar print(self).
def main():
brasilia = Carro('brasilia', 1968, 'amarela', 80)
fuscao = Carro('fuscao', 1981, 'preto', 95)
print(brasilia)
print(fuscao)
brasilia.acelere(40)
fuscao.acelere(50)
brasilia.acelere(80)
brasilia.pare()
fuscao.acelere(100)
class Carro:
def __init__(self, m, a, c, vm):
self.modelo = m
self.ano = a
self.cor = c
self.vel = 0
self.maxV = vm # velocidade maxima
def __str__(self):
if self.vel == 0: # parado da para ver o ano
s = "%s %s %d"%(self.modelo, self.cor, self.ano)
elif self.vel < self.maxV:
s = "%s %s indo a %d Km/h"%(self.modelo, self.cor, self.vel)
else:
s = "%s %s indo muito raaaaaapiiiiiiiidoooooo!"%(self.modelo, self.cor)
return s
def acelere(self, v):
self.vel = v
if self.vel > self.maxV:
self.vel = self.maxV
print(self)
def pare(self):
self.vel = 0
print(self)
main()
brasilia amarela 1968
fuscao preto 1981
brasilia amarela indo a 40 Km/h
fuscao preto indo a 50 Km/h
brasilia amarela indo muito raaaaaapiiiiiiiidoooooo!
brasilia amarela 1968
fuscao preto indo muito raaaaaapiiiiiiiidoooooo!
class Carro:
def __init__(self):
self.cor = "Preto"
self.quatidade_de_lugares = 7
self.velocidade_maxima = 200
self.ligado = False
self.marcha = 1
self.velocidade = 0
def Ligar(self):
self.ligado = True
def Acelerar(self):
self.velocidade += 10
def Freiar(self):
self.velocidade -= 10
def Trocar_Marcha(self, nova_marcha):
self.marcha = nova_marcha
def Desligar(self):
self.ligado = False
class Celta(Carro):
def __init__(self):
Carro.__init__(self)
self.cor = "Prata"
self.quantidade_de_lugares = 5
self.ar_condicionado_ligado = False
def Ligar_ar_condicionado(self):
self.ar_condicionado_ligado = True
def Ligar(self):
self.ligado = True
self.marcha = 6
celta_1=Celta()
print(celta_1.cor)
celta_1.Ligar_ar_condicionado()
print(celta_1.ar_condicionado_ligado)
Prata
True
celta_1=Celta()
print(celta_1.cor)
celta_1.Ligar_ar_condicionado()
print(celta_1.ar_condicionado_ligado)
Prata
True
Importação de classes#
Salvar classes em arquivos separados e importá-las em outros programas é uma prática comum em programação. Essa abordagem é usada para organizar o código, facilitar a reutilização e melhorar a manutenção. A seguir, explica-se como isso funciona, por que é vantajoso e como implementá-lo, usando Python como exemplo.
O que significa salvar classes em arquivos separados?#
Em vez de definir todas as classes e funcionalidades de um programa em um único arquivo, você pode dividir seu código em vários arquivos, cada um contendo classes ou funções relacionadas. Esses arquivos são chamados de módulos e podem ser importados em outros programas.
Por que usar essa abordagem?#
Organização: O código fica mais limpo e fácil de entender quando funções e classes relacionadas são separadas em arquivos diferentes.
Reutilização: Classes e funções podem ser reaproveitadas em outros projetos ou partes do mesmo programa, sem a necessidade de reescrever o código.
Manutenção: É mais fácil encontrar e corrigir bugs ou adicionar funcionalidades quando o código está dividido em partes lógicas.
Colaboração: Em projetos maiores, diferentes membros da equipe podem trabalhar em arquivos diferentes sem conflitos.
Modularidade: Facilita a substituição ou atualização de partes específicas do código sem impactar o restante.
Como salvar e importar classes?#
Criar um arquivo com a classe
Salve sua classe em um arquivo separado. Por exemplo, em um diretório chamado módulos, vamos salvar:Carro.py
:class Carro: def __init__(self): self.cor = "Preto" self.quatidade_de_lugares = 7 self.velocidade_maxima = 200 self.ligado = False self.marcha = 1 self.velocidade = 0 def Ligar(self): self.ligado = True def Acelerar(self): self.velocidade += 10 def Freiar(self): self.velocidade -= 10 def Trocar_Marcha(self, nova_marcha): self.marcha = nova_marcha def Desligar(self): self.ligado = False class Celta(Carro): def __init__(self): Carro.__init__(self) self.cor = "Prata" self.quantidade_de_lugares = 5 self.ar_condicionado_ligado = False def Ligar_ar_condicionado(self): self.ar_condicionado_ligado = True def Ligar(self): self.ligado = True self.marcha = 6
Importar a classe em outro arquivo
Em outro arquivo, importe a classe e use-a:main.py
:from modulos.Carro import Celta celta_1=Celta() print(celta_1.cor) celta_1.Ligar_ar_condicionado() print(celta_1.ar_condicionado_ligado))
Executar o programa principal
Execute o arquivomain.py
. Ele usará a classe definida emCarro.py
.
Outras formas de importação#
Importar várias classes de um arquivo:
from meu_modulo import Classe1, Classe2
Importar o módulo inteiro:
import meu_modulo obj = meu_modulo.Classe1()
Usar apelidos para simplificar:
import meu_modulo as mm obj = mm.Classe1()
Por que é uma boa prática?#
Evita duplicação de código: Você não precisa recriar a mesma classe em diferentes partes do projeto.
Melhora a legibilidade: Programas grandes tornam-se mais fáceis de entender.
Facilita a manutenção: Alterações em uma classe não exigem edições em múltiplos arquivos.
Promove testes unitários: Cada classe ou módulo pode ser testado separadamente.
Essa abordagem modular é um dos princípios fundamentais da programação orientada a objetos e do desenvolvimento de software escalável.
No exemplo do bloco a seguir a sub-classe Celta
é importada da classe Carro.py
salva no diretório modulos
.
from modulos.Carro import Celta
celta_1=Celta()
print(celta_1.cor)
celta_1.Ligar_ar_condicionado()
print(celta_1.ar_condicionado_ligado)
Prata
True