Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple constructors: the Pythonic way? [duplicate]

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?

like image 988
Johannes Avatar asked Jun 26 '17 17:06

Johannes


People also ask

Can we have 2 constructors in Python?

Python does not support explicit multiple constructors, yet there are some ways using which the multiple constructors can be achieved. If multiple __init__ methods are written for the same class, then the latest one overwrites all the previous constructors.

Can you have multiple constructors in C++?

In c++, there are multiple constructors in a class under the same name but a different list of arguments. This concept of constructor overloading in c++ is quite similar to function overloading. Usually, you should create more than one constructor in a class to initialize member variables differently for objects.

Can you overload constructors in Python?

Python does not support Constructor overloading; it has no form of function. In Python, Methods are defined solely by their name, and there can be only one method per class with a given name.

Can you have two constructors in Java?

The technique of having two (or more) constructors in a class is known as constructor overloading. A class can have multiple constructors that differ in the number and/or type of their parameters. It's not, however, possible to have two constructors with the exact same parameters.


1 Answers

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.

like image 148
glegoux Avatar answered Sep 21 '22 17:09

glegoux