(original) (raw)

I personally don't think this is a big enough issue to warrant any changes, but I think Serhiy's solution would be the ideal best with one additional parameter: the caller's type. Something like

def \_\_make\_me\_\_(self, cls, \*args, \*\*kwargs)

and the idea is that any time you want to construct a type, instead of

self.\_\_class\_\_(assumed arguments…)

where you are not sure that the derived class' constructor knows the right argument types, you do

def SomeCls:
def some\_method(self, ...):
return self.\_\_make\_me\_\_(SomeCls, assumed arguments…)

Now the derived class knows who is asking for a copy. In the case of defaultdict, for example, he can implement \_\_make\_me\_\_ as follows:

def \_\_make\_me\_\_(self, cls, \*args, \*\*kwargs):
if cls is dict: return default\_dict(self.default\_factory, \*args, \*\*kwargs)
return default\_dict(\*args, \*\*kwargs)

essentially the caller is identifying himself so that the receiver knows how to interpret the arguments.

Best,

Neil

On Fri, Feb 13, 2015 at 5:55 PM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:


Let me humbly conjecture that the people who wrote the top answers have background in less capable languages than Python.

Not every language allows you to call self.\_\_class\_\_(). In the languages that don't you can get away with incompatible constructor signatures.

However, let me try to focus the discussion on a specific issue before we go deep into OOP theory.

With python's standard datetime.date we have:

>>> from datetime import \*
>>> class Date(date):
... pass
...
>>> Date.today()
Date(2015, 2, 13)
>>> Date.fromordinal(1)
Date(1, 1, 1)

Both .today() and .fromordinal(1) will break in a subclass that redefines \_\_new\_\_ as follows:

>>> class Date2(date):
... def \_\_new\_\_(cls, ymd):
... return date.\_\_new\_\_(cls, \*ymd)
...
>>> Date2.today()
Traceback (most recent call last):
File "", line 1, in
TypeError: \_\_new\_\_() takes 2 positional arguments but 4 were given
>>> Date2.fromordinal(1)
Traceback (most recent call last):
File "", line 1, in
TypeError: \_\_new\_\_() takes 2 positional arguments but 4 were given

Why is this acceptable, but we have to sacrifice the convenience of having Date + timedelta
return Date to make it work with Date2:

>>> Date2((1,1,1)) + timedelta(1)
datetime.date(1, 1, 2)