Perfeito! Aqui está um README.md com um resumo técnico e detalhado de tudo o que você viu até agora no seu estudo sobre o Python Data Model, com foco no funcionamento interno do CPython, PyObject, PyTypeObject e os slots C como sq_length.
# Estudo sobre o Python Data Model (com base no Fluent Python)
## 📖 Contexto
Este documento resume os principais conceitos estudados até agora sobre o **modelo de dados do Python** e sua implementação no **CPython**, com base no capítulo 1 do livro *Fluent Python*, de Luciano Ramalho.
---
## 🧩 O que é o Python Data Model?
O **data model** é o conjunto de regras que define como os objetos em Python devem se comportar em diferentes contextos — incluindo operadores (`+`, `==`, `in`), funções built-in (`len()`, `abs()`, `iter()`), e estruturas de controle (`for`, `with`, `if`).
O modelo é expresso através dos **special methods** (também chamados de *dunder methods*), como:
- `__len__`
- `__getitem__`
- `__add__`
- `__call__`
- `__enter__`, `__exit__`
- `__iter__`, `__next__`
---
## 💡 Exemplo: `FrenchDeck`
```python
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]Esse objeto se comporta como uma sequence sem herdar de list. Ele implementa os métodos __len__ e __getitem__, e por isso:
- Pode ser iterado:
for card in deck: ... - Pode ser fatiado:
deck[12::13] - Funciona com
random.choice(deck) - Aceita
len(deck) - Funciona com
in,sorted, etc.
Python chama a função built-in len(), que é implementada em C (no código-fonte do CPython).
len(deck)chama a função CPySequence_Size(obj)- Ela acessa
obj->ob_type->tp_as_sequence->sq_length - Esse campo (
sq_length) aponta para uma função C comolist_length() - Resultado: chamada direta em C, altíssima performance
-
Durante a criação da classe, o CPython detecta que ela tem
__len__ -
Cria um wrapper C chamado
slot_sq_length -
Esse wrapper é armazenado em
tp_as_sequence->sq_length -
Ao chamar
len(deck), o CPython executa:slot_sq_length(deck)- Que chama
deck.__len__()por nome, uma única vez, e cacheia o acesso
📍 Caminho interno percorrido pelo CPython:
Etapa 1: Python chama função built-in ────────────────────────────────────── len(deck) └─► builtin_len(obj) ← função C em builtins.c └─► PyObject_Length(obj) ← wrapper └─► PySequence_Size(obj)
Etapa 2: Acessa struct de tipo do objeto ────────────────────────────────────── deck → PyObject │ └──► ob_type → PyTypeObject (FrenchDeck) │ └──► tp_as_sequence → PySequenceMethods
Etapa 3: Verifica e chama o slot de comprimento ────────────────────────────────────── PySequence_Size(obj) faz:
if (tp_as_sequence && tp_as_sequence->sq_length) { return tp_as_sequence->sq_length(obj); }
Ou seja:
→ lê ponteiro sq_length → que aponta para slot_sq_length() → chama slot_sq_length(obj)
Etapa 4: slot_sq_length (função C wrapper) ────────────────────────────────────── slot_sq_length(obj) faz:
- Acessa atributo "len" na instância (só na 1ª vez)
- Chama obj.len() em Python
- Recebe PyObject (número)
- Converte para Py_ssize_t
- Retorna o valor
Etapa 5: Execução do método Python ────────────────────────────────────── def len(self): return len(self._cards)
Etapa 6: len(self._cards) ────────────────────────────────────── Como self._cards é uma list, o caminho é:
len(self._cards) └─► PySequence_Size(list_obj) └─► list_obj->ob_type->tp_as_sequence->sq_length └─► list_length(list_obj) ← função C nativa └─► return Py_SIZE(list_obj)
Estrutura mínima de qualquer objeto:
typedef struct {
Py_ssize_t ob_refcnt; // contador de referências
PyTypeObject *ob_type; // ponteiro para o tipo
} PyObject;Slots (como sq_length, nb_add, mp_subscript, etc.) são campos dentro de structs em C (como PySequenceMethods, PyNumberMethods, PyMappingMethods) que:
- Armazenam ponteiros para funções C.
- Essas funções implementam o comportamento associado a cada parte do Python Data Model.
📌 Em outras palavras:
Slots são ponteiros que apontam para funções C que implementam as operações definidas pelo data model.
Por exemplo:
| Slot | Implementa | Quando é usado? |
|---|---|---|
sq_length |
len(obj) |
Quando se chama len(obj) |
sq_item |
obj[i] |
Indexação |
nb_add |
obj1 + obj2 |
Soma |
mp_subscript |
obj[key] |
Acesso via chave |
tp_call |
obj() |
Objeto chamável |
tp_str |
str(obj) |
Conversão para string |
🎯 Exemplo: sq_length
typedef Py_ssize_t (*lenfunc)(PyObject *);
typedef struct {
lenfunc sq_length; // ← slot
// outros slots omitidos
} PySequenceMethods;Esse slot (sq_length) aponta para uma função como:
slot_sq_length(wrapper para__len__)- ou
list_length(implementação nativa paralist)
Representa o “tipo” (classe) de um objeto.
Contém:
-
tp_name: nome do tipo -
tp_basicsize: tamanho da instância -
Ponteiros para funções C como
tp_init,tp_new,tp_dealloc -
Ponteiros para sub-estruturas como:
tp_as_sequence→ structPySequenceMethodstp_as_number→ structPyNumberMethodstp_as_mapping→ structPyMappingMethods
typedef struct {
lenfunc sq_length; // → usado por len()
binaryfunc sq_concat; // → usado por +
ssizeargfunc sq_repeat; // → usado por *
ssizeargfunc sq_item; // → usado por []
// ...
} PySequenceMethods;No caso de FrenchDeck, sq_length aponta para slot_sq_length.
| Conceito | Explicação |
|---|---|
| CPython | Implementação oficial da linguagem Python, escrita em C. |
| Compilador | Programa que traduz código para outro formato (ex: bytecode). |
| Interpretador | Executa o bytecode linha por linha (CPython faz os dois). |
| PyObject | Estrutura base para toda instância em Python. |
| PyTypeObject | Estrutura que representa a "classe" de um objeto em C. |
| Slot C | Campo da struct do tipo que guarda ponteiros para funções C. |
sq_length |
Slot usado para len(obj). |
| Bytecode | Instruções intermediárias interpretadas pelo CPython. |
len(obj)em tipos embutidos (list,str) chama diretamente funções C via slotsq_length.obj.__len__()executa bytecode e envolve lookup, sendo mais lento e menos idiomático.- Ao definir
__len__numa classe Python, o CPython preenche automaticamente o slot C com um wrapper. - Slots C são fundamentais para performance e integração entre objetos e a linguagem.
- Fluent Python – Capítulo 1
- CPython source code –
typeobject.c - Documentação oficial: C API
PyTypeObject
---
Etapa 1: Instância do objeto
────────────────────────────────────────────
+-------------------+
deck ──►| PyObject | ← instancia de FrenchDeck
| ob_refcnt | ← contador de referências
| ob_type ─────────┼─────► PyTypeObject (FrenchDeck)
+-------------------+
Etapa 2: Descrição do tipo (classe FrenchDeck)
───────────────────────────────────────────────
# tp_as_sequence é um ponteiro para uma struct do tipo PySequenceMethods
# Se o tipo implementa o sequence protocol (como __len__, __getitem__, etc), esse campo aponta para uma instância da struct PySequenceMethods.
PyTypeObject (FrenchDeck)
+-----------------------------+
| tp_name = "FrenchDeck" |
| tp_as_sequence ─────────┐ |
+--------------------------│--+
▼
Etapa 3: Tabela de slots da sequência
────────────────────────────────────────────
PySequenceMethods
+-----------------------------+
| sq_length ─────────────┐ |
| sq_item | |
| sq_concat | |
+------------------------│----+
▼
Etapa 4: Ponteiro para função C genérica
────────────────────────────────────────────
slot_sq_length ← função C gerada pelo CPython
// Implementação:
slot_sq_length(PyObject *self) {
// chama self.__len__() em Python
return _PyObject_CallMethodId(self, &PyId___len__, NULL);
}
Etapa 5: Método Python definido pelo usuário
────────────────────────────────────────────
def __len__(self):
return len(self._cards)
self._cards = list → objeto embutido Python
Etapa 6: Caminho interno até list_length
────────────────────────────────────────────
self._cards ─► PyObject (tipo: list)
│
└─► PyTypeObject (list)
│
└─► tp_as_sequence → PySequenceMethods
│
└─► sq_length = list_length
list_length(PyObject *list) {
return Py_SIZE(list);
}