Um detalhe: para pegar uma determinada quantidade de cartas aleatórias, não precisa necessariamente embaralhar tudo, você pode usar a função random.sample.

E não precisa de range para percorrer as listas, você pode fazer for elemento in lista diretamente (assim não precisa ficar mudando o range quando o tamanho da lista mudar, por exemplo - na verdade é melhor porque nem precisa saber o tamanho da lista). E desde o Python 3.6 é possível usar f-string para construir strings.

Ficaria assim:

import random

naipes = ['Ouros', 'Espadas', 'Copas', 'Paus']
numeros = ['Ás', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K']
cartas = []
for naipe in naipes:
    for num in numeros:
        cartas.append(f'{num} de {naipe}')

# dê um nome melhor do que "b"
qtd_cartas = int(input('Qual a quantidade de cartas?\n'))
for carta in random.sample(cartas, qtd_cartas):
    print(carta)

Claro que se for distribuir as cartas entre os jogadores, aí é melhor embaralhar tudo com shuffle. Mas se só quer uma amostra aleatória, sample é mais adequado por manter a lista original intacta e só pegar os elementos aleatoriamente.