繁体   English   中英

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

[英]In Python, use a method both as instance and class method

我正在编写一个播放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)

是否有可能使用RandomPlayerget_move的方法OtherPlayer而无需创建实例RandomPlayer

听起来您正在寻找一种staticmethod ,该方法无法访问clsself但可以通过以下任一方法进行访问:

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

随机移动是一种特定类型的移动。 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不一定需要是静态方法; 您可以将其保留为具有默认实现的实例方法,该实现在选择举动时会忽略任何特定于玩家的信息。 派生类可以根据需要重写该方法。

(这是我的其他答案的替代方法,使用了不同的抽象。)

随机移动与玩家无关,而与棋盘相关。 就像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()

为了完整起见,这是我对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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM