Cálculo do Dígito Verificador em Python

imagem ilustrativa

Segundo a Wikipédia, “dígito verificador ou algarismo de controle é um mecanismo de autenticação utilizado para verificar a validade e a autenticidade de um valor numérico, evitando dessa forma fraudes ou erros de transmissão ou digitação. Consiste em um ou mais algarismos acrescentados ao valor original e calculados a partir deste através de um determinado algoritmo”.1

Nos anos 80s, 90s a informatização ainda era embrionária. Para abrir uma conta, os funcionários recorriam a uma tabela fornecida pela central de processamento que já vinha com o dígito verificador calculado. Por exemplo, o último dígito 3 na conta 1234-3 é o dígito verificador (DV). Existe também DV para CPF e CNPJ. Nestes documentos, os dois últimos algarismos é o dígito verificador.

No Brasil, os padrões mais usados são o Módulo 11 e o Módulo 10, cujos algorítimos são facilmente encontrados numa busca rápida na Web, por isso não vou reproduzí-los aqui. Se tiver curiosidade, procure também exemplos de rotinas que implementem o cálculo em alguma linguagem de programação, como o Java, por exemplo.

Se você já viu ou já escreveu uma solução para o algorítimo Módulo 11 em qualquer outra linguagem, vai se surpreender com a simplicidade da solução usando list comprehension2 em Python:

def dv(nr):
	d = sum([i*int(x) for i, x in enumerate(nr.rjust(10,'0'))]) % 11
	return str(0 if d > 9 else d)

Copie ou digite essas 3 linhas de código no arquivo dv.py, teste e veja a mágica acontecer.

from dv import *

dv('1234')

Para calcular o DV do CPF, basta chamar a rotina duas vezes, passando, na segunda chamada, o primeiro dígito encontrado.

def cpf(nr):
  d = dv(nr)
  return '%s-%s%s' % (nr, d, dv(nr+d))

O cálculo do DV do CNPJ é um pouquinho diferente do já mostrado, então faremos uma pequena adaptação:

def dv_cnpj(nr):
  d = sum([int(x)*(6-i if 6-i>1 else 14-i) for i, x in enumerate(nr.rjust(13,'0'))]) % 11
  return str(0 if d<2 else 11 - d)

Para executar, é só fazer duas chamadas, como no caso do CPF.

def cnpj(nr):
    d = dv_cnpj(nr)
    return "%s-%s%s" % (nr.rjust(13, "0"), d, dv_cnpj(nr + d))

Não vamos explicar cada um dos códigos acima, mas examinaremos o primeiro cálculo para entender como o Python faz a mágica acontecer. Uma das vantagens dessa linguagem é que ela foca diretamente na solução do problema. Assim, o programador não precisa gastar tempo e energia com idiossincrasias da própria linguagem. Voltemos ao código:

def dv(nr):
	d = sum([i*int(x) for i, x in enumerate(nr.rjust(10,'0'))]) % 11
	return str(0 if d > 9 else d)

A função criada com o comando def dv(nr) espera receber um valor numérico como string, entre aspas, que no Python tanto podem ser simples como duplas. E , na última linha, retorna o resultado, testando a seguinte condição: se o dígito encontrado estiver entre 0 e 9, retorna o próprio dígito; caso seja 10, retorna 0 (zero). Alguns bancos usam a letra X, quando o dígito é 10.

Toda a complexidade do cálculo está condensada e resolvida na linha d = sum(...). Vamos dissecar cada trechinho dessa linha, para entender o que está acontecendo, começando pelo trecho nr.rjust(10,'0'). Esse código pega o valor recebido em nr e alinha à direita, preenchendo com zeros à esquerda até o tamanho de 10 dígitos. Se o valor recebido foi “1234”, ele passa a ser “0000001234” e estará pronto para as próximas etapas do cálculo.

Agora, vamos criar um enumerador3 associado a esse novo valor de nr: enumerate(nr.rjust(10,'0')). Devido as características dos enumeradores, mesmo que você crie uma variável, algo assim: e = enumerate(nr.rjust(10,'0')), não será possível ver o valor do objeto criado. Para conhecer seu conteúdo, será necessário iterar sobre ele.

Experimente isso no prompt do Python:

>>> nr = '1234'
>>> nr = nr.rjust(10,"0")
>>> print (nr)
0000001234
>>> e = enumerate(nr.rjust(10,'0'))
>>> print(e)
<enumerate object at 0x7a4036015e80>
>>> for i,x in e:
...   print(i,x)

A saída do último print serão pares de valores, associando cada posição i do enumerador com cada um dos dígitos de nr. São estes valores que usaremos para gerar uma nova lista, multiplicando esses fatores, como pede o algorítimo do Módulo 11.

É isso que faz o trecho de código a seguir: [i*int(x) for i, x in enumerate(nr.rjust(10,'0'))].

Se executarmos no prompt do Python, veremos algo como o seguinte:

>>> e = enumerate(nr.rjust(10,'0'))
>>> l = [i*int(x) for i, x in e]
>>> print (l)
[0, 0, 0, 0, 0, 0, 6, 14, 24, 36]

O resultado obtido é uma nova lista com a multiplicação dos fatores, como esperado.

Uma observação importante quando se lida com iteráveis no Python é que não podemos iterar mais de uma vez sobre eles, por isso, nos exemplos que dei usando o prompt, tive que recriar a variável e que continha o enumerator.

Seguindo o algorítimo do Módulo 11, somaremos, agora, os valores da lista obtida pela multiplicação realizada no processo anterior: sum([...]) e, a seguir, pegamos o resto4 da divisão por 11: d = sum(...) % 11. O Python usa o símbolo de percentual para fazer essa operação.

O valor de d será um número variando de 0 a 10, que nosso retorno ajustará para o intervalo de 0 a 9. No exemplo dado (1234), retornará 3, o dígito verificador que estamos procurando.


  1. Dígito Verificador: Wikipédia↩︎

  2. List comprehension cria uma nova lista a partir de valores de outra pré-existente: Python docs ↩︎

  3. Enumerador: Python Tutorial ↩︎

  4. Resto da divisão: conhecido como módulo (operador mod em algumas linguagens), daí o nome do algorítimo. ↩︎