O importante foi compreender a base que é fundamental para todo o aprendizado seja qual for o conteúdo.
Como você se preocupa em aprender a base (o que te parabenizo, é raro isso) é importante lembrar que você ou está misturando conceitos ou aprendeu errado (fica tranquilo que isso é normal, faz parte e é importante aprender errado de vez enquando)
É como se fosse uma "cópia" do modelo que pode ser manipulado individualmente.
Não é bem assim que acontece, Python meio que não deixa transparecer mas não é uma cópia (tem um paradigma de POO que é cópia, mas não é o caso, veja esse exemplo:
class Caneca:
def __init__(self, cor, material, capacidade):
self.cor = cor
self.material = material
self.capacidade = capacidade
self.volume_atual = 0
def descrever(self):
return f"Uma caneca {self.cor} feita de {self.material} com capacidade de {self.capacidade} ml."
def descreve_xicara(self):
return f"Uma xícara {self.cor} feita de {self.material} com capacidade de {self.capacidade} ml."
minha_caneca = Caneca('azul', 'cerâmica', 350)
print(minha_caneca.descrever())
Caneca.descrever = descreve_xicara
print(minha_caneca.descrever())
O que realmente acontece é melhor ilustrado em Lua:
Caneca = {
new = function (self, cor, material, capacidade)
local instance = {}
instance.cor = cor
instance.material = material
instance.capacidade = capacidade
-- Diz pra procurar (__index) chaves nulas de
-- instance em self (que é Caneca)
return setmetatable(instance,{__index = self})
end,
descrever = function (self)
return ("Uma xícara %s feita de %s com capacidade de %d ml."):format(self.cor, self.material, self.capacidade)
end
}
minha_caneca = Caneca:new('azul', 'cerâmica', 350)
minha_caneca:descrever()
O que acontece em descrever
:
- Busca no objeto
minha_caneca
pelo campo "descrever" - Tem? Chama
minha_caneca.descrever(minha_caneca)
- Não tem? Chama
Caneca.descrever(minha_caneca)
O que eu quero ilustrar é que uma classe é um objeto de "fallback" ou seja se o que eu procuro não estiver no objeto, eu procuro na classe, quando você instancia uma classe em um objeto, o que você está dizendo pro compilador (sim, python é compilada) é: "Compilador, seguinte, se eu procurar uma propriedade ou método em minha_caneca
e não achar, por favor, procure em Caneca
porque provavelmente vai estar lá", é por isso que POO com classes geralmente ocupa menos espaço em RAM em troca de ficar lento quando acontece o inheritance hell (heranças em excesso), isso se chama lookup chain
uma classe é um modelo para criar objetos
Eu inverti a ordem pra facilitar entender o conceito, como a gente viu no exemplo anterior, uma classe é um objeto no qual outros objetos podem usar para buscar propriedades e métodos faltantes, um modelo para crar objetos GERALMENTE é uma função, essa técnica é chamada de prototyping (as vezes como fábricas) em Python seria algo assim:
# Importante porque vamos precisar de acesso a namespace do objeto
import types
def Caneca(cor, material, capacidade):
def descrever(self):
return f"Uma caneca {self.cor} feita de {self.material} com capacidade de {self.capacidade} ml."
# Cria um objeto vazio com acesso direto a namespace
instance = types.SimpleNamespace()
# Definimos as propriedades
instance.cor = cor
instance.material = material
instance.capacidade = capacidade
# E os métodos
instance.descrever = types.MethodType(descrever,instance)
# E agora é só retornar
return instance
minha_caneca = Caneca('azul', 'cerâmica', 350)
minha_caneca.descrever() # Uma caneca azul feita de cerâmica com capacidade de 350 ml.