Funções getattr() e setattr() em Python
As funções getattr() e setattr() em Python permitem o acesso e a modificação dinâmica de atributos de objetos, facilitando metaprogramação e introspecção de código.
Função getattr()
A assinatura de getattr() é:
getattr(objeto, nome[, padrao])
- objeto: A instância ou classe a ser inspecionada.
- nome: Uma string com o nome do atributo a ser acessado.
- padrao: Valor opcional retornado caso o atributo não exista; se omitido, levanta
AttributeError.
Retorna o valor do atributo especificado.
Exemplo Básico com Classe Dinâmica
Considere uma classe para representar veículos, onde os atributos podem ser acessados com base em entrada do usuário:
class Veiculo:
def __init__(self, marca, modelo, ano, cor):
self.marca = marca
self.modelo = modelo
self.ano = ano
self.cor = cor
carro = Veiculo('Toyota', 'Corolla', 2020, 'Prata')
# Entrada do usuário para selecionar atributo
atributo_escolhido = input('Qual atributo do veículo deseja visualizar? ')
# Uso de getattr com valor padrão
valor = getattr(carro, atributo_escolhido, 'Atributo não encontrado.')
print(f'Valor do atributo: {valor}')
Neste caso, getattr() permite acessar atributos dinamicamente, evitando condicionais para cada possibilidade.
Detalhamento de getattr()
getattr() funciona para instâncias, classes, funções embutidas e módulos padrão.
1. Acesso a Atributos de Instância
class Dispositivo:
def __init__(self):
self.serial = 'XYZ123'
self.versao = 2.1
dev = Dispositivo()
print(getattr(dev, 'serial')) # Saída: XYZ123
print(getattr(dev, 'versao')) # Saída: 2.1
2. Acesso a Métodos
class Logger:
def registrar(self, mensagem):
print(f'Log: {mensagem}')
log = Logger()
metodo = getattr(log, 'registrar')
metodo('Sistema inicializado') # Saída: Log: Sistema inicializado
3. Acesso a Funções e Tipos Embutidos
# Acessar a função embutida 'len'
func_len = getattr(__builtins__, 'len')
print(func_len([1, 2, 3])) # Saída: 3
# Acessar o tipo 'list'
tipo_lista = getattr(__builtins__, 'list')
nova_lista = tipo_lista()
print(type(nova_lista)) # Saída: <class 'list'>
4. Acesso a Atributos de Bibliotecas Padrão
import math
valor_pi = getattr(math, 'pi')
print(f'Valor de pi: {valor_pi}') # Saída: Valor de pi: 3.141592653589793
metodo_floor = getattr(math, 'floor')
print(metodo_floor(3.7)) # Saída: 3
Caso Complexo com Importação Dinâmica
Combinando getattr() com importlib.import_module(), é possível carregar e usar módulos dinamicamente.
Suponha um módulo processador.py com a classe Calculadora:
# processador.py
class Calculadora:
def __init__(self, valores):
self.valores = valores
def somar(self):
return sum(self.valores)
def multiplicar(self, fator):
return [v * fator for v in self.valores]
def obter_dados():
return [10, 20, 30]
Em outro script, podemos importar e usar dinamicamente:
import importlib
modulo_nome = 'processador'
classe_nome = 'Calculadora'
metodo_nome = 'somar'
modulo = importlib.import_module(modulo_nome)
classe_obj = getattr(modulo, classe_nome)(modulo.obter_dados())
metodo = getattr(classe_obj, metodo_nome)
print(metodo()) # Saída: 60
# Atualizar valores e usar outro método
classe_obj.valores = [5, 10, 15]
metodo_nome = 'multiplicar'
metodo = getattr(classe_obj, metodo_nome)
print(metodo(2)) # Saída: [10, 20, 30]
Este padrão permite flexibiliddae ao estender funcionalidades sem alterar o código principal.
Explicação de import_module()
importlib.import_module() retorna um objeto módulo, que contém todas as definições (classes, funções, variáveis) do módulo importado. Podemos acessá-las usando getattr() para chamadas dinâmicas.
Função setattr()
A assinatura de setattr() é:
setattr(objeto, nome, valor)
Atribui o valor ao atributo nome do objeto. Se o atributo não existir, ele é criado.
Exemplo de Uso
class Configuracao:
idioma = 'pt-BR'
def obter_tema(self):
return 'claro'
config = Configuracao()
# Verificar existência de atributo
print(hasattr(config, 'tema')) # Saída: False
# Usar setattr para criar atributo
setattr(config, 'tema', 'escuro')
# Verificar novamente
print(hasattr(config, 'tema')) # Saída: True
print(config.tema) # Saída: escuro
Combinação com getattr()
Para atributos condicionais, podemos usar setattr() dentro de getattr() como padrão:
class Sistema:
nome = 'Alpha'
s = Sistema()
# Acessar 'versao', definindo-a se não existir
versao = getattr(s, 'versao', setattr(s, 'versao', '1.0'))
print(versao) # Saída: 1.0
print(s.versao) # Saída: 1.0
Método __getattribute__()
O método __getattribute__() é um interceptor de acesso a atributos em Python. Quando um atributo de instância é acessado, este método é chamado automaticamente, e seu retorno é o valor do atributo.
A ordem de acesso a atributos para instâncias é:
__getattribute__()(se sobrescrito)- Descritores de dados
- Atributos de instância
- Atributos de classe e descritores não-dados
__getattr__()(como último recurso)
Implementação e Exemplos
Considere uma classe Registro que intercepta o acesso a certos atributos:
class Registro:
dados_confidenciais = 'Sensível'
def __init__(self, descricao, codigo):
self.descricao = descricao
self.codigo = codigo
def __getattribute__(self, item):
# Interceptar acesso a 'descricao'
if item == 'descricao':
return 'Acesso restrito'
# Para outros atributos, usar o método da superclasse
return object.__getattribute__(self, item)
reg = Registro('Dados importantes', 42)
print(reg.descricao) # Saída: Acesso restrito
print(reg.codigo) # Saída: 42 (acesso via __getattribute__ da object)
print(reg.dados_confidenciais) # Saída: Sensível (acesso a atributo de classe via instância)
Ao sobrescrever __getattribute__(), o acesso a atributos de instância é controlado. Para acessar o valor real, usamos object.__getattribute__(self, item) para evitar recursão infinita.
Interação com __getattr__()
Se __getattribute__() levantar AttributeError, o método __getattr__() é chamado como fallback:
class Seguranca:
segredo = 'Top Secret'
def __init__(self, usuario):
self.usuario = usuario
def __getattribute__(self, item):
if item == 'segredo':
raise AttributeError
return object.__getattribute__(self, item)
def __getattr__(self, item):
return 'Informação não disponível'
s = Seguranca('admin')
print(s.segredo) # Saída: Informação não disponível (chama __getattr__)
print(s.usuario) # Saída: admin (acesso normal)
Neste caso, como __getattribute__() levanta exceção para 'segredo', __getattr__() é acionado, retornando uma mensagem padrão.