for convenience I want to create subclasses of datetime.timedelta. The idea is to define a class as such:
class Hours(datetime.timedelta):
def __init__(self, hours):
super(Hours, self).__init__(hours=hours)
so I can quickly create timedeltas like that:
x = Hours(n)
However, the code above produces a timedelta of n days instead of n hours. As an example, look at the following ipython session:
In [1]: import datetime
In [2]: class Hours(datetime.timedelta):
...: def __init__(self, hours):
...: super(Hours, self).__init__(hours=hours)
...:
In [3]: print(Hours(10))
Out[3]: 10 days, 0:00:00
I'm not able to explain this. Is anybody?
If you use __new__
, instead of __init__
:
import datetime as DT
class Hours(DT.timedelta):
def __new__(self, hours):
return DT.timedelta.__new__(self, hours=hours)
x = Hours(10)
print(x)
yields
10:00:00
If you override __init__
, but not __new__
, then DT.timedelta.__new__
gets called before your Hours.__init__
. Notice
import datetime as DT
class Hours(DT.timedelta):
def __init__(self, hours):
print(self)
x = Hours(10)
prints 10 days, 0:00:00
. This shows that DT.timedelta.__new__
has already set the timedelta to 10 days before you even get a chance to configure it in Hours.__init__
.
Moreover, DT.timedelta
is an immutable object -- you can't change the days
or seconds
or microseconds
after the object has been instantiated. Python creates immutable objects by using the __new__
method, and generally don't do anything in the __init__
method. Mutable objects do the reverse: they configure the object in __init__
and don't do anything in __new__
.
Per the docs:
When subclassing immutable built-in types like numbers and strings, and occasionally in other situations, the static method
__new__
comes in handy.__new__
is the first step in instance construction, invoked before__init__
. The__new__
method is called with the class as its first argument; its responsibility is to return a new instance of that class. Compare this to__init__
:__init__
is called with an instance as its first argument, and it doesn't return anything; its responsibility is to initialize the instance....All this is done so that immutable types can preserve their immutability while allowing subclassing.
(If immutable objects performed configuration in __init__
, then you could mutate an immutable by calling immutable.__init__
. Obviously, we don't want that, so immutable.__init__
generally does nothing.)
Note also that unless you plan to add new methods to your Hours
class, it would be simpler, and therefore better to just use a function:
def hours(hours):
return DT.timedelta(hours=hours)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With