Dicas sobre desenvolvimento de software, gestão e tributações

Rede Social

1 de outubro de 2020

Python: Uma função Pythônica para validar CPF


 


Algoritmos para validação de CPF são alguns dos primeiros algoritmos que aprendemos em cursos de programação (pelo menos foi assim comigo).

CPFs são compostos por 11 dígitos, sendo que os 2 últimos são dígitos verificadores. Portanto, para sabermos se um CPF é ou não válido, precisamos realizar o cálculo (que foi definido pela Receita Federal) e comparar o resultado obtido com os valores do dígito verificador.
 
O cálculo é bem simples! Vamos imaginar o seguinte CPF:
504.065.090-69 (CPF gerado aleatoriamente)
Primeiro pegamos os 9 primeiros dígitos e aplicamos a cada um deles um peso que se inicia em 2, da direita para a esquerda.

5

0

4

0

6

5

0

9

0

10

9

8

7

6

5

4

3

2

 
Após isso, basta somarmos cada dígito multiplicado por seu peso:
5 * 10 + 0 * 9 + 4 * 8 + ... + 0 * 2 = 170
 
Com este resultado, podemos optar entre 2 cálculos:
 
11 - 170 % 11

ou
 
170 * 10 % 11
 
Obs.: Lembrando que "%" é usado para calcular o resto (módulo) da divisão na maioria das linguagens de programação.
Ambos os cálculos nos levará ao valor do primeiro dígito verificador, que para este exemplo é 6.

Agora basta fazermos o mesmo calculo anterior, mas considerando também o primeiro dígito.
Os pesos ficarão conforme abaixo:
 

 5

0

4

0

6

5

0

9

0

6

11

10

9

8

7

6

5

4

3

2

 

Como a partir daqui o cálculo é exatamente o mesmo, vamos então para o algoritmo em Python!

TAMANHO_CPF = 11


def is_cpf_valido(cpf: str) -> bool:
if len(cpf) != TAMANHO_CPF:
return False

if cpf in (c * TAMANHO_CPF for c in "1234567890"):
return False

cpf_reverso = cpf[::-1]
for i in range(2, 0, -1):
cpf_enumerado = enumerate(cpf_reverso[i:], start=2)
dv_calculado = sum(map(lambda x: int(x[1]) * x[0], cpf_enumerado)) * 10 % 11
if cpf_reverso[i - 1:i] != str(dv_calculado % 10):
return False

return True


if __name__ == "__main__":
print(is_cpf_valido("0"))
print(is_cpf_valido("123456789012345"))
print(is_cpf_valido("55555555555"))
print(is_cpf_valido("19627722000"))
print(is_cpf_valido("19627722091"))
print(is_cpf_valido("19627722090"))
print(is_cpf_valido("02927367035"))
print(is_cpf_valido("64935775009"))
print(is_cpf_valido("45508834052"))
print(is_cpf_valido("50406509069"))

Na linha 1 apenas definimos uma constante com o tamanho do CPF.
 
Na linha 4 temos a declaração da nossa função.
 
Na linha 5 realizamos nossa primeira validação. Se o CPF não tiver exatamente 11 caracteres retornamos que o CPF informado é inválido. (lembre-se de limpar a mascara do CPF antes de chamar esta função).
 
Na linha 8 validamos se não é um CPF inválido conhecido (111.111.111-11, 222.222.222-22, ..., 000.000.000-00). Esta validação é feita a partir de um iterável gerado com uma comprehension. A comprehension irá iterar sobre cada valor da string "1234567890" e irá multiplicar cada caractere pelo tamanho do CPF. Aqui temos 2 particularidades sobre strings no Python:
1. Strings são iteráveis
2. Quando multiplicamos uma string por um valor inteiro, o resultado obtido será aquela string repetida x vezes.
 
Exemplo:
"a" * 10 = 'aaaaaaaaaa'
 
Na linha 11 o CPF informado é invertido. Exemplo:
De 12345678910 para 01987654321
A inversão é feita para facilitar a aplicação dos pesos.
 
Na linha 12 nós declaramos um for que irá iterar 2x, iniciando em 2 e finalizando em 1.

Na linha 13 é onde aplicamos os pesos de cada digito do CPF.
Primeiro nós pegamos o CPF invertido e fazemos uma substring delete que vai de "i" até o final.
Então pegamos esta substring e aplicamos os pesos nela, através da função "enumerate".
A função enumerate merece um post só para ela. Mas, de maneira resumida, ela recebe um iterável e enumera cada posição deste iterável. Ela retorna um "enumerate object" (que é também um iterável) de tuplas. Podemos converter este "enumarete object" em uma lista, por exemplo.
 
Exemplo:
list(enumerate([1, "b", 3])) = [(0, 1), (1, 'b'), (2, 3)]

Obs.: Vale atentar que no nosso caso nós iniciamos a função enumerate em 2 e não em 0.
 
A linha 14 é onde aplicamos o cálculo explicado no começo do post.
Primeiro multiplicamos os dígitos por seus respectivos pesos através da função "map". Assim como a função enumerate, a função map merece um post só para falar dela, mas vamos para um resumo. Ela recebe uma função e um iterável, e a partir disso gera um novo iterável.
No nosso exemplo, a função informada é uma expressão lambda (que resumidamente é uma função anônima) e o enumerate object criado na linha anterior. É na expressão lambda que ocorre a multiplicação dos digitos pelos seus pesos.
Usamos então função sum para somar o iterável retornado pela função map e aplicamos o resto da fórmula.

Na linha 15 nós comparamos o dígito do CPF informado com o dígito calculado. Caso os valores sejam diferentes, retornamos que o CPF é inválido.
A única observação que cabe aqui (além da conversão do dígito calculado para string) é que nós comparamos o módulo do dígito calculado por 10. Fazemos isso, pois quando o módulo da divisão por 11 resulta em 10, o valor que deve ser considerado é 0.

Como dito anteriormente, o loop será executado 2x. E caso não haja nenhuma inconsistência, a função irá retornar que o CPF é válido (linha 18).

0 comentários:

Postar um comentário