How to overload __init__ method based on argument type?

Let's say I have a class that has a member called data which is a list.

I want to be able to initialize the class with, for example, a filename (which contains data to initialize the list) or with an actual list.

What's your technique for doing this?

Do you just check the type by looking at __class__?

Is there some trick I might be missing?

I'm used to C++ where overloading by argument type is easy.


Asked by: Aston500 | Posted: 24-09-2021






Answer 1

A much neater way to get 'alternate constructors' is to use classmethods. For instance:

>>> class MyData:
...     def __init__(self, data):
...         "Initialize MyData from a sequence"
...         self.data = data
...     
...     @classmethod
...     def fromfilename(cls, filename):
...         "Initialize MyData from a file"
...         data = open(filename).readlines()
...         return cls(data)
...     
...     @classmethod
...     def fromdict(cls, datadict):
...         "Initialize MyData from a dict's items"
...         return cls(datadict.items())
... 
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]

The reason it's neater is that there is no doubt about what type is expected, and you aren't forced to guess at what the caller intended for you to do with the datatype it gave you. The problem with isinstance(x, basestring) is that there is no way for the caller to tell you, for instance, that even though the type is not a basestring, you should treat it as a string (and not another sequence.) And perhaps the caller would like to use the same type for different purposes, sometimes as a single item, and sometimes as a sequence of items. Being explicit takes all doubt away and leads to more robust and clearer code.

Answered by: Lucas427 | Posted: 25-10-2021



Answer 2

Excellent question. I've tackled this problem as well, and while I agree that "factories" (class-method constructors) are a good method, I would like to suggest another, which I've also found very useful:

Here's a sample (this is a read method and not a constructor, but the idea is the same):

def read(self, str=None, filename=None, addr=0):
    """ Read binary data and return a store object. The data
        store is also saved in the interal 'data' attribute.

        The data can either be taken from a string (str 
        argument) or a file (provide a filename, which will 
        be read in binary mode). If both are provided, the str 
        will be used. If neither is provided, an ArgumentError 
        is raised.
    """
    if str is None:
        if filename is None:
            raise ArgumentError('Please supply a string or a filename')

        file = open(filename, 'rb')
        str = file.read()
        file.close()
    ...
    ... # rest of code

The key idea is here is using Python's excellent support for named arguments to implement this. Now, if I want to read the data from a file, I say:

obj.read(filename="blob.txt")

And to read it from a string, I say:

obj.read(str="\x34\x55")

This way the user has just a single method to call. Handling it inside, as you saw, is not overly complex

Answered by: Rubie172 | Posted: 25-10-2021



Answer 3

with python3, you can use Implementing Multiple Dispatch with Function Annotations as Python Cookbook wrote:

import time


class Date(metaclass=MultipleMeta):
    def __init__(self, year:int, month:int, day:int):
        self.year = year
        self.month = month
        self.day = day

    def __init__(self):
        t = time.localtime()
        self.__init__(t.tm_year, t.tm_mon, t.tm_mday)

and it works like:

>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018

Answered by: Stella222 | Posted: 25-10-2021



Answer 4

Quick and dirty fix

class MyData:
    def __init__(string=None,list=None):
        if string is not None:
            #do stuff
        elif list is not None:
            #do other stuff
        else:
            #make data empty

Then you can call it with

MyData(astring)
MyData(None, alist)
MyData()

Answered by: Daisy159 | Posted: 25-10-2021



Answer 5

A better way would be to use isinstance and type conversion. If I'm understanding you right, you want this:

def __init__ (self, filename):
    if isinstance (filename, basestring):
        # filename is a string
    else:
        # try to convert to a list
        self.path = list (filename)

Answered by: David793 | Posted: 25-10-2021



Answer 6

You should use isinstance

isinstance(...)
    isinstance(object, class-or-type-or-tuple) -> bool

    Return whether an object is an instance of a class or of a subclass thereof.
    With a type as second argument, return whether that is the object's type.
    The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
    isinstance(x, A) or isinstance(x, B) or ... (etc.).

Answered by: Dainton691 | Posted: 25-10-2021



Answer 7

You probably want the isinstance builtin function:

self.data = data if isinstance(data, list) else self.parse(data)

Answered by: Edgar279 | Posted: 25-10-2021



Answer 8

OK, great. I just tossed together this example with a tuple, not a filename, but that's easy. Thanks all.

class MyData:
    def __init__(self, data):
        self.myList = []
        if isinstance(data, tuple):
            for i in data:
                self.myList.append(i)
        else:
            self.myList = data

    def GetData(self):
        print self.myList

