types.new_class() cria classes dinamicamente usando as mesmas regras centrais de uma declaração class. A função escolhe a metaclass apropriada, prepara o namespace e executa um callback que adiciona atributos, métodos e metadados antes de construir o tipo final.
Ela é útil em frameworks, ORMs, geradores de modelos, sistemas de plugins e ferramentas que precisam produzir classes a partir de schemas. Para casos simples, a função embutida type() pode bastar; new_class é valiosa quando metaclasses, __prepare__ e bases dinâmicas importam.
Assinatura
types.new_class(name, bases=(), kwds=None, exec_body=None)
name define o nome, bases contém as classes base, kwds representa opções do cabeçalho da classe e exec_body recebe o namespace preparado.
Primeiro exemplo
from types import new_class
def preencher(namespace):
namespace["categoria"] = "dinâmica"
namespace["descrever"] = lambda self: self.categoria
Produto = new_class("Produto", (), {}, preencher)
print(Produto().descrever())
O callback modifica o namespace antes da criação. Evite lambdas quando métodos precisam de documentação ou lógica significativa.
Equivalência conceitual
class Produto:
categoria = "dinâmica"
def descrever(self):
return self.categoria
A declaração normal é preferível quando a estrutura é conhecida ao escrever o código. Use geração dinâmica quando os campos ou bases realmente são determinados em runtime.
Usando uma classe base
class Modelo:
def salvar(self):
print("salvando", type(self).__name__)
def corpo(ns):
ns["tabela"] = "clientes"
Cliente = new_class("Cliente", (Modelo,), exec_body=corpo)
A classe criada participa normalmente de herança, MRO, descriptors e super().
Metaclass explícita
class Meta(type):
def __new__(mcls, nome, bases, namespace):
namespace["registrada"] = True
return super().__new__(mcls, nome, bases, namespace)
Gerada = new_class(
"Gerada",
(),
{"metaclass": Meta},
lambda ns: ns.update(valor=10),
)
O dicionário kwds representa argumentos do cabeçalho de classe. A opção metaclass é usada para selecionar a metaclass e não permanece necessariamente no namespace.
__prepare__ e namespaces especiais
Metaclasses podem implementar __prepare__ para devolver um mapping personalizado. new_class respeita esse protocolo, ao contrário de abordagens manuais que montam um dicionário e chamam type diretamente.
types.prepare_class
Quando você precisa controlar cada etapa, types.prepare_class() calcula a metaclass e devolve o namespace preparado. new_class combina essas etapas em uma interface mais conveniente.
Bases dinâmicas e __mro_entries__
Objetos usados como bases podem fornecer __mro_entries__ para serem substituídos por classes reais. O módulo types também oferece resolve_bases. Esse mecanismo aparece em genéricos e frameworks avançados.
Adicionando métodos com closure
def criar_modelo(nome, campos):
def corpo(ns):
ns["__annotations__"] = dict(campos)
def __repr__(self):
valores = ", ".join(
f"{campo}={getattr(self, campo, None)!r}"
for campo in campos
)
return f"{nome}({valores})"
ns["__repr__"] = __repr__
return new_class(nome, (), exec_body=corpo)
Tenha cuidado com closures em loops. Capture valores deliberadamente para evitar que todos os métodos usem a última iteração.
Definindo __module__ e __qualname__
Classes geradas para uso público devem ter metadados coerentes. Defina __module__ quando necessário para documentação, pickling e mensagens de erro.
def corpo(ns):
ns["__module__"] = __name__
ns["__doc__"] = "Modelo criado dinamicamente."
Pickle e importabilidade
Para serializar instâncias com pickle, a classe geralmente precisa estar acessível por um nome importável no módulo indicado. Criar a classe dentro de uma função sem registrá-la no namespace do módulo pode impedir a reconstrução.
Decorators depois da criação
Você pode aplicar dataclasses.dataclass, registradores ou outros decorators à classe retornada. Entretanto, frameworks que dependem do momento de criação podem exigir atributos no exec_body.
Segurança
Não transforme schemas não confiáveis diretamente em nomes de atributos, bases, metaclasses ou código executável. Valide identificadores, use listas permitidas e nunca execute texto externo com exec apenas para gerar uma classe.
new_class versus type
type(nome, bases, namespace) é simples e adequado quando o namespace já está pronto. new_class é melhor quando você quer reproduzir o processo de uma declaração de classe, incluindo seleção de metaclass e namespace preparado.
Erros comuns
- Gerar classes quando uma dataclass ou objeto simples bastaria.
- Esquecer
__module__e prejudicar pickle ou documentação. - Capturar variáveis de loop incorretamente em closures.
- Confiar em metaclasses ou bases fornecidas externamente.
- Não testar herança, MRO e introspecção.
Boas práticas
Mantenha uma factory central, valide schemas, crie nomes determinísticos, preserve metadados e teste a classe como qualquer API pública. Leia também os guias internos sobre módulo types, orientação a objetos e dataclasses.
Conclusão
types.new_class oferece uma forma estruturada de gerar classes em runtime respeitando metaclasses, namespaces preparados e herança. Ele é indicado para infraestrutura e frameworks em que a definição realmente depende de dados dinâmicos.







