W3docs

Enums em Python

Aprenda enums em Python: crie membros Enum, IntEnum, Flag e auto(), adicione métodos, compare com segurança e elimine números mágicos do seu código.

Um enum (abreviação de enumeração) é um conjunto de valores nomeados e constantes agrupados sob um único tipo. Em vez de espalhar inteiros ou strings avulsas como 1, 2, "pending", "active" pelo código, você atribui a cada um um nome descritivo — Status.PENDING, Color.RED — e o Python garante que esse nome sempre mapeará para o mesmo valor.

Este capítulo aborda:

  • Por que enums existem e quais problemas eles resolvem
  • Como criar um enum com a classe Enum
  • Como acessar membros pelo nome e pelo valor
  • Como iterar sobre um enum
  • auto() — deixando o Python atribuir valores automaticamente
  • IntEnum — enums que se comportam como inteiros
  • Flag — enums de bit-flag combináveis
  • Como adicionar métodos e propriedades a um enum
  • Aliases, @unique e _missing_
  • Quando usar enums versus outros padrões

Antes de ler este capítulo, certifique-se de que você está familiarizado com classes e objetos em Python e tipos de dados em Python.

Por que Usar Enums?

Considere esta função que processa um status de pedido passado como um inteiro simples:

def handle_order(status):
    if status == 1:
        print("Order is pending")
    elif status == 2:
        print("Order is active")
    elif status == 3:
        print("Order is complete")

Isso funciona, mas tem problemas reais:

  • Números mágicos. O que significa 2 isoladamente? Você precisa rastrear até a definição da função.
  • Sem validação. handle_order(99) não faz nada silenciosamente — sem erro, sem aviso.
  • Erros de digitação são invisíveis. handle_order(2) e handle_order(20) são ambos Python válido.
  • Refatoração é arriscada. Se você decidir que 1 deve significar outra coisa, precisará encontrar cada 1 no código.

Enums resolvem tudo isso. A mesma lógica escrita com um enum é autodocumentada, segura e fácil de refatorar:

from enum import Enum

class OrderStatus(Enum):
    PENDING = 1
    ACTIVE = 2
    COMPLETE = 3

def handle_order(status: OrderStatus):
    if status == OrderStatus.PENDING:
        print("Order is pending")
    elif status == OrderStatus.ACTIVE:
        print("Order is active")
    elif status == OrderStatus.COMPLETE:
        print("Order is complete")

handle_order(OrderStatus.ACTIVE)   # Order is active

A intenção é clara, e o Python impede que handle_order(99) coincida acidentalmente com qualquer ramificação.

Criando um Enum

Importe Enum do módulo enum (parte da biblioteca padrão do Python — sem instalação necessária) e crie uma subclasse:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Cada atributo de nível de classe (RED, GREEN, BLUE) torna-se um membro do enum. Os valores à direita (1, 2, 3) podem ser inteiros, strings ou qualquer outro tipo — a escolha é sua.

Acessando membros

Existem três formas de acessar um membro do enum:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

# Attribute access (most common)
print(Color.RED)           # Color.RED

# By name (square bracket notation)
print(Color['GREEN'])      # Color.GREEN

# By value (call the class with the value)
print(Color(3))            # Color.BLUE

Cada membro expõe dois atributos:

print(Color.RED.name)      # RED
print(Color.RED.value)     # 1

Use .name quando precisar de um rótulo legível por humanos (para logging ou exibição), e .value quando precisar passar o valor subjacente para um sistema externo (um banco de dados, uma API).

repr e type

print(repr(Color.RED))     # <Color.RED: 1>
print(type(Color.RED))     # <enum 'Color'>

Um membro de enum é uma instância de sua classe enum, não de int ou str.

Iterando Sobre um Enum

Enums são iteráveis. A iteração produz membros na ordem de definição:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

for color in Color:
    print(color.name, color.value)
# RED 1
# GREEN 2
# BLUE 3

Você também pode verificar a pertinência:

print(Color.RED in Color)   # True

Isso torna os enums úteis para preencher listas suspensas, construir tabelas de despacho similares a switch ou criar listas de opções para entrada do usuário.

