我如何根据对其他相同实例属性的更改来制作我的(派生的?)python 实例属性 "self-update"?

How do I make my (derived?) python instance attributes "self-update" based on alterations to other same-instance attributes?

我知道我对 Python 属性的工作原理有误解,因为我在这里写这个问题,但我不知道我到底误解了什么。我正在尝试获取

self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])

根据 self.hand 获得它们的值,我在实例化时将其传递给 __init__。在整个游戏中,我都在更改 .hand,我希望每次更改 .hand.card.card_val 都更改,而不必告诉它在其他地方(外部属性定义)。我知道有一种方法可以做到这一点,或者至少我认为有,我的意思是简单地根据 .hand 在任何给定时间将它们的值定义为继承,而不调用内部或外部函数。

在发布的代码中,我已将其更改为按照游戏说明的要求工作,使用...

def get_card_vals(p1, p2):
    for player in [p1, p2]:
        player.card = player.hand[player.card_number].split()
        player.card_val = deck.ranks.get(player.card[0])
    print("{a} vs. {b}".format(a = p1.card, b = p2.card))
    print("---------------------------")

...但这就是我想要改变的。我希望在处理实例时在属性定义内更简洁地执行该函数正在执行的操作。基本上我的问题是为什么这两个属性不能直接从我通过传递给 init 的“hand”定义的第一个属性获取它们的值?

任何帮助将不胜感激,更重要的是,我认为不仅仅是解决方案,它会帮助我更多地理解我对属性、实例和实例化以及所有这些工作方式的误解,以便我知道我的想法是错误的。谢谢!

import random
from random import shuffle
from collections import deque

class Deck():
    def __init__(self):
        self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
        self.suites = ["Heart", "Diamond", "Spade", "Club"]
        self.cards = []

    def create_cards(self):
        for suite in self.suites:
            for key in self.ranks.keys():
                self.cards.append(key + " " + suite)

    def shuffle(self):
        random.shuffle(deck.cards)

deck = Deck()
deck.create_cards()
deck.shuffle()

class Player():
    def __init__(self, hand):
        self.name = "name"
        self.hand = hand
        self.card_number = 1
        self.card = self.hand[self.card_number].split()
        self.card_val = deck.ranks.get(self.card[0])

def war(bool, p1, p2):
    if bool == True:
        for player in [p1, p2]:
            player.card_number = 4
    else:
        for player in [p1, p2]:
            player.card_number = 0

p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))

p2.name = "The Computer"

def get_card_vals(p1, p2):
    for player in [p1, p2]:
        player.card = player.hand[player.card_number].split()
        player.card_val = deck.ranks.get(player.card[0])
    print("{a} vs. {b}".format(a = p1.card, b = p2.card))
    print("---------------------------")

def cant_war_lose(winner, loser):
    print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))

def battle_win(winner, loser):
    print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))

def play_cards(p1, p2):
    war(False, p1, p2)
    get_card_vals(p1, p2)
    if p1.card_val > p2.card_val:
        p1.hand.append(p2.hand.popleft())
        p1.hand.rotate(-1)
    elif p1.card_val == p2.card_val:
        if len(p1.hand) < 5 or len(p2.hand) < 5:
            if len(p1.hand) > len(p2.hand):
                cant_war_lose(p1.name, p2.name)
            else:
                cant_war_lose(p2.name, p1.name)
            return 0
        else:
            input("War is inititated! Press Enter to continue!")
            print("---------------------------")
            war(True, p1, p2)
            get_card_vals(p1, p2)
            if p1.card_val > p2.card_val:
                for i in range(0,5):
                    p1.hand.append(p2.hand.popleft())
                p1.hand.rotate(-5)
            elif p1.card_val < p2.card_val:
                for i in range(0,5):
                    p2.hand.append(p1.hand.popleft())
                p2.hand.rotate(-5)
            else:
                p1.hand.rotate(-1)
                p2.hand.rotate(-1)
    elif p1.card_val < p2.card_val:
        p2.hand.append(p1.hand.popleft())
        p2.hand.rotate(-1)
    if len(p1.hand) != 0 and len(p2.hand) != 0:
        input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
        print("---------------------------")
    else:
        if len(p1.hand) > len(p2.hand):
            battle_win(p1.name, p2.name)
        else:
            battle_win(p2.name, p1.name)
        return 0

def game_run():
    run = 1

    p1.name = input("Player 1's name? ")
    print("---------------------------")

    while run == 1:
        if play_cards(p1, p2) == 0:
            run = 0

game_run()

你误会的是变量,不是实例。例如,属性 card 是附加到实例的标量变量。用

分配给它
self.card = <blah>

不会将其绑定到 blah 以进行不断的重新计算。这是赋值,不是内存映射。如果你想要 long-term 绑定,你必须自己编写维护例程——你已经在某种程度上用一致的重新计算完成了——或者你必须分配一个 mutable 引用 self.card,因此 cardsame 对象引用为您创建的表达式。

鉴于您一直在旋转和改变手,这在您的设计中是不可行的。相反,只需编写一个访问例程,也许 get_next_card(hand),它将轮换手牌,取出牌,并 return 所需的等级和花色。

如果您打算编写更多纸牌游戏,您还会发现定义 class cardclass hand 以及适当的支持例程会很方便。将卡片维护为一对整数;转换为仅供打印的字符串。

这让你感动吗?

您可以使用 property 装饰器创建 calculated property

class Player():
    def __init__(self, hand):
        self.name = "name"
        self.hand = hand
        self.card_number = 1

    @property
    def hand(self):
        return self._hand

    @hand.setter
    def hand(self, value):
        self._hand = value
        self.card = self._hand[self.card_number].split()
        self.card_val = deck.ranks.get(self.card[0])

对于任何想要比较问题前后和最终解决方案的人,下面是针对我的特定问题的工作代码。我所要做的就是将 self.card 和 self.card_val 转换为计算得出的 属性。通过手工传递,随后只处理手工,计算 self.card & self.card_val,因为每次我处理 class 的实例(通过手工处理)时,这些“方法属性" 正在被调用和更改。感谢您的输入,伙计们!

class Player():
    def __init__(self, hand):
        self.name = "name"
        self.card_number = 1
        self.hand = hand

    @property
    def card(self):
        return self.hand[self.card_number].split()

    @property
    def card_val(self):
        return deck.ranks.get(self.card[0])