简体   繁体   中英

Is there a way to sort objects created through classes that is made up of both strings and integers in Python?

I have an assignment to create a deck of cards through the use of a class with methods that deal, shuffle, fan, order and checks if the deck is in order but I am having trouble creating the last one. The deck must be ordered by their values in each suit, and the suits ordered as Clubs, Diamonds, Hearts, and then Spades. My code up to this point is found below:

import random

class Card():

    def __init__(self, value, suit):
        self.value = value
        self.suit = suit


    def show_card(self):
        print(str(self.value)+ " of " + str(self.suit))


class Deck():

    deck_of_cards = []
    cards_in_play = [] 
    draw_card = None

    def __init__(self):
        self.make_deck()      


    def make_deck(self):
        for suit in ['Clubs', 'Diamonds', 'Hearts', 'Spades']:
            for value in [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']:
                self.deck_of_cards.append(Card(value,suit))
                self.cards_in_play.append(Card(value, suit))

    def shuffle(self):
        self.shuffled_deck = random.shuffle(self.cards_in_play)


    def fan(self):
        for card in self.cards_in_play:
            card.show_card()

    def deal(self):
        draw_card=self.cards_in_play.pop()
        draw_card.show_card()

    def order(self):
        for suit in ['Club', 'Diamonds', 'Hearts', 'Spades']:
            for value in [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']:
                self.deck_of_cards.append(Card(value, suit))
                self.cards_in_play.append(Card(value, suit))

Some code fixes before we go on:

  1. Universalize the suits and values list so that it can be used by all classes if needed.
  2. Move deck_of_cards and cards_in_play inside the __init__ function. If you do not, this is called a "class attribute" and will make it so every class has that value if not initialized (likely something you do not intend).
class Test:
    a = 10

t1 = Test
t2 = Test

t1.a = 11
print(t2.a) # >>> 11
  1. random.shuffle() is a function that runs in place. In other words, it returns None , but modifies the list given to it.
import random

l = ["a", "b", "c"]
print(random.shuffle(l)) # >>> None
print(l) # >>> [b, c, a]
  1. Don't print things -- return them. It will make your code not only clearer, but also more useful. If you want to print something that a function returns, just simply print the return.
def deal(self):
    draw_card = self.cards_in_play.pop()
    return draw_card
  1. Familiarize yourself with sort and the __lt__ (detail below).

import random
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']

class Card:
    def __init__(self, value, suit):
        self.suit = suit
        self.value = value

    def __lt__(self, other):
        if suits.index(self.suit) > suits.index(other.suit):
            return False

        if values.index(self.value) > values.index(other.value):
            return False

        return True

    def __repr__(self):
        return f"{self.value} of {self.suit}"

class Deck:
    def __init__(self):    
        self.deck_of_cards = []
        self.cards_in_play = []

        self.make_deck()

    def make_deck(self):
        for suit in suits:
            for value in values:
                self.deck_of_cards.append(Card(value,suit))
                self.cards_in_play.append(Card(value, suit))

    def shuffle(self):
        random.shuffle(self.cards_in_play)

    def fan(self):
        for card in self.cards_in_play:
            print(card)

    def deal(self):
        draw_card = self.cards_in_play.pop()
        return draw_card

    def order(self):
        self.cards_in_play.sort()

    def __repr__(self):
        return repr(self.cards_in_play)

Utilizing the magic method __lt__ (less than), we can use the function sort to automatically sort a class . To make the point more clear, notice the following:

# Your new `order` function.
def order(self):
    self.cards_in_play.sort()

# Implementing __lt__ now allows us to do the following comparison (which is what sort will use to sort your list of Card objects):
print(Card("Clubs", 2) > Card("Clubs", 3)) # True

# If you notice on docs, __ge__ (greater than), __eq__ (equal than), etc. can also be implemented to give you full flexibility.

Notice I also added the __repr__ function to both Deck and Card so that you can instead more simply do:

card = Card("Clubs", 2)
print(card) # >>> 2 of Clubs

Edit: @Discussion below.

suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']

class Card:
    def __init__(self, value, suit):
        self.suit = suit
        self.value = value

    def __lt__(self, other):
        if suits.index(self.suit) > suits.index(other.suit):
            return False

        if values.index(self.value) > values.index(other.value):
            return False

        return True

    def __eq__(self, other):
        if self.suit == other.suit and self.value == other.value:
            return True
        else:
            return False

    def __repr__(self):
        return f"{self.value} of {self.suit}"

With the new __eq__ method, we can use the == sign between classes.

c1 = Card(2, "Clubs")
c2 = Card(2, "Diamonds")
c3 = Card(2, "Diamonds")

print(c1 == c2) # False
print(c1 > c2) # False
print(c1 < c2) # True
print(c2 == c3) # True

This allows us to compare the Card s with ease.

You can initialize your Card class as follows:

values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King', 'Ace']
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']

class Card:
    def __init__(self, value, suit):
        self.suit = suit
        self.value = value
        #if value order matters in first place
        self.rank = 4*values.index(self.value) + suits.index(self.suit)
        #else
        self.rank = 13*suits.index(self.suit) + values.index(self.value)

and create the check function as follows:

class Deck:
    def check(self):
        rank_before = self.cards_in_play[0].rank
        for card in self.cards_in_play[1:]:
            rank = card.rank
            if rank > rank_before:
                rank_before = rank
            else:
                return False
        return True

The canonical way to handle this is to realize that you have two identities for the card: its play value and its display value. You need a simple cognate to the __repr__ function.

Very simply, value value with a simple rank integer, card_rank , 0-12. The display value, what you print any time a human refers to it, is handled with a trivial translation list:

print_value = (
    '2', '3', '4', '5', '6', '7', '8', '9', '10',
    'Jack', 'Queen', 'King', 'Ace'
)

Whenever you need to output a value, you simply use print_value[card_rank] . Put this into the __repr__ function of your Card class.

With this partitioning of functionality, sorting by card_rank solves your problem without further ado.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM