簡體   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