重要的一行是这一行,在to_local()方法中:
self = self - self.tz_offset
而不是更改self(this worldtimeobject) 以使其现在代表本地时间,您实际上是将其设置为一个全新的对象,特别是self - self.tz_offset.
那么为什么那个结果不是一个worldtime对象呢?
请注意,此计算中的对象类型是worldtime- timedelta。目前你还没有做任何事情来指定如何对你的worldtime类执行减法,所以自动从它的父类( )worldtime继承它的减法行为。datetime但这意味着它被视为普通datetime对象(毕竟,它实际上是一个datetime,只是带有几个额外的属性和方法)。
所以 Python 进行了一个datetime-timedelta计算,结果是一个datetime对象,然后将其赋值给self. 这就是为什么您的worldtime对象似乎正在“更改”为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在内部存储时间的方式),但是像这样更改“私有”属性通常是个坏主意。另外,我们仍然需要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__()中,称为(如减法)。这意味着当 Python 命中 时-,它会将左右操作数作为和(分别)传递给__sub__()特殊方法,然后返回该方法的结果。selfother
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 进入时self,它会调用. 到目前为止,一切都很好。但是当它到达 inside 时,我们仍在对一个对象进行减法运算,因此 Python 尽职尽责地一次又一次地调用……一次又一次,然后陷入无限循环!self.tz_offsetto_local()__sub__(self, self.tz_offset)self - other__sub__()worldtime__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)意味着我们正在使用__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 空格制表符,这是 Python 中的标准)
但是......这是最好的方法吗?
希望这能回答您关于 Python 中的子类化的问题。
但考虑到这个问题,我不确定这是否是最好的方法。子类化内置函数可能很复杂并且容易出错,而datetimes 本身已经很复杂并且容易出错。子类化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()返回一个datetime对象,然后你可以打印出来,或者以后做任何你想做的事情。
编辑
我有另一个关于继承的实验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
所以看起来timedeltas 的加法和减法返回一个WorldTime对象,我们可以找到两个WorldTime对象之间的差异作为 a timedelta。
但是,这没有经过严格测试,因此请谨慎操作!