How to Overload _Init_ Method Based on Argument Type

How to overload __init__ method based on argument type?

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.

Python overload __init__

In C++ you can define multiple functions/methods with the same name and different signatures. In Python, everytime you define a new function with the same name of a previously defined function, you are replacing it.

To achieve what you want, you have to use optional arguments and/or explicit checks.

Here are a few possible solutions:

class Handle:
def __init__(self, pid=-1):
if isinstance(pid, str):
self.procname = pid
self.pid = -1
else:
self.procname = None
self.pid = pid

class Handle:
# Both pid and procname may be specified at the same time.
def __init__(self, pid=-1, procname=None):
self.procname = procname
self.pid = pid

class Handle:
# Either pid or procname, not both, may be specified.
def __init__(self, pid=-1, procname=None):
if pid >= 0 and procname is not None:
raise ValueError('you can specify either pid or procname, not both')
self.procname = procname
self.pid = pid

class Handle:
def __init__(self, pid=-1):
self.pid = pid

# A separate constructor, with a different name,
# because "explicit is better than implicit" and
# "sparse is better than dense" (cit. The Zen of
# Python).
# In my opinion, this is the most Pythonic solution.
@classmethod
def from_process_name(cls, procname):
pid = get_pid(procname)
return cls(pid)

By the way, I would not recommend using -1 for "not specified". I'd rather use None.

Python: How can I make the __Init__ call for two different method of argument?

You can define the init as normal, for example:

class candy(object):
def __init__(self, name, type):
self.name = name
self.type = type

and then pass arguments in both ways:

candy(name='name', type='type')

or

candy(**{ 'name': 'name', 'type': 'type' })

What is a clean pythonic way to implement multiple constructors?

Actually None is much better for "magic" values:

class Cheese():
def __init__(self, num_holes = None):
if num_holes is None:
...

Now if you want complete freedom of adding more parameters:

class Cheese():
def __init__(self, *args, **kwargs):
#args -- tuple of anonymous arguments
#kwargs -- dictionary of named arguments
self.num_holes = kwargs.get('num_holes',random_holes())

To better explain the concept of *args and **kwargs (you can actually change these names):

def f(*args, **kwargs):
print 'args: ', args, ' kwargs: ', kwargs

>>> f('a')
args: ('a',) kwargs: {}
>>> f(ar='a')
args: () kwargs: {'ar': 'a'}
>>> f(1,2,param=3)
args: (1, 2) kwargs: {'param': 3}

http://docs.python.org/reference/expressions.html#calls

Inheritance and Overriding __init__ in python

The book is a bit dated with respect to subclass-superclass calling. It's also a little dated with respect to subclassing built-in classes.

It looks like this nowadays:

class FileInfo(dict):
"""store file metadata"""
def __init__(self, filename=None):
super(FileInfo, self).__init__()
self["name"] = filename

Note the following:

  1. We can directly subclass built-in classes, like dict, list, tuple, etc.

  2. The super function handles tracking down this class's superclasses and calling functions in them appropriately.

Overloading __init__() method results in error

You cannot have two __init__ methods in a single class.

What your code effectively does is override the first method so it is never used, then you get an error because you haven't supplied enough arguments.

One way to get around this would be to supply default values using keyword-arguments. In this way if you create the Message object with no values, it'll use the defaults. The example below uses None as the default value but it could be something more complex:

class Message(object):

def __init__(self, type=None, length=None, data=None):
self.type = type
self.length = length
self.data = data

Python checking __init__ parameter

You have to define __new__ for that:

class Foo(object):
def __new__(cls, arg):
if arg > 10: #error!
return None
return super(Foo, cls).__new__(cls)

print Foo(1) # <__main__.Foo object at 0x10c903410>
print Foo(100) # None

That said, using __init__ and raising an exception on invalid args is generally much better:

class Foo(object):
def __init__(self, arg):
if arg > 10: #error!
raise ValueError("invalid argument!")
# do stuff

How to call Base Class's __init__ method from the child class?

You could use super(ChildClass, self).__init__()

class BaseClass(object):
def __init__(self, *args, **kwargs):
pass

class ChildClass(BaseClass):
def __init__(self, *args, **kwargs):
super(ChildClass, self).__init__(*args, **kwargs)

Your indentation is incorrect, here's the modified code:

class Car(object):
condition = "new"

def __init__(self, model, color, mpg):
self.model = model
self.color = color
self.mpg = mpg

class ElectricCar(Car):
def __init__(self, battery_type, model, color, mpg):
self.battery_type=battery_type
super(ElectricCar, self).__init__(model, color, mpg)

car = ElectricCar('battery', 'ford', 'golden', 10)
print car.__dict__

Here's the output:

{'color': 'golden', 'mpg': 10, 'model': 'ford', 'battery_type': 'battery'}

More arguments in derived class __init__ than base class __init__

A flexible approach is to have every method in the ancestor tree cooperatively designed to accept keyword arguments and a keyword-arguments dictionary, to remove any arguments that it needs, and to forward the remaining arguments using **kwds, eventually leaving the dictionary empty for the final call in the chain.

Each level strips-off the keyword arguments that it needs so that the final empty dict can be sent to a method that expects no arguments at all (for example, object.__init__ expects zero arguments):

class Shape:
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)

class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)

cs = ColoredShape(color='red', shapename='circle')

For more on this approach, see the "Practical Advice" section of the Super Considered Super blogpost, or see the related at Pycon video.

In your example, the code would look like this:

class Ipsum:
""" A base ipsum instance. """

def __init__(self, foo, bar):
self.foo = flonk(foo)
grungole(self, bar)
self._baz = make_the_baz()

class LoremIpsum(Ipsum):
""" A more refined ipsum that also lorems. """

def __init__(self, dolor, sit, amet, **kwds):
super().__init__(**kwds)
farnark(sit, self, amet)
self.wibble(dolor)

An instantiation would look like this:

li = LoremIpsum(dolor=1, sit=2, amet=3, foo=4, bar=5) 

Note, this lets you achieve your original objective of adding new arguments to either __init__ method without affecting the other.

python __init__ method in inherited class

As far as I know that's not possible, however you can call the init method of the superclass, like this:

class inheritedclass(initialclass):
def __init__(self):
initialclass.__init__(self)
self.attr3 = 'three'


Related Topics



Leave a reply



Submit