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