在Python中,使用方法作为实例和类方法

时间:2016-08-15 15:10:37

标签: python

我正在编写一个播放Tic Tac Toe的程序,并有ComputerPlayer的各种版本,例如RandomPlayerTHandPlayer

class RandomPlayer(ComputerPlayer):
    def __init__(self, mark):
        super(RandomPlayer, self).__init__(mark=mark)

    def get_move(self, board):
        moves = board.available_moves()
        if moves:   # If "moves" is not an empty list (as it would be if cat's game were reached)
            return moves[np.random.choice(len(moves))]    # Apply random select to the index, as otherwise it will be seen as a 2D array

class THandPlayer(ComputerPlayer):
    def __init__(self, mark):
        super(THandPlayer, self).__init__(mark=mark)

    def get_move(self, board):
        moves = board.available_moves()
        if moves:   # If "moves" is not an empty list (as it would be if cat's game were reached)
            for move in moves:
                if board.get_next_board(move, self.mark).winner() == self.mark:                         # Make winning move (if possible)
                    return move
                elif board.get_next_board(move, self.opponent_mark).winner() == self.opponent_mark:     # Block opponent's winning move
                    return move
            else:
                # return moves[np.random.choice(len(moves))]        # This is a repetition of the code in RandomPlayer and is not DRY
                randomplayer = RandomPlayer(mark=self.mark)
                return randomplayer.get_move(board)
                # return RandomPlayer.get_move(board)         # This returns an error as "get_move" is an instance method

如果不能进行获胜动作或对手的获胜动作被阻挡,THandPlayer也会随机选择动作。现在我通过创建RandomPlayer的实例并在其上调用get_move来实现此目的。但是,如果get_move可以被解释为可以将其解释为类方法和实例方法,则可以使其更简洁。这可能吗?

修改

为简化问题,假设我们有两个类RandomPlayerOtherPlayer,两个类都有一个实例方法get_move

import numpy as np

class RandomPlayer:
    def get_move(self, arr):
        return np.random.choice(arr)

class OtherPlayer:
    def get_move(self, arr):
        if max(arr) > 5:
            return max(arr)
        else:
            randomplayer=RandomPlayer()
            return randomplayer.get_move(arr)

arr = np.arange(4)

otherplayer = OtherPlayer()
print otherplayer.get_move(arr)

是否可以在RandomPlayer中使用get_move的{​​{1}}方法,而无需创建OtherPlayer的实例?

4 个答案:

答案 0 :(得分:2)

听起来您正在寻找staticmethod,它既不能访问cls也不能访问self,但可以通过以下方式访问:

>>> class Foo:
...     @staticmethod
...     def bar():
...         print('baz')
...         
>>> Foo.bar()
baz
>>> Foo().bar()
baz

答案 1 :(得分:1)

随机移动是一种特定类型的移动;放一个在ComputerPlayer中生成一个的方法;然后,RandomPlayerTHandPlayer都可以根据需要调用它。

class ComputerPlayer(...):
    @staticmethod
    def choose_random_move(moves):
        if moves:
            return moves[np.random.choice(len(moves))]


class RandomPlayer(ComputerPlayer):    
    def get_move(self, board):
        moves = board.available_moves()
        if moves:
            return self.choose_random_move(moves)


class THandPlayer(ComputerPlayer):

    def get_move(self, board):
        moves = board.available_moves()
        for move in moves:
            for mark in [self.mark, self.opponent_mark]:
                if board.get_next_board(move, mark).winner() == mark:
                    return move
        else:
            return self.choose_random_move(moves)

一些额外的说明:

  • 如果您的__init__方法除了调用super之外没有执行任何操作并传递完全相同的参数,请不要实现它;只需直接调用继承的方法。

  • 可以重构对获胜者的两次检查。

  • choose_random_move不一定需要是静态方法;您可以将其保留为具有默认实现的实例方法,该实现在选择移动时忽略任何特定于播放器的信息。如果他们愿意,派生类可以覆盖该方法。

答案 2 :(得分:1)

(这是我的另一个答案的替代方案,使用不同的抽象。)

随机移动不是与玩家相关的东西,而是与棋盘相关的东西;它就像board.available_moves,但返回一个动作而不是所有动作。

 class Board(...):
     # Given how often this is called by or before
     # random_move(), it would be smart to implement
     # some kind of caching so that the available
     # moves don't have to be recalcuated for the same board
     # state every time it is called.
     def available_moves(self):
         ...

     def random_move(self):
         moves = self.available_moves()
         if moves:
             return moves[np.random.choice(len(moves))]

class RandomPlayer(ComputerPlayer):

    def get_move(self, board):
        return board.random_move()

class THandPlayer(ComputerPlayer):
    def get_move(self, board):
        moves = board.available_moves()
        if moves:
            for move in moves:
                if board.get_next_board(move, self.mark).winner() == self.mark:
                    return move
                elif board.get_next_board(move, self.opponent_mark).winner() == self.opponent_mark:
                    return move
            else:
                return board.random_move()

答案 3 :(得分:0)

为了完整起见,这是我对deceze建议的解决方案的实现,其中我还遵循chepner的建议来重构两个布尔语句:

class RandomPlayer(ComputerPlayer):
    def __init__(self, mark):
        super(RandomPlayer, self).__init__(mark=mark)

    @staticmethod
    def get_move(board):
        moves = board.available_moves()
        if moves:   # If "moves" is not an empty list (as it would be if cat's game were reached)
            return moves[np.random.choice(len(moves))]    # Apply random selection to the index, as otherwise it will be seen as a 2D array

class THandPlayer(ComputerPlayer):
    def __init__(self, mark):
        super(THandPlayer, self).__init__(mark=mark)

    def get_move(self, board):
        moves = board.available_moves()
        if moves:
            for move in moves:
                if THandPlayer.next_move_winner(board, move, self.mark):
                    return move
                elif THandPlayer.next_move_winner(board, move, self.opponent_mark):
                    return move
            else:
                return RandomPlayer.get_move(board)         

    @staticmethod
    def next_move_winner(board, move, mark):
        return board.get_next_board(move, mark).winner() == mark

静态方法既可用于默认随机播放器,也可用于重构布尔语句。