继承类中定义的特殊字段的 Python 键入错误
我正在用 python 开发 Makao(澳门)纸牌游戏,并尝试在开发时使用最佳实践。 我对自己提出的挑战是使实现可扩展,因为该纸牌游戏有很多变体。 我还引入了 mypy 来检查注释。
所有当前游戏数据都存储在名为“Board”的单个类中,以下是该类的一部分:
@dataclass
class Board:
"""
The board is the main game state dataclass.
It stores all data about the game.
"""
players: Players
deck: Deck
discard_pile: DiscardPile
state: AbstractGameState
player_pauses: dict = field(default_factory=dict)
drawn_cards_in_turn: int = 0
winners: List[Player] = field(default_factory=list)
游戏可以运行到不同的状态,例如战斗状态、正常状态、排名调用状态等。 我决定在单独的类中定义每个状态,并且 Board 类具有当前状态的对象。 它工作正常并且可扩展(如果需要,我可以轻松添加新的状态),大多数状态都定义自己的字段。例如,下面的 rank_call
和 call_turns
是此状态所特有的:
@dataclass
class RankCallState(AbstractGameState):
"""
Rank call state.
"""
rank_call: Rank
call_turns: int
name: str = field(default=RANK_CALL_STATE_NAME, init=False)
Board 数据类的所有更改均由操作完成。 每个操作都可以访问 Board
实例,因此也可以访问当前的 State
对象。 但在尝试从状态访问这些特定字段的操作中,mypy 抱怨 AbstractGameState
没有这些字段。 这当然是可以理解的(Board 类需要 AbstractGameState,它没有这些特殊字段),但是我如何保留扩展系统并修复打字错误的能力?
我有一种可能的解决方案(在我看来并不理想) - 我也可以将所有特殊字段放入 Board 数据类中。 如果您看到其他更好的选择来处理这个问题,我很想听听您的意见。 谢谢
编辑: 下面是一个示例操作类(我将代码行精简为重要的代码行):
@dataclass
class RankCallAction(AbstractPlayerCardAction):
"""
Action to call a rank.
"""
rank_call: Optional[Rank] = None
@staticmethod
def _check_if_card_is_valid(
card: Card, top_card: Card, state: AbstractGameState
) -> bool:
card_is_jack = card.rank == Rank.JACK
suit_call_state_and_card_matching_jack = (
state.name == SUIT_CALL_STATE_NAME and card.suit == state.suit_call
)
return card_is_jack and suit_call_state_and_card_matching_jack
@classmethod
def get_single_cards_to_play(cls, hand: List[Card], board: Board) -> List[Card]:
"""
Returns the single cards to play.
:param hand: List of Card objects.
:param board: Board object.
:return: List of Card objects.
"""
state = board.state
top_card = board.discard_pile.top_card
return [
card for card in hand if cls._check_if_card_is_valid(card, top_card, state)
]
问题出在 check_if_card_is_valid
方法中,特别是 card.suit == state.suit_call
- mypy Mypy 说:“AbstractGameState”没有属性“suit_call” state
取自 Board
类,因为在 Board
类中,state
字段定义为 AbstractGameState
- 它导致了这个问题。
I'm working on Makao(Macao) card game in python and try to use best practices while development.
The challenge I made to myself to make the implementation extensible, because there are many variants of that card game.
I've also introduced mypy to check annotations.
All current game data is stored in single class called "Board", below is part of that class:
@dataclass
class Board:
"""
The board is the main game state dataclass.
It stores all data about the game.
"""
players: Players
deck: Deck
discard_pile: DiscardPile
state: AbstractGameState
player_pauses: dict = field(default_factory=dict)
drawn_cards_in_turn: int = 0
winners: List[Player] = field(default_factory=list)
Game can run into different states e.g. battle state, normal state, rank call state etc.
I decided to define each of the state in separate class and Board class has object of the current state.
It works fine and is extensible (I can easily add new States if needed), most of the states define its own fields. For example below rank_call
and call_turns
are unique to this state:
@dataclass
class RankCallState(AbstractGameState):
"""
Rank call state.
"""
rank_call: Rank
call_turns: int
name: str = field(default=RANK_CALL_STATE_NAME, init=False)
All changes to the Board data class are being done by actions.
Each action has access to the Board
instance and thus also to the current State
object.
But in actions which tries to access these specific fields from the state, mypy complains that AbstractGameState
does not have these fields.
This is understandable of course (Board class expects AbstractGameState which doesn't have these special fields), but how can I preserve the ability to extend the system and fix that typing errors?
I have one possible solution (not ideal in my opinion) - I can just put all the special fields in the Board data class as well.
I would like to hear from you if you see other, better options to handle that.
Thank you
EDIT:
Below is an example action class (I stripped down code lines to important ones):
@dataclass
class RankCallAction(AbstractPlayerCardAction):
"""
Action to call a rank.
"""
rank_call: Optional[Rank] = None
@staticmethod
def _check_if_card_is_valid(
card: Card, top_card: Card, state: AbstractGameState
) -> bool:
card_is_jack = card.rank == Rank.JACK
suit_call_state_and_card_matching_jack = (
state.name == SUIT_CALL_STATE_NAME and card.suit == state.suit_call
)
return card_is_jack and suit_call_state_and_card_matching_jack
@classmethod
def get_single_cards_to_play(cls, hand: List[Card], board: Board) -> List[Card]:
"""
Returns the single cards to play.
:param hand: List of Card objects.
:param board: Board object.
:return: List of Card objects.
"""
state = board.state
top_card = board.discard_pile.top_card
return [
card for card in hand if cls._check_if_card_is_valid(card, top_card, state)
]
The problem is in check_if_card_is_valid
method, specifically with card.suit == state.suit_call
- mypy says Mypy: "AbstractGameState" has no attribute "suit_call"
The state
is taken from Board
class and because in Board
class, state
field is defined as AbstractGameState
- it causes this issue.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于
Action
修改AbstractGameState
的特定子类,并且我们知道需要哪个特定子类,因此对操作进行类型检查的最简单方法是将类型缩小与isinstance
结合使用。这使我们能够得出更精确的当前状态类型。Since
Action
modifies a specific subclass ofAbstractGameState
, and we know which specific subclass is expected, the easiest way to get the action type-checked is to use type narrowing withisinstance
. This allows us to derive a more precise type of the current state.