Desculpe ser o estraga-prazeres, mas a própria documentação oficial do módulo random
tem aviso bem claro:
Warning: The pseudo-random generators of this module should not be used for security purposes. For security or cryptographic uses, see the secrets module.
Ou seja, o módulo random
não deve ser usado para aplicações de segurança ou criptografia, e sugere o uso do módulo secrets
para isso (disponível a partir do Python 3.6). Na documentação já tem algumas "receitas" prontas para gerar senhas. Adaptando para o seu caso, seria algo como:
import secrets
from string import ascii_letters, digits
symbols = '!#$%&()*+><^~@-_çÇ`/|ªº¿'
alphabet = ascii_letters + digits + symbols
# eu não colocaria quantidades específicas de cada tipo (X letras, Y números, etc)
# talvez a quantidade mínima para alguns (abaixo vejo se tem pelo menos 3 dígitos)
# em vez disso, verifico se tem pelo menos um de cada
tamanho_senha = 16
while True:
password = ''.join(secrets.choice(alphabet) for _ in range(tamanho_senha))
if (any(c.islower() for c in password) # tem alguma letra minúscula
and any(c.isupper() for c in password) # tem alguma letra maiúscula
and any(c in symbols for c in password) # tem algum símbolo
and sum(c.isdigit() for c in password) >= 3): # tem pelo menos 3 dígitos
break # deu certo, sai do while
print(password)
Repare que as letras e dígitos já estão prontas no módulo string
.
No caso eu só vejo se tem pelo menos uma letra maiúscula, minúscula e um símbolo, e vejo se tem pelo menos 3 dígitos. Não é difícil adaptar para o seu caso, que tem quantidades fixas. Por exemplo:
import secrets
from string import ascii_letters, digits
symbols = '!#$%&()*+><^~@-_çÇ`/|ªº¿'
alphabet = ascii_letters + digits + symbols
qtd_letras = 10
qtd_digitos = 4
qtd_simbolos = 2
tamanho_senha = qtd_simbolos + qtd_digitos + qtd_letras
while True:
password = ''.join(secrets.choice(alphabet) for _ in range(tamanho_senha))
if (sum(c.isalpha() for c in password) == qtd_letras
and sum(c in symbols for c in password) == qtd_simbolos
and sum(c.isdigit() for c in password) == qtd_digitos): # tem pelo menos 3 dígitos
break # deu certo, sai do while
print(password)