Атрибут класса, определенный как длина другого атрибута класса типа списка, не обновляется и всегда возвращает одно и то же значение

#python #oop #playing-cards #class-attributes

Вопрос:

Я пишу код для карточной игры «Война». Определяя класс «Колода», я определил атрибут «self.cards», который дает список всех карт, находящихся в настоящее время в колоде карт. Я также определил атрибут «я».длина», которая, как ожидается, даст длину атрибута «self.cards».

НО когда я использую выражение deck.length , оно возвращает фиксированное значение , которое не меняется даже при удалении/добавлении карт в колоде («self.карты»). С другой стороны , когда я использую выражение len(deck.cards) , оно возвращает желаемый результат (то есть обновленный размер колоды).

Код таков:

 # I've used only two suits and three ranks to explain this problem

suits = ('Hearts','Spades')
ranks = ('Queen','King','Ace')

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

class Deck():
    
    def __init__(self):
        # Create an empty deck and fill it with the available cards
        self.cards = []
        for suit in suits:
            for rank in ranks:
                self.cards.append(Card(suit,rank))
        
        self.length = len(self.cards)

    def deal_top_card (self):
        self.cards.pop(0)

deck = Deck()
# Removing Three cards one after the other
deck.deal_top_card()
deck.deal_top_card()
deck.deal_top_card()
print(f"Length according to length attribute = {deck.length}")
print(f"Length of the list of cards = {len(deck.cards)}")
 

Вывод приведенного выше кода выглядит так:

 Length according to length attribute = 6
Length of the list of cards = 3
 

Ответ №1:

Ваша проблема в том, что length это всего лишь атрибут. Это означает, что он получает значение, когда оно определено, и сохраняет это значение до тех пор, пока оно не будет присвоено снова.

Но в Python есть концепция, которая делает со свойствами то, что вы хотите:

 class Deck():
    
    def __init__(self):
        # Create an empty deck and fill it with the available cards
        self.cards = []
        for suit in suits:
            for rank in ranks:
                self.cards.append(Card(suit,rank))
        
    @property
    def length(self):
        return len(self.cards)

    def deal_top_card (self):
        self.cards.pop(0)
 

Свойство используется в качестве специального элемента, который фактически выполняет метод каждый раз, когда он используется. Теперь вы можете успешно использовать:

 deck = Deck()
# Removing Three cards one after the other
deck.deal_top_card()
deck.deal_top_card()
deck.deal_top_card()
print(f"Length according to length attribute = {deck.length}")
print(f"Length of the list of cards = {len(deck.cards)}")
 

и получите, как и ожидалось:

 Length according to length attribute = 3
Length of the list of cards = 3
 

Кстати, свойства еще более мощные, чем это, и могут вызывать другие методы при назначении или удалении. Читать https://docs.python.org/3/library/functions.html#property для большего…

Ответ №2:

Вы только обновляете себя.длина в методе init (). Так что он не обновляется каждый раз, когда вы открываете карту. Инициализация вызывается только один раз, когда вы создаете объект. Вы должны добавить это:

 def deal_top_card (self):
    self.cards.pop(0)
    self.length = len(self.cards)
 

Чтобы обновлять длину каждый раз, когда вы открываете карту

Ответ №3:

Атрибут At не будет обновляться сам по себе. Похоже, вам нужно свойство или метод.

Свойство-это то, что вы хотите использовать, если хотите получить доступ length , как если бы это был атрибут. Это также может позволить вам контролировать, что произойдет, если какой-то код захочет переопределить значение длины:

 class Deck:
    ....
    @property
    def length(self):
        return len(self.cards)

...
print(f"Length according to length property = {deck.length}")
 

метод аналогичен, но вы вызываете его явно:

 class Deck:
    ....
    
    def length(self):
        return len(self.cards)

...
print(f"Length according to length method = {deck.length()}")
 

Наконец, вы можете определить магию __len__ и просто позвонить len(deck) , так как понятие длины применимо ко многим типам в Python.

Кроме того, вы можете подумать , что a Deck -это концепция, настолько похожая на «последовательность карт», что, возможно, имеет смысл сделать ее подклассом list , и в этом случае вам вообще не нужно будет определять метод длины.