3
from datetime import timedelta

class A:
    def __abs__(self):
        return -self

class B1(A):
    def __neg__(self):
        return 'neg from B1'

class B2(timedelta):
    def __neg__(self):
        return 'neg from B2'

print(abs(B1()))     # neg from B1
print(abs(B2(-1)))   # 1 day, 0:00:00

Why does the first print call use the overridden method, but the second one doesn't? I don't understand. The second case appears to also call -self in the python implementation here.

wim
  • 338,267
  • 99
  • 616
  • 750

1 Answers1

2

I'm sure I am missing something here, but there is no reason for B2 to ever call __neg__. The timedelta baseclass certainly won't use it.

B1().__abs__() uses -self, which triggers the self.__neg__() call, but B2 has no such operator applied to it.

Note that the datetime.py Python implementation isn't involved here; that code is there for systems that cannot, for some reason, run the C implementation of the same:

static PyObject *
delta_abs(PyDateTime_Delta *self)
{
    PyObject *result;

    assert(GET_TD_MICROSECONDS(self) >= 0);
    assert(GET_TD_SECONDS(self) >= 0);

    if (GET_TD_DAYS(self) < 0)
        result = delta_negative(self);
    else
        result = delta_positive(self);

    return result;
}

where delta_negative is the native implementation for the __neg__ hook; the code never considers subclasses here.

wim
  • 338,267
  • 99
  • 616
  • 750
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 2
    I think the presumption is that `timedelta` will require `__neg__` to convert the negative input to positive. But it seems likely that it negates the constituent parts instead. – Mark Ransom May 26 '15 at 16:20
  • Can I trick my system into using the python implementation instead of the C implementation? – wim May 26 '15 at 16:25
  • 1
    @wim: It'd take re-running the module without the last few lines. Load the `datetime.py` file into a string, remove the lines from `try: import _datetime` onwards, use [this post](http://stackoverflow.com/questions/5362771/load-module-from-string-in-python) to create a replacement module object and stuff that into `sys.modules['datetime']`. Super hacky. – Martijn Pieters May 26 '15 at 16:28
  • You are right. I tried your "hack" and saw the output that I was initially expecting (`neg from B1`;`neg from B2`) – wim May 26 '15 at 16:53