В питоне есть два метода преобразования в строку: __str__() и __repr__().
Первый преобразует объект в какую-то человекочитаемую строку. Он вызывается, если передать объект в функцию str().
Второй обычно даёт более "техническое" представление объекта - в идеале, он показывает питоновский код, который нужно выполнить, чтобы этот объект получить. Но это требование не строгое, и никто не запрещает в __repr__() показывать что-то другое. Метод вызывается, если передать объект в функцию repr().
Если у объекта нет __str__(), то питон попробует вызвать __repr__(). А __repr__() есть у всех объектов - он наследуется от базового object.
Ты выводишь список целиком. У списка (list) нет метода __str__(), он использует __repr__() - и поэтому пытается вызывать repr() на твоих объектах, а repr() даже не пытается вызвать __str__(), она сразу идёт к __repr__().
Я бы посоветовал переписать метод Deck.__str__() так:
def __str__(self):
return '[' + (', '.join(str(card) for card in self.mydeck)) + ']'
Тперь он явно вызывает str() на каждой карте, а потому будет использоваться метод Card.__str__().