anon_swe
anon_swe

Reputation: 9335

Python: Multiple One-Argument Constructors

I have a Game class which has a field that's a Board object. I currently make a Game by passing in a string encoding information about the game and making the Board object.

However, I now have a bunch of Board objects and want to make a new constructor that takes in the Board object directly.

Here's my current constructor:

def __init__(self, game_string):
    self.single_list = game_string.split(",")
    self.board = self.parse_game_string(game_string)
    self.directions = self.get_all_directions()
    self.red_number_of_streaks = self.get_number_of_streaks("R")
    self.black_number_of_streaks = self.get_number_of_streaks("B")

But now I have the board object, so I'd like to just do:

def __init__(self, board):
    self.board = board
    self.directions = self.get_all_directions()
    self.red_number_of_streaks = self.get_number_of_streaks("R")
    self.black_number_of_streaks = self.get_number_of_streaks("B")

I don't think Python will know how to distinguish between these two constructors. I could determine what to do based on the type of argument? Something like:

if isinstance(str): # usual constructor functionality elif isinstance(Game): # new constructor functionality

Is there a better way?

Thanks!

Upvotes: 2

Views: 126

Answers (3)

phihag
phihag

Reputation: 287755

Looking at the type of input is certainly an option:

class Game(object):
    def __init__(self, str_or_board):
        if isinstance(str_or_board, Board):
            board = str_or_board
        else:
            board = self.parse_game_string(str_or_board)

        self.board = board

Instead of isinstance, duck typing is also an option.

Alternatively, you can use a factory function:

class Game(object):
    def __init__(self, board):
        assert isinstance(board, Board)
        self.board = board

    @classmethod
    def fromstring(cls, board_str):
        return cls(self.parse_game_string(board_str))

# Use like ...
game1 = Game(Board())
game2 = Game.fromstring('foobar')

Upvotes: 3

drumhellerb
drumhellerb

Reputation: 381

For method overloading in Python, you can pass both arguments (game_string, board) to the __init__ initializing them as None.

You can then have one constructor such as:

def __init__(self, game_string=None, board=None):
    self.single_list = game_string.split(",")
    self.board = self.parse_game_string(game_string) if game_string else board
    self.directions = self.get_all_directions()
    self.red_number_of_streaks = self.get_number_of_streaks("R")
    self.black_number_of_streaks = self.get_number_of_streaks("B")

If game_string is provided it will be used as the board. Otherwise, the board argument will be used.

Upvotes: 2

Blender
Blender

Reputation: 298056

I would do it like this:

class Game:
    def __init__(self, board):
        ...

    @classmethod
    def from_string(cls, game_string):
        board = cls.parse_game_string(game_string)
        game = cls(board)
        game.something = 5

        return game

    @staticmethod
    def parse_game_string(game_string):
        ...

        return Board(...)

Game.from_string would then construct an instance of Game out of a string, using the default Game initializer which accepts a Board object.

You will have to make Game.parse_game_string a static method for it to be usable from the Game.from_string class method.

Upvotes: 4

Related Questions