W3docs

Agrupamento de Variáveis em Python

Aprenda a agrupar variáveis relacionadas em Python usando classes, dataclasses, named tuples, SimpleNamespace e dicts — com exemplos executáveis.

Quando um programa precisa rastrear vários dados relacionados — o nome, a idade e o e-mail de um usuário, por exemplo — armazená-los em variáveis separadas e desconectadas torna-se difícil de gerenciar. Python oferece diversas ferramentas para agrupar variáveis sob um único nome, de modo que elas se movam juntas e permaneçam organizadas. Este capítulo explica as abordagens mais comuns, quando usar cada uma e as trocas envolvidas.

Tópicos abordados:

  • Por que agrupar variáveis é importante
  • Usando um dicionário simples
  • Usando types.SimpleNamespace para acesso por ponto
  • Usando collections.namedtuple para registros imutáveis leves
  • Usando uma classe com __init__
  • Usando @dataclass (Python 3.7+) para a sintaxe mais limpa
  • Escolhendo a ferramenta certa

Por Que Agrupar Variáveis?

Suponha que você esteja escrevendo um script que processa contas de usuários. Sem agrupamento, você poderia escrever:

user_name = "Alice"
user_age = 30
user_email = "[email protected]"

Isso funciona para um usuário, mas entra em colapso no momento em que você precisa de dois usuários ou passa dados para uma função:

def greet(name, age, email):
    print(f"Hello {name}, age {age} ({email})")

greet(user_name, user_age, user_email)

Três argumentos separados precisam permanecer sincronizados em todos os lugares. O agrupamento resolve isso ao reunir os dados:

user = {"name": "Alice", "age": 30, "email": "[email protected]"}

def greet(user):
    print(f"Hello {user['name']}, age {user['age']} ({user['email']})")

greet(user)

Agora a assinatura da função tem um parâmetro em vez de três, e adicionar um novo campo afeta apenas o dicionário.

Usando um Dicionário

Um dicionário Python é a maneira mais simples de agrupar variáveis nomeadas. As chaves são strings; os valores podem ser de qualquer tipo.

point = {"x": 10, "y": 20, "label": "origin"}

print(point["x"])      # 10
print(point["label"])  # origin

# Update a field
point["x"] = 15
print(point)
# {'x': 15, 'y': 20, 'label': 'origin'}

Quando usar: Agrupamento rápido e pontual, dados JSON, situações em que o conjunto de campos não é conhecido de antemão.

Desvantagens: Você acessa os campos com chaves string (point["x"]), o que é mais verboso do que a notação por ponto e não oferece autocomplete na IDE.

Usando types.SimpleNamespace

SimpleNamespace é um invólucro leve que oferece acesso por ponto em um namespace ad-hoc sem precisar escrever uma classe.

from types import SimpleNamespace

point = SimpleNamespace(x=10, y=20, label="origin")

print(point.x)      # 10
print(point.label)  # origin

# Update a field
point.x = 15
print(point)
# namespace(x=15, y=20, label='origin')

Objetos SimpleNamespace são mutáveis — você pode adicionar, alterar ou excluir atributos a qualquer momento:

from types import SimpleNamespace

config = SimpleNamespace(debug=False, timeout=30)
config.debug = True     # update
config.retries = 3      # add new attribute
del config.timeout      # remove

print(vars(config))
# {'debug': True, 'retries': 3}

Quando usar: Substituir um dicionário quando você quer acesso por ponto, mas não precisa de métodos ou verificação de tipos. Bom para fixtures de teste e objetos de configuração simples.

Usando collections.namedtuple

Um namedtuple é um registro imutável e leve. Ele se comporta como uma tupla comum, mas permite acessar os campos por nome, além de por índice.

from collections import namedtuple

# Define the type once
Point = namedtuple("Point", ["x", "y"])

# Create an instance
p = Point(x=10, y=20)

print(p.x)    # 10
print(p.y)    # 20
print(p[0])   # 10 — index access still works
print(p)      # Point(x=10, y=20)

Como instâncias de namedtuple são imutáveis, você não pode alterar um campo após a criação:

from collections import namedtuple

Color = namedtuple("Color", ["red", "green", "blue"])
white = Color(255, 255, 255)

# white.red = 0  # AttributeError: can't set attribute

Se precisar de uma cópia modificada, use o método _replace() — ele retorna uma nova instância:

from collections import namedtuple

Color = namedtuple("Color", ["red", "green", "blue"])
white = Color(255, 255, 255)

grey = white._replace(red=128, green=128, blue=128)
print(grey)
# Color(red=128, green=128, blue=128)

Quando usar: Registros imutáveis em que os nomes dos campos importam — coordenadas, cores RGB, linhas de banco de dados. Ocupa menos memória do que uma classe completa.

Usando uma Classe

Para variáveis agrupadas que também precisam de comportamento (métodos), defina uma classe com um método __init__:

class User:
    def __init__(self, name, age, email):
        self.name = name
        self.age = age
        self.email = email

    def greet(self):
        return f"Hello, I am {self.name} and I am {self.age} years old."

alice = User("Alice", 30, "[email protected]")
print(alice.name)     # Alice
print(alice.greet())  # Hello, I am Alice and I am 30 years old.

# Update a field
alice.age = 31
print(alice.age)      # 31

Múltiplas instâncias permanecem independentes — cada uma guarda sua própria cópia de name, age e email:

class User:
    def __init__(self, name, age, email):
        self.name = name
        self.age = age
        self.email = email

alice = User("Alice", 30, "[email protected]")
bob   = User("Bob",   25, "[email protected]")

print(alice.name, bob.name)   # Alice Bob

Quando usar: Sempre que os dados agrupados também precisam de métodos, lógica de validação ou herança. Classes são a base da programação orientada a objetos em Python — consulte Python Classes e Objetos para uma explicação completa.

Usando @dataclass (Python 3.7+)

O decorador @dataclass gera automaticamente __init__, __repr__ e __eq__ a partir dos campos de classe anotados, eliminando a maior parte do código repetitivo:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
    label: str = "unnamed"

p = Point(x=3.0, y=4.0)
print(p)           # Point(x=3.0, y=4.0, label='unnamed')
print(p.label)     # unnamed

p.label = "A"
print(p)           # Point(x=3.0, y=4.0, label='A')

Campos com valor padrão devem vir após os campos sem valor padrão (mesma regra dos argumentos de funções normais).

Dataclass imutável com frozen=True

Passe frozen=True para impedir que qualquer campo seja alterado após a criação — comportamento similar ao de um namedtuple, mas com todas as capacidades de uma classe:

from dataclasses import dataclass

@dataclass(frozen=True)
class RGB:
    red: int
    green: int
    blue: int

white = RGB(255, 255, 255)
print(white)
# RGB(red=255, green=255, blue=255)

# white.red = 0  # FrozenInstanceError: cannot assign to field 'red'

Agrupando múltiplos registros em uma lista

Dataclasses funcionam naturalmente com listas quando você precisa de uma coleção de registros:

from dataclasses import dataclass
from typing import List

@dataclass
class Product:
    name: str
    price: float
    in_stock: bool = True

inventory: List[Product] = [
    Product("Widget", 9.99),
    Product("Gadget", 24.99),
    Product("Doohickey", 4.50, in_stock=False),
]

for item in inventory:
    status = "available" if item.in_stock else "out of stock"
    print(f"{item.name}: ${item.price:.2f} ({status})")

Saída:

Widget: $9.99 (available)
Gadget: $24.99 (available)
Doohickey: $4.50 (out of stock)

Para o conjunto completo de recursos de dataclasses, incluindo field(), __post_init__ e herança, consulte Python Dataclasses.

Agrupando Variáveis com Atributos de Classe

Às vezes você quer constantes compartilhadas anexadas a um grupo, em vez de dados por instância. Atributos de classe (definidos diretamente no corpo da classe, fora de __init__) são compartilhados por todas as instâncias:

class AppConfig:
    MAX_RETRIES = 3
    TIMEOUT = 30
    BASE_URL = "https://api.example.com"

print(AppConfig.MAX_RETRIES)  # 3
print(AppConfig.BASE_URL)     # https://api.example.com

Você não precisa instanciar AppConfig para ler seus atributos — trate a própria classe como um namespace para constantes relacionadas. Este é um padrão leve para grupos de configuração. Para uma discussão mais completa sobre atributos de classe versus atributos de instância, consulte Python Classes e Objetos.

Escolhendo a Ferramenta Certa

FerramentaMutávelAcesso por pontoMétodosDicas de tipoMelhor para
dictSimNão (["key"])NãoNãoCampos dinâmicos / desconhecidos
SimpleNamespaceSimSimNãoNãoConfig ad-hoc, fixtures de teste
namedtupleNãoSimNãoParcialRegistros imutáveis, dados pequenos
classSimSimSimVia anotaçõesPOO com comportamento
@dataclassSim*SimSimSimRegistros estruturados com métodos

*frozen=True torna um dataclass imutável.

Regra geral:

  • Use dict quando a estrutura não é conhecida de antemão.
  • Use SimpleNamespace quando quiser acesso por ponto sem definir uma classe.
  • Use namedtuple para registros simples e imutáveis (coordenadas, cores, linhas).
  • Use uma class comum quando precisar de métodos e POO completa.
  • Use @dataclass quando precisar de um registro estruturado com métodos opcionais — oferece o máximo com o mínimo de código repetitivo.

Tópicos Relacionados

Prática

Prática
In Python, what are the main reasons for grouping variables into classes?
In Python, what are the main reasons for grouping variables into classes?
Was this page helpful?