Atributos de classes#
Vimos que podemos definir atributos de instância em uma classe, que são variáveis que pertencem a cada objeto da classe. Mas também podemos definir atributos de classe, que são variáveis que pertencem à classe em si, e não a cada objeto da classe. A diferença é que os atributos de classe são compartilhados por todos os objetos da classe.
Eu sei que é difícil entender isso só com palavras, então vamos ver um exemplo.
class Carro:
quantidade_rodas: int = 4
def __init__(self, marca: str, modelo: str):
self.marca = marca
self.modelo = modelo
corolla = Carro("Toyota", "Corolla")
civic = Carro("Honda", "Civic")
O que há de diferente no código acima? Percebam que o atributo quantidade_rodas
é definido dentro da classe, porém fora do método __init__
e sem usar a referência à instância self
com a notação de ponto self.quantidade_rodas
. O que isso quer dizer? Isso significa que ele é um atributo de classe e não de instância. Por ele ser um atributo de classe, significa que todas as instâncias da classe Carro
compartilham o mesmo valor para quantidade_rodas
.
Tal valor pode ser acessado por cada uma das instâncias ou a partir da própria classe com a notação de ponto Carro.quantidade_rodas
.
class Carro:
quantidade_rodas: int = 4
def __init__(self, marca: str, modelo: str):
self.marca = marca
self.modelo = modelo
corolla = Carro("Toyota", "Corolla")
civic = Carro("Honda", "Civic")
# Todas as instâncias compartilham o mesmo valor do atributo de classe quantidade_rodas
print(Carro.quantidade_rodas) # 4
print(corolla.quantidade_rodas) # 4
print(civic.quantidade_rodas) # 4
4
4
4
Podemos mudar de forma global o valor de quantidade_rodas
para todas as instâncias da classe Carro
, e isso será refletido em todas as instâncias. Nem sempre isso é desejado e é por isso que devemos ter cuidado ao usar atributos de classe principalmente quando alteramos tal valor.
class Carro:
quantidade_rodas: int = 4
def __init__(self, marca: str, modelo: str):
self.marca = marca
self.modelo = modelo
corolla = Carro("Toyota", "Corolla")
civic = Carro("Honda", "Civic")
print("Atributo de classe quantidade_rodas antes da alteração de valor")
print(f"Corolla tem {corolla.quantidade_rodas} rodas") # 4
print(f"Civi tem {civic.quantidade_rodas} rodas") # 4
# Alterando o valor do atributo de classe quantidade_rodas.
# Isso terá reflexo tanto em instâncias criadas antes da
# alteração quanto em instâncias criadas após a alteração!
print("---")
print("Alteramos o valor do atributo de classe quantidade_rodas para 5!")
print("---")
Carro.quantidade_rodas = 5
gol = Carro("Volkswagen", "Gol")
print("Instâncias criadas antes da alteração de valor do atributo de classe")
print(f"Corolla tem {corolla.quantidade_rodas} rodas") # 5
print(f"Civi tem {civic.quantidade_rodas} rodas") # 5
print("Instância criada após a alteração de valor do atributo de classe")
print(f"Gol tem {gol.quantidade_rodas} rodas") # 5
Atributo de classe quantidade_rodas antes da alteração de valor
Corolla tem 4 rodas
Civi tem 4 rodas
---
Alteramos o valor do atributo de classe quantidade_rodas para 5!
---
Instâncias criadas antes da alteração de valor do atributo de classe
Corolla tem 5 rodas
Civi tem 5 rodas
Instância criada após a alteração de valor do atributo de classe
Gol tem 5 rodas
Atributos de classe são úteis para definir valores que são compartilhados por todas as instâncias da classe, como por exemplo, valores padrão que são comuns a todas as instâncias. Porém, é preciso ter muito cuidado ao alterar tais valores, pois isso pode causar efeitos colaterais inesperados, conforme vimos acima.
Vamos ver um exemplo real de aplicação de atributos de classe.
# 1
class Senior:
# 2
salario_base = 10_000
# 3
def __init__(self, nome: str, id_funcionario: int, anos_experiencia: int):
self.nome = nome
self.id_funcionario = id_funcionario
self.anos_experiencia = anos_experiencia
self.bonus_individual = 0
# 4
def calcular_salario(self) -> int:
return Senior.salario_base + self.bonus_individual
# 5
def aplicar_bonus(self, valor: int) -> None:
self.bonus_individual += valor
# 6
senior1 = Senior(nome="Ana Silva", id_funcionario=1, anos_experiencia=8)
senior2 = Senior(nome="Carlos Oliveira", id_funcionario=2, anos_experiencia=10)
senior3 = Senior(nome="Mariana Santos", id_funcionario=3, anos_experiencia=7)
# 7
print("Salários iniciais:")
print(senior1.calcular_salario())
print(senior2.calcular_salario())
print(senior3.calcular_salario())
# 8
Senior.salario_base = Senior.salario_base * (1 + 5 / 100)
# 9
print("\nSalários após aumento de 5% para todos os sêniors da empresa:")
print(senior1.calcular_salario())
print(senior2.calcular_salario())
print(senior3.calcular_salario())
# 10
senior2.aplicar_bonus(1_000)
# 11
print("\nSalários após bônus individual para Carlos:")
print(senior1.calcular_salario())
print(senior2.calcular_salario())
print(senior3.calcular_salario())
Salários iniciais:
10000
10000
10000
Salários após aumento de 5% para todos os sêniors da empresa:
10500.0
10500.0
10500.0
Salários após bônus individual para Carlos:
10500.0
11500.0
10500.0
Vamos entender o código acima com calma. Vá acompanhando pelas numerações no código e a respectiva explicação abaixo.
Criamos a classe
Senior
, que representa um colaborador sênior de uma empresaDefinimos o atributo de classe
10_000
. Todos os sêniors da empresa recebem o mesmo salário.O método construtor define os atributos de instância
nome
,id_funcionario
,anos_experiencia
ebonus_individual
.Temos um método que calcula o salário do colaborador sênior. O salário é a soma do salário base mais o bônus individual.
Temos um outro método que acrescenta um valor ao bônus individual do colaborador sênior com base em um parâmetro informado.
Criamos 3 funcionários seniores.
Imprimimos os salários iniciais de cada um deles. Todos são iguais até aqui.
Aplicamos um aumento de 5% para todos os seniores modificando o atributo de classe.
Imprimimos novamente os salários de cada um deles. Todos tiveram um aumento de 5%.
Configuramos um bônus individual para um dos seniores.
Imprimimos novamente os salários de cada um deles. Apenas o salário do senior, que teve o bônus individual aplicado, foi alterado.
O passo 8 é o principal, pois refere-se ao tópico desta seção. Ele poderia ser muito bem definido como uma regra igualitária da empresa na qual todos os colaboradores sêniores recebem um aumento de 5% no salário. Isso é um exemplo de aplicação de atributos de classe.
Mas novamente, notem que ao alterar o atributo de classe, literalmente todas as instâncias, tanto aquelas criadas antes da alteração quanto as criadas após a alteração do atributo de classe são afetadas.
Prática#
Será que você realmente comprrendeu o conceito de atributos de classes? Experimente fazer o exercício 18 e veja se você consegue aplicar o conceito de atributos de classes em um problema prático.
Conclusão#
Aqui vimos um outro conceito sobre atributos de classe e como eles funcionam com um exemplo prático. Na sequência veremos um outro conceito importante de POO em Python: herança!