auto() — Valores Automáticos

Se os valores específicos não importam — você só precisa que cada membro seja distinto — use auto(). O Python atribui inteiros sequenciais começando em 1:

from enum import Enum, auto

class Direction(Enum):
    NORTH = auto()
    SOUTH = auto()
    EAST = auto()
    WEST = auto()

for d in Direction:
    print(d.name, d.value)
# NORTH 1
# SOUTH 2
# EAST 3
# WEST 4

auto() é especialmente útil quando o enum crescerá com o tempo e você não quer renumerar membros manualmente.

Comparando Membros de Enum

Use is ou == para comparar membros. Ambos funcionam, mas is é ligeiramente mais rápido porque os membros de enum são singletons — cada nome mapeia exatamente para um objeto:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

print(Color.RED is Color.RED)    # True
print(Color.RED == Color.RED)    # True
print(Color.RED == Color.GREEN)  # False

Um membro de Enum simples não é igual ao seu valor bruto:

print(Color.RED == 1)   # False

Isso é intencional. Evita igualdade acidental entre enums diferentes que compartilham o mesmo inteiro:

class Size(Enum):
    SMALL = 1

print(Color.RED == Size.SMALL)   # False — different types

Se você precisar de comparação baseada em valor (por exemplo, member > 1), use IntEnum (veja abaixo).

IntEnum — Enums que se Comportam como Inteiros

Os membros de IntEnum também são inteiros Python regulares. Isso significa que você pode usar aritmética, operadores de comparação e passá-los onde um int é esperado:

from enum import IntEnum

