Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy constructor in python

Tags:

python

I want to know if it possible to do a copy constructor in python like in java, this is my java code, regular contractor and a copy constructor. how the below code can be written in python? thx

public Date(int day, int month, int year)
{
    _day = day;
    _month = month;
    _year = year;

    if(!checkDate(_day,_month,_year))
    {
        _day = DEFAULT_DAY;
        _month = DEFAULT_DAY;
        _year = DEFAULT_YEAR;
    }        
}

/**
 * Copy constructor.
 */
public Date (Date other)
{
    if(other != null)
    {
      _day = other._day;
      _month = other._month;
      _year = other._year;
    }
}
like image 702
Barak Michaeli Avatar asked Oct 17 '25 18:10

Barak Michaeli


1 Answers

There's no overloading in Python, so you can't "overload the constructor" for the simple reason that you can't overload anything.

There are two general ways to "simulate overloading" in Python, and both of them apply to overloaded constructors:1

  • Write a single method that's flexible enough to take either set of parameters.
  • Write two methods with different names.

The first one is pretty simple:

def __init__(self, date_or_day, month=None, year=None):
    if isinstance(date_or_day, Date):
        # do "copy" stuff
    else:
        # do "non-copy" stuff

… or, a minor variation:

def __init__(self, day=None, month=None, year=None, *, date=None):
    if month is None:
        # do "copy" stuff
    else:
        # do "non-copy" stuff

This works, but it can be a little clunky. (Think of the interface to range, and how hard it is to write down clearly even after you know it.) And either way, you probably want to throw in some checks to raise TypeError("some message") if the user does something nonsensical, like calling it with a date and a month.

If you prefer to require keyword arguments:

def __init__(self, day=None, month=None, year=None, *, date=None):
    if date is not None:
        # do "copy" stuff, maybe also assert that month and year are None
    else:
        # do "non-copy" stuff

… then the constructor can't be called as just Date(otherdate) anymore, but it can be called as Date(date=otherdate), which may be clearer anyway, and you can avoid all the fiddly stuff with the first parameter having potentially two different meanings.


The second one may seem impossible at first, because the constructor has to be called as Date(…).

But you can use the "alternate constructor" idiom, by defining a classmethod that can be called explicitly as Date.from_somethingelse(…). This idiom is, in fact, heavily used in the types from the stdlib's datetime library—e.g., datetime.datetime.now() is an alternate constructor that creates a datetime object representing right now.

So, for example:

def __init__(self, date=None):
    if date is None:
        # do "default constructor" stuff
    else:
        # do "copy" stuff

@classmethod
def from_dmy(cls, day, month, year):
    self = cls()
    # do "non-copy" stuff

One last point: copy constructors are rare in idiomatic Python, because they're really not as useful as in languages like Java.

First, there's nowhere for them to get applied implicitly—assignment isn't a copy, and even if it were, variables don't have types, values do, so there'd be no "hook" for you to specify what constructor you wanted. And if you want to explicitly copy something, you usually either add a copy method (as with list and dict) or just use the copy module.

What is somewhat common is a constructor that takes some wider range of things that includes your own type. For example, lists can be constructed from any iterable, including another list, and dicts can be constructed from any iterable of pairs or any mapping, including another dict.


1. Actually, in Python, __init__ methods aren't constructors, but initializers—they don't return an object, they get called on an already-constructed object and initialize it. If you actually need to write a constructor (typically for immutable objects, that you can't initialize after creation), that's __new__.

like image 161
abarnert Avatar answered Oct 20 '25 08:10

abarnert