Executando verificação de segurança...
Em resposta a [Não disponível]
4

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

Algoritmos adaptados daqui e daqui.

Carregando publicação patrocinada...
Conteúdo excluído
2

Embora seja possível com regex, acho desnecessário. O loop que sugeri acima é bem mais simples, além de ser mais rápido. Fiz um teste básico pra verificar:

import string, re
from timeit import timeit

r = re.compile('[0-9]')
string_chain = '123@#$%456&*()789-09'

# executa 1 milhão de vezes cada teste
params = { 'number' : 1000000, 'globals': globals() }

# imprime os tempos em segundos
# todos retornam uma lista com os dígitos já convertidos para int

# loop simples
print('          loop:', timeit('list(int(c) for c in string_chain if c in string.digits)', **params))

# loop simples, usando um set com os dígitos (ligeiramente mais rápido)
digitos = set(string.digits)
print('  loop com set:', timeit('list(int(c) for c in string_chain if c in digitos)', **params))

# regex finditer (o mais lento)
print('regex finditer:', timeit('list(int(c[0]) for c in r.finditer(string_chain))', **params))

# regex findall (o segundo mais lento)
print(' regex findall:', timeit('list(int(c) for c in r.findall(string_chain))', **params))

Os tempos exatos variam de acordo com o hardware, mas de forma geral, regex foi o mais lento. Na minha máquina o resultado foi (tempos em segundos, quanto menor, mais rápido):

          loop: 2.2100358860002416
  loop com set: 1.788499561000208
regex finditer: 3.175833621000038
 regex findall: 2.61522052600003

Claro que para poucas strings pequenas, a diferença será imperceptível. Mas se tiver milhões de strings para validar, começa a fazer diferença.