Não estou com tempo pra criar um PR agora, mas seguem algumas sugestões.
Para limpar a string, em vez de ter uma lista do que quer remover, é mais simples filtrar pelo que quer manter, já que é uma lista bem menor (apenas os dígitos de 0 a 9).
Ou seja,
import string
def __clear(string_chain: str) -> str:
# cria outra string apenas com os dígitos de 0 a 9
return ''.join(c for c in string_chain if c in string.digits)
Na verdade é questionável se uma string como "abc123xxx456,;.^789{$#@}09"
deveria ser considerada um CPF (afinal, estamos limpando todos os caracteres indesejados e verificando os números que sobraram). Talvez ele só devesse aceitar no máximo os separadores (ponto e hífen) em posições específicas, mas enfim...
Se bem que, como vc usa isso para fazer o cálculo dos dígitos verificadores, talvez seja mais simples já converter tudo para int
, em vez de criar outra string.
E o cálculo pode ser feito mais diretamente, não precisa separar em tantos métodos. E até o uso de reduce
eu acho desnecessário, pois um loop simples é mais que suficiente.
Fiz uma versão mais simplificada, mas que pode ser adaptada no seu código:
import string
# retorna uma lista com os dígitos presentes na string
def extrai_digitos(string_chain):
return list(int(c) for c in string_chain if c in string.digits)
def calcula_digito(multiplicador, digitos):
total = 0
for d in digitos:
if multiplicador >= 2:
total += d * multiplicador
multiplicador -= 1
else: break
resto = total % 11
if resto < 2:
return 0
else:
return 11 - resto
def validar_cpf(cpf):
digitos = extrai_digitos(cpf)
if len(digitos) != 11:
return False
# primeiro dígito não bate, CPF inválido
if digitos[9] != calcula_digito(10, digitos):
return False
# segundo dígito não bate, CPF inválido
if digitos[10] != calcula_digito(11, digitos):
return False
return True
string_chain = '123@#$%456&*()789-09'
print(validar_cpf(string_chain)) # True
Ah, também dá pra calcular ambos os dígitos verificadores em um único loop:
import string
# retorna uma lista com os dígitos presentes na string
def extrai_digitos(string_chain):
return list(int(c) for c in string_chain if c in string.digits)
def calcula_resto(resto):
if resto < 2:
return 0
return 11 - resto
def validar_cpf(cpf):
digitos = extrai_digitos(cpf)
if len(digitos) != 11:
return False
# no mesmo loop, calculo ambos os dígitos verificadores
total1 = total2 = 0
multiplicador = 11
for d in digitos:
if multiplicador >= 2:
if multiplicador >= 3:
total1 += d * (multiplicador - 1)
total2 += d * multiplicador
multiplicador -= 1
else: break
# CPF válido se os DV's batem
return digitos[9] == calcula_resto(total1 % 11) and digitos[10] == calcula_resto(total2 % 11)
string_chain = '123@#$%456&*()789-09'
print(validar_cpf(string_chain)) # True
Muito obrigado pelas dicas. A ideia de usar um método de limpeza que englobe tantos caracteres me veio depois de um cliente precisar persisdir dados de clientes em um bando de dados usando extensos arquivos xlsx gerados por outro sistema. Não sei o porque mas esses relatórios gerados, além de gerar dados bagunçados, ainda gerava saídas com caracteres estranhos e indesejáveis em meio aos dados. Tentei usar regex para selecionar somente os números, mas mesmo assim não tive êxito. Ah, e como os arquivos xlsx do cliente tinham muitos dados, com reduce consegui um certo desempenho melhorado, nada tão melhor assim, mas no final melhorou. Vou aplicar sua ideia de realizar o cálculo dos dígitos em um único momento. Forte abraço.