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)