3

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;
    }
}
  • I don't think this is a dup, despite the (slightly misleading) title. The OP is asking how to create an "overloaded constructor"; the fact that one of the two overloads happens to be a copy constructor is only secondary. – abarnert May 04 '18 at 20:35
  • 1
    Anyway, you can't overload constructors in Python for the simple reason that you can't overload _anything_ in Python. The way you translate overloading to Python is (1) write a function with a signature that's flexible enough to take both sets of args, or (2) write functions with different names. If you're thinking (2) can't possibly work for constructors, you're looking for the "alternate constructor idiom": define a `@classmethod` as the alternate constructor, and call it explicitly, as with `datetime.datetime.now()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.now). – abarnert May 04 '18 at 20:38
  • Perhaps you don't need any class specific code: Just use `copy.copy` (or maybe `copy.deepcopy` if you need it) wherever you want to make the copy of an object. – Blckknght May 04 '18 at 20:44
  • Also, the most obvious design for a date class is to make it immutable (see `datetime.Date`, and even the clunky C-friendly types in `time` and `cal`), at which point you shouldn't _ever_ need to make copies, because it never matters whether you have two equal dates or the exact same date twice. – abarnert May 04 '18 at 21:08

1 Answers1

8

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__.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • thanks, I guess Ill drop the copy constructor for now, but if u can look on another issue it will be great – Barak Michaeli May 04 '18 at 20:58
  • @BarakMichaeli If you have another issue, create another question. If they're closely related, you can use the "share" button to get a link for each question and paste it into the other question, so you don't have to exhaustively repeat all of the background details. – abarnert May 04 '18 at 21:07