class Priority(IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

print(Priority.HIGH > Priority.LOW)    # True
print(Priority.MEDIUM + 10)            # 12
print(Priority.HIGH == 3)              # True

Um caso de uso comum é ordenar uma lista de membros de enum:

from enum import IntEnum

class Level(IntEnum):
    LOW = 1
    MED = 2
    HIGH = 3

levels = [Level.HIGH, Level.LOW, Level.MED]
print([l.name for l in sorted(levels)])   # ['LOW', 'MED', 'HIGH']

Quando preferir Enum em vez de IntEnum

A transparência inteira do IntEnum também é sua fraqueza: Priority.HIGH == 3 é True, então um literal 3 digitado errado comparará silenciosamente como igual a Priority.HIGH. Use Enum simples sempre que quiser segurança estrita de tipos, e use IntEnum somente quando precisar genuinamente de aritmética inteira ou tiver que interagir com uma API que trabalha com números brutos.

Flag — Enums de Bit-Flag Combináveis

Flag é projetado para cenários onde múltiplas opções podem estar ativas ao mesmo tempo. Seus membros são potências de dois, e você os combina com o operador | (OR bit a bit):

from enum import Flag, auto

class Permission(Flag):
    READ = auto()
    WRITE = auto()
    EXECUTE = auto()
    ALL = READ | WRITE | EXECUTE

user = Permission.READ | Permission.WRITE
print(user)                           # Permission.WRITE|READ
print(Permission.READ in user)        # True
print(Permission.EXECUTE in user)     # False

auto() dentro de Flag atribui potências de dois sucessivas (1, 2, 4, 8, …) para que a combinação de membros com | nunca produza resultados ambíguos.

Use Flag para sistemas de permissões, alternâncias de funcionalidades e qualquer situação em que você precise de um conjunto compacto de interruptores booleanos.

Adicionando Métodos e Propriedades

Como um enum é uma classe, você pode adicionar métodos e propriedades a ele. Isso mantém a lógica relacionada dentro do tipo, em vez de espalhá-la em cadeias de if/elif:

from enum import Enum

class HttpStatus(Enum):
    OK = 200
    CREATED = 201
    NOT_FOUND = 404
    INTERNAL_ERROR = 500

    @property
    def is_success(self):
        return 200 <= self.value < 300

    @property
    def is_error(self):
        return self.value >= 400

def handle_response(status: HttpStatus):
    if status.is_success:
        print(f"{status.value} {status.name}: request succeeded")
    elif status.is_error:
        print(f"{status.value} {status.name}: request failed")

handle_response(HttpStatus.OK)           # 200 OK: request succeeded
handle_response(HttpStatus.NOT_FOUND)    # 404 NOT_FOUND: request failed

Você também pode dar a um enum um __init__ personalizado para armazenar dados extras por membro. Forneça os valores como tuplas:

from enum import Enum

class Planet(Enum):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS   = (4.869e+24, 6.0518e6)
    EARTH   = (5.976e+24, 6.37814e6)

    def __init__(self, mass, radius):
        self.mass = mass
        self.radius = radius

    @property
    def surface_gravity(self):
        G = 6.67430e-11
        return G * self.mass / (self.radius ** 2)

print(round(Planet.EARTH.surface_gravity, 2))    # 9.8
print(round(Planet.MERCURY.surface_gravity, 2))  # 3.7

A tupla (mass, radius) torna-se os argumentos do construtor; self.value ainda contém a tupla completa.

Aliases e @unique

Se dois membros compartilham o mesmo valor, o segundo torna-se um alias — ele resolve para o primeiro membro. Aliases não são produzidos durante a iteração:

from enum import Enum

class Status(Enum):
    ACTIVE = 1
    RUNNING = 1   # alias for ACTIVE

print(Status.ACTIVE is Status.RUNNING)   # True
print(list(Status))                      # [<Status.ACTIVE: 1>]

Aliases são ocasionalmente úteis (por exemplo, um nome legado apontando para um novo), mas também podem mascarar erros de digitação. Aplique o decorador @unique para proibir valores duplicados completamente:

from enum import Enum, unique

@unique
class Status(Enum):
    PENDING = 1
    ACTIVE = 2
    INACTIVE = 3

# Trying to add a duplicate value to a @unique enum raises ValueError:
# ValueError: duplicate values found in <enum 'Bad'>: B -> A

@unique é um bom padrão para qualquer enum onde aliasing acidental seria um bug.

Busca Personalizada com _missing_

Por padrão, chamar Color('unknown') levanta um ValueError. Você pode substituir o método de classe _missing_ para lidar com valores não reconhecidos — por exemplo, para fazer uma busca sem distinção de maiúsculas e minúsculas:

from enum import Enum

class Color(Enum):
    RED = 'red'
    GREEN = 'green'
    BLUE = 'blue'

    @classmethod
    def _missing_(cls, value):
        if isinstance(value, str):
            for member in cls:
                if member.value == value.lower():
                    return member
        return None

print(Color('RED'))     # Color.RED
print(Color('Green'))   # Color.GREEN

_missing_ recebe o valor que não foi encontrado. Retorne o membro correspondente ou None (o que deixa o Python levantar seu ValueError padrão).

Quando Usar Enums

Enums são a escolha certa quando:

  • Uma variável só pode conter um de um conjunto fixo de estados nomeados (status de pedido, verbo HTTP, naipe de carta).
  • Você quer prevenir valores inválidos de passarem silenciosamente.
  • O mesmo conceito é comparado em vários lugares e você quer uma única fonte de verdade.
  • Você precisa iterar sobre todos os valores válidos (preenchendo um formulário, documentando uma API).

Você provavelmente não precisa de um enum quando:

  • O conjunto de valores é aberto ou muda em tempo de execução (use um dicionário ou uma tabela de consulta de banco de dados).
  • Você só precisa de dois estadosTrue/False com um significado booleano claro é mais simples.
  • Os valores vêm de entrada do usuário que deve ser validada contra um esquema — considere uma biblioteca como Pydantic, que se integra perfeitamente com enums do Python.

Para padrões estreitamente relacionados, veja dataclasses em Python (para dados estruturados com valores padrão) e classes abstratas em Python (para impor contratos de interface em subclasses). Se você precisar de contêineres de constantes nomeadas sem toda a maquinaria de enum, o módulo collections do Python oferece namedtuple como alternativa.

Prática

Prática
What does Color['RED'] do when Color is an Enum with a RED member?
What does Color['RED'] do when Color is an Enum with a RED member?
Was this page helpful?