Funções getattr() e setattr() em Python

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 é:

  1. __getattribute__() (se sobrescrito)
  2. Descritores de dados
  3. Atributos de instância
  4. Atributos de classe e descritores não-dados
  5. __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.

Tags: Python getattr setattr __getattribute__ __getattr__

Publicado em 6-26 01:24