如何重写 python3 __sub__ 函数以便不更改数据类型
How to override a python3 __sub__ function so the data type isn't changed
我正在尝试子class 日期时间 class 以便我的主要代码看起来更清晰。但是,对我的 subclass 进行任何算术运算都会将数据类型更改回 datetime.datetime.
我把我的原始代码缩减为一个最小的例子。
from datetime import datetime, timedelta
class worldtime(datetime):
UTC = True
tz_offset = timedelta(hours = 4)
def __new__(cls, *args, **kwargs):
#kwargs['tzinfo'] = dateutil.tz.tzutc()
return super().__new__(cls, *args, **kwargs)
def is_UTC(self):
return self.UTC
def to_local(self):
print(f"type(self): {type(self)}")
if self.UTC is True:
self = self - self.tz_offset
print(f"type(self): {type(self)}")
self.UTC = False
return self
dt = worldtime(2019, 8, 26, 12, 0, 0)
print (f"dt = {dt} is_UTC(): {dt.is_UTC()}")
print (f"type(dt): {type(dt)}")
print (f"dir(dt): {dir(dt)}")
dt = dt.to_local()
我减去 tz_offset timedelta 的那一刻,对象的类型变回 datetime.datetime:
dt = 2019-08-26 12:00:00 is_UTC(): True
type(dt): <class '__main__.worldtime'>
dir(dt): ['UTC', '__add__', '__class__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__radd__',
'__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__',
'__sizeof__', '__str__', '__sub__', '__subclasshook__', '__weakref__',
'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold',
'fromisoformat', 'fromordinal', 'fromtimestamp', 'hour', 'is_UTC',
'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min',
'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime',
'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'to_local',
'today', 'toordinal', 'tz_offset', 'tzinfo', 'tzname', 'utcfromtimestamp',
'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']
type(self): <class '__main__.worldtime'>
type(self): <class 'datetime.datetime'>
Traceback (most recent call last):
File "testwt.py", line 33, in <module>
dt.to_local()
File "testwt.py", line 27, in to_local
self.UTC = False
AttributeError: 'datetime.datetime' object has no attribute 'UTC'
我可以承认我是 python 中 subclass 的新手。虽然我看到其他帖子似乎在谈论这个问题,但没有可参考的例子。我见过的最好的是我必须覆盖 __sub__ 运算符,但我不确定如何做到这一点并确保 returned 对象是正确的类型。同样,没有任何清晰的代码示例可以使用...
更新:更正了示例代码中的一个小错误,因为 worldtime.to_local() 需要 return 将新实例返回到主代码。
重要的是这一行,在 to_local()
方法中:
self = self - self.tz_offset
您没有更改 self
(此 worldtime
对象)以使其现在代表本地时间,而是实际上将其设置为一个全新的对象,具体来说,是 [= 的结果23=].
那为什么那个结果不是 worldtime
对象呢?
请注意,此计算中的对象类型为 worldtime
- timedelta
。目前你还没有做任何事情来指定如何在你的 worldtime
class 上执行减法,所以 worldtime
自动从它的父 class 继承它的减法行为(datetime
).但这意味着它被当作一个普通的 datetime
对象对待(毕竟,它 是 实际上是一个 datetime
,只是有几个额外的属性和方法)。
所以Python进行datetime
-timedelta
计算,结果是一个datetime
对象,然后赋给self
。这就是为什么你的 worldtime
对象 似乎 'changing' 变成了 datetime
.
我们怎样才能让它发挥作用?
有两种选择:
1) 更新我们的对象而不是创建一个新对象
如果我们知道我们的偏移量总是几个小时,我们可以这样做:
def to_local(self):
if self.UTC is True:
self.hour = self.hour + self.tz_offset.hours
self.UTC = False
但是这个不会因为(与我最初的预期相反!):
tz_offset
没有 hours
属性(当您创建 timedelta
时,它会将时间存储为天、秒和微秒)
datetime
对象不允许您像这样直接设置 hour
我们可以尝试更改 _hour
属性(这是 datetime
在内部存储时间的方式),但像这样更改 'private' 属性通常不是一个好主意。另外,我们仍然需要将 tz_offset
转换回小时来进行计算,如果我们稍后想要偏移小时 和分钟 会怎样?我们需要确保我们的偏移量不会让我们跨越日期边界......(可能还有我们没有想到的其他问题!)
最好让datetime
做它擅长的事情,所以:
2a) 让 datetime
处理减法,但将结果转回 worldtime
def to_local(self):
if self.UTC is True:
new_time = self - self.tz_offset
self = worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
self.UTC = False
或者,正如您提到的,您可以定义 __sub__()
特殊方法来定义 -
运算符对我们的 worldtime
对象执行的操作。
2b) 用 __sub__()
覆盖 -
运算符
我们将 to_local()
保留为
def to_local(self):
if self.UTC is True:
self = self - self.tz_offset
self.UTC = False
但要改变 -
的行为方式。在这里,我们基本上将我们在 2a 中所做的移动到一个名为 __sub__()
的单独方法中(如 subtraction)。这意味着当 Python 命中 -
时,它将左右操作数传递给 __sub__()
特殊方法作为 self
和 other
(分别),并且然后 returns 方法的结果。
def __sub__(self, other):
new_time = self - other
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
但是当我们运行这个的时候,我们得到这样的错误:
RecursionError: maximum recursion depth exceeded
发生了什么事?
当 Python 命中 to_local()
中的 self
- self.tz_offset
时,它会调用 __sub__(self, self.tz_offset)
。到目前为止,一切都很好。但是当它在 __sub__()
内达到 self - other
时,我们仍在对 worldtime
对象进行减法,因此 Python 尽职尽责地再次调用 __sub__(self, other)
...一次又一次,陷入死循环!
我们不希望那样。相反,一旦我们进入 __sub__()
,我们只想进行正常的 datetime
减法。所以它应该是这样的:
def __sub__(self, other):
new_time = super().__sub__(other)
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
这里,super().__sub__(other)
意味着我们在父 class 上使用 __sub__()
方法。在这里,那是 datetime
,所以我们得到一个 datetime
对象,并可以从中创建一个新的 worldtime
对象。
整个事情(和你的打印语句)现在看起来像这样:
from datetime import datetime, timedelta
class worldtime(datetime):
UTC = True
tz_offset = timedelta(hours = -4)
def __new__(cls, *args, **kwargs):
#kwargs['tzinfo'] = dateutil.tz.tzutc()
return super().__new__(cls, *args, **kwargs)
def is_UTC(self):
return self.UTC
def to_local(self):
print(f"type(self): {type(self)}")
if self.UTC is True:
self = self - self.tz_offset
print(f"type(self): {type(self)}")
print(self)
self.UTC = False
def __sub__(self, other):
new_time = super().__sub__(other)
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
dt = worldtime(2019, 8, 26, 12, 0, 0)
print (f"dt = {dt} is_UTC(): {dt.is_UTC()}")
print (f"type(dt): {type(dt)}")
print (f"dir(dt): {dir(dt)}")
dt.to_local()
(我改为 4-space 标签,这是 Python 中的标准)
但是...这是最好的方法吗?
希望这已经回答了您关于在 Python 中使用 subclass 的问题。
但考虑到这个问题,我不确定这是否是最好的方法。 Subclassing 内置函数可能很复杂且容易出错,datetime
s 本身已经很复杂且容易出错。 Subclassing datetime
意义不大,因为在创建后更改它们并不简单,并且创建一个新对象并将其设置为 self
感觉不太整洁。
我想知道用组合代替继承会不会更好。所以 worldtime
会在内部存储一个 datetime
对象,你可以对其进行操作,并使用 datetime
模块中的时区支持来管理你的时区转换,也许只是在 -返回当地时间的飞翔。
类似于:
from datetime import datetime, timedelta, timezone
class WorldTime:
OFFSET = timedelta(hours=-4)
# assumes input time is in UTC, not local time
def __init__(self, year, month=None, day=None, hour=0, minute=0, second=0,
microsecond=0, tzinfo=timezone.utc, *, fold=0):
self.dt_in_utc = datetime(year, month, day, hour, minute, second,
microsecond, tzinfo, fold=fold)
# convert to our timezone, and then make naive ("local time")
def to_local(self):
return self.dt_in_utc.astimezone(timezone(self.OFFSET)).replace(tzinfo=None)
dt = WorldTime(2019, 8, 26, 12, 0, 0)
print(dt.to_local())
# Gives:
# 2019-08-26 08:00:00
我已经做到了 to_local()
returns 一个 datetime
对象,然后你可以打印出来,或者之后做任何你想做的事。
- 这个问题涵盖了类似的内容:
Convert a python UTC datetime to a local datetime using only python standard library?
- 这个参考很好:
https://howchoo.com/g/ywi5m2vkodk/working-with-datetime-objects-and-timezones-in-python
编辑
我进行了另一个从 datetime
继承的实验,我认为以下应该可行:
from datetime import datetime, timedelta, timezone
class WorldTime(datetime):
OFFSET = timedelta(hours=-4)
def __new__(cls, *args, tzinfo=timezone.utc, **kwargs):
return super().__new__(cls, *args, tzinfo=tzinfo, **kwargs)
def __add__(self, other):
result = super().__add__(other)
return WorldTime(*result.timetuple()[:6], tzinfo=result.tzinfo,
fold=result.fold)
def __sub__(self, other):
"Subtract two datetimes, or a datetime and a timedelta."
if not isinstance(other, datetime):
if isinstance(other, timedelta):
return self + -other
return NotImplemented
return super().__sub__(other)
def to_local(self):
return self.astimezone(timezone(self.OFFSET)).replace(tzinfo=None)
dt = WorldTime(2019, 8, 26, 12, 0, 0)
print(dt)
print(dt.to_local()) # local time
print(dt + timedelta(days=20, hours=7)) # 20 days, 7 hours in the future
print(dt - timedelta(days=40, hours=16)) # 40 days, 16 hours in the past
print(dt - WorldTime(2018, 12, 25, 15, 0, 0)) # time since 3pm last Christmas Day
# Output:
# 2019-08-26 12:00:00+00:00 # WorldTime
# 2019-08-26 08:00:00 # datetime
# 2019-09-15 19:00:00+00:00 # WorldTime
# 2019-07-16 20:00:00+00:00 # WorldTime
# 243 days, 21:00:00 # timedelta
所以看起来像timedelta
s returns一个WorldTime
对象的加减,我们可以找到两个WorldTime
对象之间的差异作为一个timedelta
.
然而,这并未经过严格测试,因此请谨慎行事!
datetime
减去(子)classes 的结果总是 return 一个 datetime
实例。查看 datetime
模块中 __add__(self, other)
的实现时,这一点变得很明显(因为 __sub__(self, other)
本质上只是在从中减去 timedelta
实例时将计算转发给加法函数一个 datetime
个实例):
class datetime(date):
...
def __sub__(self, other):
"Subtract two datetimes, or a datetime and a timedelta."
if not isinstance(other, datetime):
if isinstance(other, timedelta): # This is True in our case
return self + -other # This is calling the __add__ function
return NotImplemented
# The remainder of the __sub__ function is omitted as we are
# focussing on the case in which a timedelta instance is subtracted
# from a datetime instance.
def __add__(self, other):
"Add a datetime and a timedelta."
if not isinstance(other, timedelta):
return NotImplemented
delta = timedelta(self.toordinal(),
hours=self._hour,
minutes=self._minute,
seconds=self._second,
microseconds=self._microsecond)
delta += other
hour, rem = divmod(delta.seconds, 3600)
minute, second = divmod(rem, 60)
if 0 < delta.days <= _MAXORDINAL:
return type(self).combine(date.fromordinal(delta.days),
time(hour, minute, second,
delta.microseconds,
tzinfo=self._tzinfo))
raise OverflowError("result out of range")
这里的关键是_add__
函数创建一个新的timedelta
实例,然后使用.combine()
函数创建一个新的输出。
我将向您展示两个有关如何解决此行为的示例:
覆盖class方法combine(cps, date, time, tzinfo=True)
:
class worldtime
...
@classmethod
def combine(cls, date, time, tzinfo=True):
"Construct a datetime from a given date and a given time."
if not isinstance(date, _date_class):
raise TypeError("date argument must be a date instance")
if not isinstance(time, _time_class):
raise TypeError("time argument must be a time instance")
if tzinfo is True:
tzinfo = time.tzinfo
return cls(date.year, date.month, date.day,
time.hour, time.minute, time.second, time.microsecond,
tzinfo, fold=time.fold)
现在应该调用 worldtime
的构造函数,而不是 class datetime
和 return 的父对象 worldtime
的对象。由于 combine
函数是从许多现有的魔术方法中调用的,因此它应该有望涵盖其他情况(和算术运算)。
覆盖__sub__(self, other)
方法:
class worldtime:
...
def __sub__(self, other):
# the subtraction will turn sub into an instance of datetime
# as we‘re calling the original subtraction function of datetime
sub = super(worldtime, self).__sub__(other)
# timetuple returns the parameters (year, month, day, etc.)
# and we need the first six parameters only to create a new instance.
return worldtime(*sub.timetuple()[:6])
这将使用其构造函数将 self
和 other
(已变为 datetime
)之间的差异转换回 worldtime
的实例。
第一个选项可能更简洁,因为它将应用于 datetime
的所有算术函数。 第二个选项需要您添加更多特殊情况到其他算术运算,并可能导致更大的实施和维护工作。
我正在尝试子class 日期时间 class 以便我的主要代码看起来更清晰。但是,对我的 subclass 进行任何算术运算都会将数据类型更改回 datetime.datetime.
我把我的原始代码缩减为一个最小的例子。
from datetime import datetime, timedelta
class worldtime(datetime):
UTC = True
tz_offset = timedelta(hours = 4)
def __new__(cls, *args, **kwargs):
#kwargs['tzinfo'] = dateutil.tz.tzutc()
return super().__new__(cls, *args, **kwargs)
def is_UTC(self):
return self.UTC
def to_local(self):
print(f"type(self): {type(self)}")
if self.UTC is True:
self = self - self.tz_offset
print(f"type(self): {type(self)}")
self.UTC = False
return self
dt = worldtime(2019, 8, 26, 12, 0, 0)
print (f"dt = {dt} is_UTC(): {dt.is_UTC()}")
print (f"type(dt): {type(dt)}")
print (f"dir(dt): {dir(dt)}")
dt = dt.to_local()
我减去 tz_offset timedelta 的那一刻,对象的类型变回 datetime.datetime:
dt = 2019-08-26 12:00:00 is_UTC(): True
type(dt): <class '__main__.worldtime'>
dir(dt): ['UTC', '__add__', '__class__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__radd__',
'__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__',
'__sizeof__', '__str__', '__sub__', '__subclasshook__', '__weakref__',
'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold',
'fromisoformat', 'fromordinal', 'fromtimestamp', 'hour', 'is_UTC',
'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min',
'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime',
'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'to_local',
'today', 'toordinal', 'tz_offset', 'tzinfo', 'tzname', 'utcfromtimestamp',
'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']
type(self): <class '__main__.worldtime'>
type(self): <class 'datetime.datetime'>
Traceback (most recent call last):
File "testwt.py", line 33, in <module>
dt.to_local()
File "testwt.py", line 27, in to_local
self.UTC = False
AttributeError: 'datetime.datetime' object has no attribute 'UTC'
我可以承认我是 python 中 subclass 的新手。虽然我看到其他帖子似乎在谈论这个问题,但没有可参考的例子。我见过的最好的是我必须覆盖 __sub__ 运算符,但我不确定如何做到这一点并确保 returned 对象是正确的类型。同样,没有任何清晰的代码示例可以使用...
更新:更正了示例代码中的一个小错误,因为 worldtime.to_local() 需要 return 将新实例返回到主代码。
重要的是这一行,在 to_local()
方法中:
self = self - self.tz_offset
您没有更改 self
(此 worldtime
对象)以使其现在代表本地时间,而是实际上将其设置为一个全新的对象,具体来说,是 [= 的结果23=].
那为什么那个结果不是 worldtime
对象呢?
请注意,此计算中的对象类型为 worldtime
- timedelta
。目前你还没有做任何事情来指定如何在你的 worldtime
class 上执行减法,所以 worldtime
自动从它的父 class 继承它的减法行为(datetime
).但这意味着它被当作一个普通的 datetime
对象对待(毕竟,它 是 实际上是一个 datetime
,只是有几个额外的属性和方法)。
所以Python进行datetime
-timedelta
计算,结果是一个datetime
对象,然后赋给self
。这就是为什么你的 worldtime
对象 似乎 'changing' 变成了 datetime
.
我们怎样才能让它发挥作用?
有两种选择:
1) 更新我们的对象而不是创建一个新对象
如果我们知道我们的偏移量总是几个小时,我们可以这样做:
def to_local(self):
if self.UTC is True:
self.hour = self.hour + self.tz_offset.hours
self.UTC = False
但是这个不会因为(与我最初的预期相反!):
tz_offset
没有hours
属性(当您创建timedelta
时,它会将时间存储为天、秒和微秒)datetime
对象不允许您像这样直接设置hour
我们可以尝试更改 _hour
属性(这是 datetime
在内部存储时间的方式),但像这样更改 'private' 属性通常不是一个好主意。另外,我们仍然需要将 tz_offset
转换回小时来进行计算,如果我们稍后想要偏移小时 和分钟 会怎样?我们需要确保我们的偏移量不会让我们跨越日期边界......(可能还有我们没有想到的其他问题!)
最好让datetime
做它擅长的事情,所以:
2a) 让 datetime
处理减法,但将结果转回 worldtime
def to_local(self):
if self.UTC is True:
new_time = self - self.tz_offset
self = worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
self.UTC = False
或者,正如您提到的,您可以定义 __sub__()
特殊方法来定义 -
运算符对我们的 worldtime
对象执行的操作。
2b) 用 __sub__()
-
运算符
我们将 to_local()
保留为
def to_local(self):
if self.UTC is True:
self = self - self.tz_offset
self.UTC = False
但要改变 -
的行为方式。在这里,我们基本上将我们在 2a 中所做的移动到一个名为 __sub__()
的单独方法中(如 subtraction)。这意味着当 Python 命中 -
时,它将左右操作数传递给 __sub__()
特殊方法作为 self
和 other
(分别),并且然后 returns 方法的结果。
def __sub__(self, other):
new_time = self - other
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
但是当我们运行这个的时候,我们得到这样的错误:
RecursionError: maximum recursion depth exceeded
发生了什么事?
当 Python 命中 to_local()
中的 self
- self.tz_offset
时,它会调用 __sub__(self, self.tz_offset)
。到目前为止,一切都很好。但是当它在 __sub__()
内达到 self - other
时,我们仍在对 worldtime
对象进行减法,因此 Python 尽职尽责地再次调用 __sub__(self, other)
...一次又一次,陷入死循环!
我们不希望那样。相反,一旦我们进入 __sub__()
,我们只想进行正常的 datetime
减法。所以它应该是这样的:
def __sub__(self, other):
new_time = super().__sub__(other)
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
这里,super().__sub__(other)
意味着我们在父 class 上使用 __sub__()
方法。在这里,那是 datetime
,所以我们得到一个 datetime
对象,并可以从中创建一个新的 worldtime
对象。
整个事情(和你的打印语句)现在看起来像这样:
from datetime import datetime, timedelta
class worldtime(datetime):
UTC = True
tz_offset = timedelta(hours = -4)
def __new__(cls, *args, **kwargs):
#kwargs['tzinfo'] = dateutil.tz.tzutc()
return super().__new__(cls, *args, **kwargs)
def is_UTC(self):
return self.UTC
def to_local(self):
print(f"type(self): {type(self)}")
if self.UTC is True:
self = self - self.tz_offset
print(f"type(self): {type(self)}")
print(self)
self.UTC = False
def __sub__(self, other):
new_time = super().__sub__(other)
return worldtime(
new_time.year,
new_time.month,
new_time.day,
new_time.hour,
new_time.minute,
new_time.second,
)
dt = worldtime(2019, 8, 26, 12, 0, 0)
print (f"dt = {dt} is_UTC(): {dt.is_UTC()}")
print (f"type(dt): {type(dt)}")
print (f"dir(dt): {dir(dt)}")
dt.to_local()
(我改为 4-space 标签,这是 Python 中的标准)
但是...这是最好的方法吗?
希望这已经回答了您关于在 Python 中使用 subclass 的问题。
但考虑到这个问题,我不确定这是否是最好的方法。 Subclassing 内置函数可能很复杂且容易出错,datetime
s 本身已经很复杂且容易出错。 Subclassing datetime
意义不大,因为在创建后更改它们并不简单,并且创建一个新对象并将其设置为 self
感觉不太整洁。
我想知道用组合代替继承会不会更好。所以 worldtime
会在内部存储一个 datetime
对象,你可以对其进行操作,并使用 datetime
模块中的时区支持来管理你的时区转换,也许只是在 -返回当地时间的飞翔。
类似于:
from datetime import datetime, timedelta, timezone
class WorldTime:
OFFSET = timedelta(hours=-4)
# assumes input time is in UTC, not local time
def __init__(self, year, month=None, day=None, hour=0, minute=0, second=0,
microsecond=0, tzinfo=timezone.utc, *, fold=0):
self.dt_in_utc = datetime(year, month, day, hour, minute, second,
microsecond, tzinfo, fold=fold)
# convert to our timezone, and then make naive ("local time")
def to_local(self):
return self.dt_in_utc.astimezone(timezone(self.OFFSET)).replace(tzinfo=None)
dt = WorldTime(2019, 8, 26, 12, 0, 0)
print(dt.to_local())
# Gives:
# 2019-08-26 08:00:00
我已经做到了 to_local()
returns 一个 datetime
对象,然后你可以打印出来,或者之后做任何你想做的事。
- 这个问题涵盖了类似的内容: Convert a python UTC datetime to a local datetime using only python standard library?
- 这个参考很好: https://howchoo.com/g/ywi5m2vkodk/working-with-datetime-objects-and-timezones-in-python
编辑
我进行了另一个从 datetime
继承的实验,我认为以下应该可行:
from datetime import datetime, timedelta, timezone
class WorldTime(datetime):
OFFSET = timedelta(hours=-4)
def __new__(cls, *args, tzinfo=timezone.utc, **kwargs):
return super().__new__(cls, *args, tzinfo=tzinfo, **kwargs)
def __add__(self, other):
result = super().__add__(other)
return WorldTime(*result.timetuple()[:6], tzinfo=result.tzinfo,
fold=result.fold)
def __sub__(self, other):
"Subtract two datetimes, or a datetime and a timedelta."
if not isinstance(other, datetime):
if isinstance(other, timedelta):
return self + -other
return NotImplemented
return super().__sub__(other)
def to_local(self):
return self.astimezone(timezone(self.OFFSET)).replace(tzinfo=None)
dt = WorldTime(2019, 8, 26, 12, 0, 0)
print(dt)
print(dt.to_local()) # local time
print(dt + timedelta(days=20, hours=7)) # 20 days, 7 hours in the future
print(dt - timedelta(days=40, hours=16)) # 40 days, 16 hours in the past
print(dt - WorldTime(2018, 12, 25, 15, 0, 0)) # time since 3pm last Christmas Day
# Output:
# 2019-08-26 12:00:00+00:00 # WorldTime
# 2019-08-26 08:00:00 # datetime
# 2019-09-15 19:00:00+00:00 # WorldTime
# 2019-07-16 20:00:00+00:00 # WorldTime
# 243 days, 21:00:00 # timedelta
所以看起来像timedelta
s returns一个WorldTime
对象的加减,我们可以找到两个WorldTime
对象之间的差异作为一个timedelta
.
然而,这并未经过严格测试,因此请谨慎行事!
datetime
减去(子)classes 的结果总是 return 一个 datetime
实例。查看 datetime
模块中 __add__(self, other)
的实现时,这一点变得很明显(因为 __sub__(self, other)
本质上只是在从中减去 timedelta
实例时将计算转发给加法函数一个 datetime
个实例):
class datetime(date):
...
def __sub__(self, other):
"Subtract two datetimes, or a datetime and a timedelta."
if not isinstance(other, datetime):
if isinstance(other, timedelta): # This is True in our case
return self + -other # This is calling the __add__ function
return NotImplemented
# The remainder of the __sub__ function is omitted as we are
# focussing on the case in which a timedelta instance is subtracted
# from a datetime instance.
def __add__(self, other):
"Add a datetime and a timedelta."
if not isinstance(other, timedelta):
return NotImplemented
delta = timedelta(self.toordinal(),
hours=self._hour,
minutes=self._minute,
seconds=self._second,
microseconds=self._microsecond)
delta += other
hour, rem = divmod(delta.seconds, 3600)
minute, second = divmod(rem, 60)
if 0 < delta.days <= _MAXORDINAL:
return type(self).combine(date.fromordinal(delta.days),
time(hour, minute, second,
delta.microseconds,
tzinfo=self._tzinfo))
raise OverflowError("result out of range")
这里的关键是_add__
函数创建一个新的timedelta
实例,然后使用.combine()
函数创建一个新的输出。
我将向您展示两个有关如何解决此行为的示例:
覆盖class方法
combine(cps, date, time, tzinfo=True)
:class worldtime ... @classmethod def combine(cls, date, time, tzinfo=True): "Construct a datetime from a given date and a given time." if not isinstance(date, _date_class): raise TypeError("date argument must be a date instance") if not isinstance(time, _time_class): raise TypeError("time argument must be a time instance") if tzinfo is True: tzinfo = time.tzinfo return cls(date.year, date.month, date.day, time.hour, time.minute, time.second, time.microsecond, tzinfo, fold=time.fold)
现在应该调用
worldtime
的构造函数,而不是 classdatetime
和 return 的父对象worldtime
的对象。由于combine
函数是从许多现有的魔术方法中调用的,因此它应该有望涵盖其他情况(和算术运算)。覆盖
__sub__(self, other)
方法:class worldtime: ... def __sub__(self, other): # the subtraction will turn sub into an instance of datetime # as we‘re calling the original subtraction function of datetime sub = super(worldtime, self).__sub__(other) # timetuple returns the parameters (year, month, day, etc.) # and we need the first six parameters only to create a new instance. return worldtime(*sub.timetuple()[:6])
这将使用其构造函数将
self
和other
(已变为datetime
)之间的差异转换回worldtime
的实例。
第一个选项可能更简洁,因为它将应用于 datetime
的所有算术函数。 第二个选项需要您添加更多特殊情况到其他算术运算,并可能导致更大的实施和维护工作。