---
title: "D-Strings no Python 3.15: Adeus textwrap.dedent()"
url: "https://python.dev.br/blog/python-d-strings-pep-822-dedent/"
markdown_url: "https://python.dev.br/blog/python-d-strings-pep-822-dedent.MD"
description: "Entenda as d-strings da PEP 822 no Python 3.15, como funcionam, sintaxe com f-strings e por que substituem textwrap.dedent() em strings multiline."
date: "2026-04-10"
author: "Equipe python.dev.br"
---

# D-Strings no Python 3.15: Adeus textwrap.dedent()

Entenda as d-strings da PEP 822 no Python 3.15, como funcionam, sintaxe com f-strings e por que substituem textwrap.dedent() em strings multiline.


Se você já escreveu strings multiline dentro de funções ou classes em Python, provavelmente conhece o problema: a indentação do código contamina o conteúdo da string. A solução clássica sempre foi `textwrap.dedent()`, mas ela tem limitações e adiciona overhead em tempo de execução.

A **PEP 822** propõe uma solução elegante para o Python 3.15: as **d-strings** (dedented strings). Com o prefixo `d` antes de aspas triplas, a indentação é removida automaticamente pelo compilador, sem custo em runtime. É uma daquelas mudanças que parece pequena, mas melhora o dia a dia de quem escreve Python profissionalmente.

## O problema com strings multiline hoje

Veja um exemplo comum em código Python atual:

```python
def gerar_sql():
    query = """
        SELECT u.nome, u.email
        FROM usuarios u
        WHERE u.ativo = true
        ORDER BY u.nome
    """
    return query

print(repr(gerar_sql()))
# '\n        SELECT u.nome, u.email\n        FROM usuarios u\n  ...'
```

A string resultante vem com toda a indentação do bloco. Para resolver, usamos `textwrap.dedent()`:

```python
import textwrap

def gerar_sql():
    query = textwrap.dedent("""
        SELECT u.nome, u.email
        FROM usuarios u
        WHERE u.ativo = true
        ORDER BY u.nome
    """)
    return query
```

Funciona, mas tem problemas:

- **Overhead em runtime**: `dedent()` é uma função que processa a string toda vez que o código executa
- **Verbosidade**: precisa importar `textwrap` e envolver a string na chamada
- **Não funciona com t-strings**: a nova sintaxe de template strings da PEP 750 não é compatível com `textwrap.dedent()`
- **Confusão com a primeira linha**: o comportamento com a linha logo após as aspas de abertura nem sempre é intuitivo

## O que são d-strings?

D-strings são strings multiline com o prefixo `d` que removem automaticamente a indentação comum de todas as linhas. O processamento acontece em tempo de compilação — sem nenhum custo em runtime.

A sintaxe básica:

```python
s = d"""
    Hello
    World!
    """
print(repr(s))  # 'Hello\nWorld!\n'
```

Regras fundamentais:

1. O prefixo `d` só funciona com **aspas triplas** (`"""` ou `'''`)
2. As aspas de abertura **devem** ser seguidas de uma quebra de linha
3. A quebra de linha após a abertura **não** é incluída no resultado
4. A indentação a remover é calculada pela indentação comum mais longa entre todas as linhas (incluindo a linha das aspas de fechamento)

Se você está aprendendo sobre os diferentes tipos de strings em Python, vale revisar também nosso glossário sobre [strings](/glossario/string/) e o artigo sobre [f-strings](/glossario/f-strings/).

## Exemplos práticos

### SQL sem indentação indesejada

```python
def buscar_usuarios(status):
    return d"""
        SELECT u.nome, u.email, u.criado_em
        FROM usuarios u
        WHERE u.status = '{status}'
        ORDER BY u.criado_em DESC
        LIMIT 100
        """

print(buscar_usuarios("ativo"))
# SELECT u.nome, u.email, u.criado_em
# FROM usuarios u
# WHERE u.status = 'ativo'
# ORDER BY u.criado_em DESC
# LIMIT 100
```

### HTML em geradores de template

```python
def card_usuario(nome, cargo):
    return d"""
        <div class="card">
            <h3>{nome}</h3>
            <p>{cargo}</p>
        </div>
        """
```

### Mensagens de log e docstrings de CLI

```python
def mostrar_ajuda():
    print(d"""
        Uso: meuscript [opções] <arquivo>

        Opções:
          -v, --verbose    Modo detalhado
          -o, --output     Arquivo de saída
          -h, --help       Mostra esta ajuda
        """)
```

### Controle do trailing newline

A posição das aspas de fechamento controla se há quebra de linha no final:

```python
# Com newline no final (aspas na linha seguinte)
s = d"""
    Hello
    World!
    """
print(repr(s))  # 'Hello\nWorld!\n'

# Sem newline no final (aspas na mesma linha)
s = d"""
    Hello
    World!"""
print(repr(s))  # 'Hello\nWorld!'
```

### Preservando indentação relativa

Se as aspas de fechamento estiverem alinhadas à esquerda, a indentação relativa é preservada:

```python
s = d"""
    def hello():
        print("world")
"""
print(repr(s))  # '    def hello():\n        print("world")\n'
```

Isso é muito útil para gerar código Python indentado corretamente.