a = [1,2]

b = (2,3)

c = MyData(a)

d = MyData(b)

c.GetData()

d.GetData()

[1, 2]

[2, 3]

Answered by: Lana727 | Posted: 25-10-2021



Answer 9

Why don't you go even more pythonic?

class AutoList:
def __init__(self, inp):
    try:                        ## Assume an opened-file...
        self.data = inp.read()
    except AttributeError:
        try:                    ## Assume an existent filename...
            with open(inp, 'r') as fd:
                self.data = fd.read()
        except:
            self.data = inp     ## Who cares what that might be?

Answered by: William625 | Posted: 25-10-2021



Answer 10

My preferred solution is:

class MyClass:
    _data = []
    __init__(self,data=None):
        # do init stuff
        if not data: return
        self._data = list(data) # list() copies the list, instead of pointing to it.

Then invoke it with either MyClass() or MyClass([1,2,3]).

Hope that helps. Happy Coding!

Answered by: Carlos473 | Posted: 25-10-2021



Similar questions

python - How to overload the __init__ function of the Thread class in python3?

I am trying to port to python3 this answer. But I am not sure how to set theThread.__init__ function in python3. The deceleration of a python2 Thread.__init__ in a thread class is: Thread.__init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None) But python3 its: ...


C++ methods overload in python

suppose a C++ class has several constructors which are overloaded according the number and type and sequences of their respective parameters, for example, constructor(int x, int y) and constructor(float x, float y, float z), I think these two are overloaded methods, which one to use depends on the parameters, right? So then in python, how could I create a constructor that can work like this? I not...


python - Is there any trick to "overload the dot operator"?

I know the question is a little weirdly stated, but I can't think of any other way of saying it. I have an application that deals with large json objects, and I want to be able to just say: object1.value.size.whatever.attributexyz instead of object1.get('value').get('size').get('whatever').get('attributexyz') Is there some clever way to catch the At...


How can I overload in Python?

I'm trying to make a function that does different things when called on different argument types. Specifically, one of the functions should have the signature def myFunc(string, string): and the other should have the signature def myFunc(list): How can I do this, given that I'm not allowed to specify whether the arguments are strings or lists?


class - Overload int() in Python

Say I have a basic class in Python 3 which represents some number-like data-type. I want to make it so when I have an instance, x, of this class I can call int(x) and have it call my conversion function to return the integer portion. I'm sure this is simple, but I can't seem to find out how to do it.


python - How to globally overload assert so I don't have to change lib code in my PyQT app?

My app use QT for the gui layer, and many other lib I made. One of this other lib is quite complex (it's a type system) and full of asserts to make it as solid as possible. But when an assert is triggered in this lib, the Qt mainloop simply continue. I have a qt_debug() that works well (with pyqtRemoveInputHook) for the Qt part but nothing for the rest of python libraries. And, obviously I would a...


Overload () operator in Python

I am trying to learn currying in Python for my class and I have to overload the () operator for it. However, I do not understand how can I can go about overloading the () operator. Can you explain the logic behind overloading the parentheses? Should I overload first ( and then ) or can I do any of these? Also, is there special name for parentheses operator?


qt - PySide Signal cannot overload Python classes

PySide claims that Signals can be defined using the QtCore.Signal() class. Python types and C types can be passed as parameters to it. If you need to overload it just pass the types as tuples or lists. From PySide Doc Signals and Slots in PySide, they showed a way to create multiple signals at once. That line is:


Python - Large XML to JSON to File / RAM and Swap Overload

I'm currently working on creating a Pythonic way of parsing through OpenStreetMaps province/states dumps; which as far as I know is just knowing how to deal with very large XML files (right?). I'm currently using lxml etree iterparse module in order to parse through the


sockets - How can I overload a built-in module in python?

I am trying to bind hosts to specified ips in my python program. Just make it affect in the python program, so I am not going to modify the /etc/hosts file. I tried to add a bit code to the create_connection function in socket.py for host-ip translation, like this: host, port = address # the original code in socket.py # My change here: if host == "www.google.c...


python - Overload a Flask block?

Let's say I have a few levels of inheritance with my templates: index.html extends base_additional.html which extends base.html. In base.html, I have: <title>{% block title %}{% endblock %}</title> and in base_additional.html, I want to append some standard text, say " - My Site" to the end of whatever is in {% block title %} in index.html. How can I do this withou...






Still can't find your answer? Check out these communities...



PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python



top