Search code examples
pythonfunctionself

Passing self from a member function to a function in another class


For context, I am trying to implement a minimax algorithm in python to play tic tac toe. Here's the relevant code:

TicTacToe.py

def dumbPlayer(game, gameType):
    return game.successors()[0]


class minimax:
    tree = None
    def minimaxPlayer(self, game, gameType):
        return game.successors()[0]


class TicTacToe:
    def play(self, xPlayer=consolePlayer, oPlayer=consolePlayer, output=consolePrint):
        while self.winner is None:
            output(self)
            if self.turn == 'X': self.move(xPlayer(self, TicTacToe))
            elif self.turn == 'O': self.move(oPlayer(self, TicTacToe))
        output(self, True)
        return

main.py:

import TicTacToe as TTT

player2 = TTT.minimax.minimaxPlayer
test = TTT.TicTacToe()

test.play(TTT.dumbPlayer, player2)

Passing in the dumbPlayer function works fine, the problem comes with the minimaxPlayer function. When the play function calls the minimaxPlayer function, I get the following error:
TypeError: minimaxPlayer() missing 1 required positional argument: 'gameType' From what I understand of python, the "self" parameter is automatically passed to the function and doesn't (or maybe can't) be passed explicitly, so as far as the play function is concerned,
def dumbPlayer(game, gameType) and
def minimaxPlayer(self, game, gameType)
should be equivalent, but it appears that's not correct in this case.

Is there an easy fix for this? Or will I need to refactor my code? I already have some ideas for how I can restructure the code if I have to, but I'd rather not, and I'd like to know the underlying reason for why this doesn't work how I expect.


Solution

  • The self argument for an instance method is automatically bound to the instance that you got the method from, but you're accessing the class's version of that method.

    Instead of:

    player2 = TTT.minimax.minimaxPlayer
    

    do:

    minimax_instance = TTT.minimax()
    player2 = minimax_instance.minimaxPlayer
    

    and it should behave the way you want.

    Note: the standard convention in Python is to use CamelCase for class names and snake_case for instance names, which eliminates the need to use weird names like minimax_instance to differentiate between the class and the instance. You probably also want tree to be an instance attribute rather than a class attribute (if the idea is to be able to use the Minimax class with different games, you don't want the different instances stepping on each others' state)...

    class Minimax:
        def __init__(self):
            self.tree = None
    
        def player(self, game, game_type):
            return game.successors()[0]
    
    minimax = TTT.Minimax()
    player2 = minimax.player