Hi there thanks for sharing your code, I have a few comments/suggestions.
Implement the __str__ method
In your code, you have a method specifically designed to print out what your card looks like. Using
__str__ method is a special method designed to return a string representation of our object. In your case it would look something like this.
return str(self.value) + " of " + str(self.suit)
Calling could would look like this
A lot of the method names you've chosen provide information about the class as well. If I want to print a Card object, to me it would make sense to say,
card.print() and not
card.print_card() I already know I'm dealing with a Card. This kind of sounds like it can print any card, not just the instance I'm dealing with.
Some more examples from your code
Player IS a Deck
In your current code, you have a player class derived from a Deck. This means that every Player IS a Deck. To me it makes more sense to use composition over inheritance. I.e. a Player HAS a Deck. But even then, a player doesn't really have a deck either, they have a hand like you have in your example.
Your game can deal with players (with hands) and one or more decks. Players could be able to draw cards FROM decks. It could be 2 different decks, or all from one deck.
Consider the following.
player1 = Player("Bob")
player2 = Player("Jim")
deck = Deck(shuffled=true) # deck used by all players
discard_pile = Deck()
Notice here how I created another Deck instance to act as the discard pile. Really a Deck is just a bunch of cards right? I Deck doesn't have a "playing" pile and a "discard" pile. What if my game doesn't ever discard cards? By having multiple decks to represent multiple piles of cards, then I have full control. So e.g. If I want to make a solitaire game, that could be represented by a number of smaller "decks" that get added to. The managing of when cards getting added/removed can be handled in some Game class.
Unneeded class variables
Consider these 2 methods
self._draw_from_deck = self.card_list.pop()
self._return_to_deck = self.card_list.append(self._draw_from_deck)
Implementing adding/removing cards like this severely limits flexibility. At the moment, the only card I can add back to the deck is the last one I took off. But what if I run into this scenario.
As a player, I want to take a card off the top of the deck, and replace it with a different card from my hand.
This seems like a fairly reasonable thing to want to do, but at the moment, as soon as I draw 2 cards without placing one back, that other card is gone forever as it's no longer the self._draw_from_deck value anymore.
What if I had code like this
card = player.draw(deck)
card_to_insert = player.get(Card(5, "Spades"))
If I had the functionality to take any card object and place it to the deck, suddenly we don't need to worry about what the last card was to be taken or the last card that was put back (if we want these values we can of course still hold onto them)
General tips and things to watch out for
Consider this line
self.shuffled_cards = random.shuffle(self.card_list) and now look at this documentation about the random.shuffle method here https://docs.python.org/2/library/random.html your code isn't doing what you think it's doing here. (hint, in-place)
In your list of values, you have a mix of data types, integers and strings. I would stick to just one data type per collection. Since you want to use strings to represent "Jack", "Queen" etc. I would stick with Strings for everything "1", "2", "3" etc. And parse the integer value from it where needed.
Using PEP8 standards, any method without a leading underscore can be considered "public" and I should be able to use freely and not need to worry about unexpected results. What happens if I do the following.
deck = Deck()
We constantly append to the same list.
Being able to compare cards for equality would be useful, consider overriding the __eq__ and __hash__ methods. And if you want to be able to do card1 < card2, you can also override __lt__ and __gt__
My final suggestion would be, try and make a card game using what you've written. See what problems you run into, what works, what doesn't work. Something straight forward like War.
Hopefully you found this review helpful!