#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
, и в этом случае вам вообще не нужно будет определять метод длины.