## Combinação com outros prefixos

Uma das grandes vantagens das d-strings é que combinam naturalmente com outros prefixos de string:

### df-string (dedent + formatação)

```python
nome = "Maria"
saudacao = df"""
    Olá, {nome}!
    Bem-vinda ao sistema.
    """
print(saudacao)
# Olá, Maria!
# Bem-vinda ao sistema.
```

### dr-string (dedent + raw)

```python
padrao = dr"""
    ^\d{3}\.\d{3}\.\d{3}-\d{2}$
    """
print(repr(padrao))  # '^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$\n'
```

Ideal para regex sem precisar escapar barras invertidas. Se você trabalha com expressões regulares, veja nosso artigo sobre [regex em Python](/blog/expressoes-regulares-python/).

### db-string (dedent + bytes)

```python
dados = db"""
    HTTP/1.1 200 OK
    Content-Type: text/plain
    """
print(type(dados))  # <class 'bytes'>
```

### dt-string (dedent + template)

```python
nome = "João"
t = dt"""
    Olá, {nome}!
    """
print(type(t))  # <class 'string.templatelib.Template'>
```

Essa combinação é especialmente relevante porque `textwrap.dedent()` **não funciona** com [template strings da PEP 750](/blog/python-3-14-template-strings/), já que elas não são `str`. As d-strings resolvem esse problema.

## Como funciona o algoritmo de dedent

O algoritmo é executado pelo compilador (não em runtime) e segue estes passos:

1. **Identifica a indentação** de cada linha (espaços e tabs no início)
2. **Ignora linhas em branco** (somente whitespace) no cálculo
3. **Inclui a linha das aspas de fechamento** no cálculo
4. **Calcula a indentação comum** mais longa compartilhada por todas as linhas
5. **Remove essa indentação** do início de cada linha

Tabs e espaços são tratados como caracteres distintos — misturar os dois pode causar `IndentationError`:

```python
# Mistura de tabs e espaços pode gerar erro
s = d"""
	Hello      # tab
    World!     # espaços
    """
# IndentationError se não houver prefixo comum
```

## D-strings vs textwrap.dedent()

| Aspecto | `textwrap.dedent()` | D-strings |
|---|---|---|
| Quando executa | Runtime | Compilação |
| Overhead | Sim (processa a string) | Zero |
| Importação | Precisa de `import textwrap` | Nenhuma |
| Funciona com f-strings | Sim, mas verboso | Sim, com `df"""` |
| Funciona com t-strings | Não | Sim, com `dt"""` |
| Continuação de linha | Não dedenta a linha seguinte | Dedenta normalmente |
| Trailing newline | Remove a primeira, mantém a última | Controlado pela posição das aspas |

## Migração de código existente

Se você tem código usando `textwrap.dedent()`, a migração é direta na maioria dos casos:

```python
# Antes
import textwrap

msg = textwrap.dedent("""\
    Linha 1
    Linha 2
    Linha 3
""")

# Depois
msg = d"""
    Linha 1
    Linha 2
    Linha 3
    """
```

Atenção: o comportamento com a primeira e última linha pode diferir em casos específicos. Teste a saída ao migrar.

## Erros comuns e restrições

A PEP define claramente o que **não** é permitido:

```python
# Aspas simples ou duplas (sem triplas) — SyntaxError
s = d"hello"

# Conteúdo na mesma linha das aspas de abertura — SyntaxError
s = d"""Hello
    World!"""

# Barra invertida logo após a abertura — SyntaxError
s = d"""\
    Hello"""
```

Essas restrições existem para manter a sintaxe previsível e consistente.

## Status atual e quando usar

A PEP 822 está em status **Draft** e é direcionada ao **Python 3.15**. O Steering Council ainda não aprovou formalmente, mas a proposta tem boa recepção na comunidade. Uma [implementação de referência](https://github.com/methane/cpython/pull/108) já existe.

Quando a feature estiver disponível, use d-strings sempre que:

- Escrever SQL, HTML, YAML ou qualquer texto multiline dentro de funções
- Precisar de strings formatadas multiline com `df"""`
- Trabalhar com template strings (`dt"""`)
- Quiser eliminar o `import textwrap` e o overhead de `dedent()`

Se você quer acompanhar outras novidades do Python 3.15, confira nosso artigo sobre [lazy imports (PEP 810)](/blog/python-3-15-lazy-imports/) e fique de olho no [blog](/blog/) para mais atualizações.

## Conclusão

As d-strings são o tipo de melhoria que não revoluciona a linguagem, mas remove uma fonte constante de atrito. Quem já perdeu tempo debugando indentação em strings multiline ou se incomodou com o boilerplate de `textwrap.dedent()` vai entender o valor dessa PEP.

A combinação com f-strings e t-strings é o ponto mais forte: pela primeira vez teremos uma forma limpa e sem overhead de escrever templates multiline formatados dentro de código indentado. Para quem trabalha com geração de código, SQL dinâmico ou templates HTML em Python, essa é uma adição bem-vinda ao toolkit da linguagem.

Para quem trabalha com geração de código em múltiplas linguagens, vale conhecer também os raw string literals de <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> e os multiline strings de <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin</a>, que já oferecem `trimMargin()` e `trimIndent()` nativamente.
