Multiple constructors: the Pythonic way?

I have a container class that holds data. When the container is created, there are different methods to pass data.

  1. Pass a file which contains the data
  2. Pass the data directly via arguments
  3. Don't pass data; just create an empty container

In Java, I would create three constructors. Here's how it would look like if it were possible in Python:

    class Container:

        def __init__(self):
            self.timestamp = 0
            self.data = []
            self.metadata = {}

        def __init__(self, file):
            f = file.open()
            self.timestamp = f.get_timestamp()
            self.data = f.get_data()
            self.metadata = f.get_metadata()

        def __init__(self, timestamp, data, metadata):
            self.timestamp = timestamp
            self.data = data
            self.metadata = metadata

In Python, I see three obvious solutions, but none of them is pretty:

A : Using keyword arguments:

    def __init__(self, **kwargs):
        if 'file' in kwargs:
            ...
        elif 'timestamp' in kwargs and 'data' in kwargs and 'metadata' in kwargs:
            ...
        else:
            ... create empty container

B : Using default arguments:

    def __init__(self, file=None, timestamp=None, data=None, metadata=None):
        if file:
            ...
        elif timestamp and data and metadata:
            ...
        else:
            ... create empty container

C : Only provide constructor to create empty containers. Provide methods to fill containers with data from different sources.

    def __init__(self):
        self.timestamp = 0
        self.data = []
        self.metadata = {}

    def add_data_from_file(file):
        ...

    def add_data(timestamp, data, metadata):
        ...

Solutions A and B are basically the same. I don't like doing the if/else, especially since I have to check if all arguments required for this method were provided. A is a bit more flexible than B if the code is ever to be extended by a fourth method to add data.

Solution C seems to be the nicest, but the user has to know which method he requires. For example: he cant do c = Container(args) if he doesn't know what args is.

Whats the most Pythonic solution?

You can't have multiple methods with same name in Python. Function overloading - unlike in Java - isn't supported.

Use default parameters or **kwargs and *args arguments.

You can make static methods or class methods with the @staticmethod or @classmethod decorator to return an instance of your class, or to add other constructors.

I advise you to do:

    class F:

        def __init__(self, timestamp=0, data=None, metadata=None):
            self.timestamp = timestamp
            self.data = list() if data is None else data
            self.metadata = dict() if metadata is None else metadata

        @classmethod
        def from_file(cls, path):
           _file = cls.get_file(path)
           timestamp = _file.get_timestamp()
           data = _file.get_data()
           metadata = _file.get_metadata()       
           return cls(timestamp, data, metadata)

        @classmethod
        def from_metadata(cls, timestamp, data, metadata):
            return cls(timestamp, data, metadata)

        @staticmethod
        def get_file(path):
            # ...
            pass

⚠ Never have mutable types as defaults in python. ⚠ See here.

From: stackoverflow.com/q/44765482

Back to homepage or read more recommendations: