使用原始和 tz 感知日期时间实例测试相等性时的意外行为
Unexpected behaviour when testing equality with naive and tz-aware datetime instances
以下是在Python 3.9.7.
中制作的
我很清楚在 Python 中不允许比较 tz 感知和天真的 datetime
实例并引发 TypeError
。但是,在测试相等性时(使用 ==
和 !=
运算符)实际上并非如此。其实比较总是returns False
:
import datetime
import pytz
t_tz_aware = datetime.datetime(2020, 5, 23, tzinfo=pytz.UTC)
t_naive = datetime.datetime(2020, 5, 23)
# Prints 'False'.
print(t_tz_aware == t_naive)
# Raises TypeError: can't compare offset-naive and offset-aware datetimes.
print(t_tz_aware < t_naive)
我检查了 source code of the datetime
library 并且比较日期时间对象的函数有一个名为 allow_mixed
的参数(默认为 False
):
def _cmp(self, other, allow_mixed=False)
当设置为 True
时,即使用 ==
运算符进行比较时的情况,可以比较 tz-aware 和 naive datetime
实例。否则,它会引发 TypeError:
# When testing for equality, set allow_mixed to True.
# For all the other operators, it remains False.
def __eq__(self, other):
if isinstance(other, datetime):
return self._cmp(other, allow_mixed=True) == 0
if myoff is None or otoff is None:
if allow_mixed:
return 2 # arbitrary non-zero value
else:
raise TypeError("cannot compare naive and aware datetimes")
所以,这看起来确实是有意为之的行为。其实Pandas'实现pandas.Timestamps
和类似的比较也是符合这个的。
我的问题是,推理是什么?我想,就像参数的名称所说的那样,我们可以通过这种方式过滤包含原始实例和 tz 感知实例(即“混合”)的 datetime
对象的集合。但这不会引入潜在错误和意外行为的来源吗?我错过了什么?
编辑 在 deceze 的评论之后:
这实际上仍然是“语义正确的”(即日期肯定不同)。
相等比较几乎不会引发异常。你只想知道object A是否等于object B,答案很明确。如果它们相等,但是这些对象定义相等,答案是 yes,在所有其他情况下它是 no。一个天真的 datetime
与一个有意识的人的区别至少在于一个有时区而另一个没有,所以他们显然不相等。
虽然greater-than/lower-than比较,对象显然需要可订购,否则无法回答问题。 <
比较不能 return False
只是因为无法比较对象,因为那意味着相反的 >=
比较应该 return True
,它也不能。所以在这种情况下提出错误是正确的第三种可能结果。
正如在 docs 中所见,直到 Python 3.2 在这些情况下实际提出了 TypeError
:
Changed in version 3.3: Equality comparisons between aware and naive
datetime instances don’t raise TypeError.
2012 年,Python 开发人员考虑了这两个问题之间的 trade-off:
- 提出一个
TypeError
可以更容易地捕获由混合天真和有意识的 datetime
对象的严重错误引起的错误。
- 在 Python 中,您几乎可以对任何对象组合使用相等比较。仅对
datetime
个对象提出 TypeError
会破坏这种一致性。
下面是Python developer mailing list的相关讨论:
This is nice when your datetime objects are freshly created. It is not so
nice when some of them already exist e.g. in a database (using an ORM
layer). Mixing naive and aware datetimes is currently a catastrophe, since
even basic operations such as equality comparison fail with a TypeError (it
must be pretty much the only type in the stdlib with such poisonous
behaviour).
比较有意识的和天真的日期时间对象没有多大意义,但
这是一个容易犯的错误。我会说 TypeError 是一个明智的
在简单地返回 False 时警告你的方法可能会导致很多
混乱。
你可以对同样“令人困惑”的结果说同样的话,但平等永远不会
引发类型错误(日期时间实例之间除外):
>>> () == []
False
引发异常具有非常严重的影响,例如使它
不可能将这些对象放在同一个字典中。
离家更近,
>>> date(2012,6,1) == datetime(2012,6,1)
`False`
我同意,相等比较不应引发异常。
我们就这样吧。
--
--Guido van Rossum (python.org/~guido)
看起来在这次交流中关于删除异常的论据更有力。 Guido van Rossum 是 Python 语言的创造者,对此类问题拥有最终决定权。这就是为什么他曾经被称为 benevolent dicator for life。因此,在他的“让我们做到这一点”之后,行为发生了变化,因此天真且有意识的 datetime
对象总是比较不相等,而不是引发 TypeError
.
以下是在Python 3.9.7.
中制作的我很清楚在 Python 中不允许比较 tz 感知和天真的 datetime
实例并引发 TypeError
。但是,在测试相等性时(使用 ==
和 !=
运算符)实际上并非如此。其实比较总是returns False
:
import datetime
import pytz
t_tz_aware = datetime.datetime(2020, 5, 23, tzinfo=pytz.UTC)
t_naive = datetime.datetime(2020, 5, 23)
# Prints 'False'.
print(t_tz_aware == t_naive)
# Raises TypeError: can't compare offset-naive and offset-aware datetimes.
print(t_tz_aware < t_naive)
我检查了 source code of the datetime
library 并且比较日期时间对象的函数有一个名为 allow_mixed
的参数(默认为 False
):
def _cmp(self, other, allow_mixed=False)
当设置为 True
时,即使用 ==
运算符进行比较时的情况,可以比较 tz-aware 和 naive datetime
实例。否则,它会引发 TypeError:
# When testing for equality, set allow_mixed to True.
# For all the other operators, it remains False.
def __eq__(self, other):
if isinstance(other, datetime):
return self._cmp(other, allow_mixed=True) == 0
if myoff is None or otoff is None:
if allow_mixed:
return 2 # arbitrary non-zero value
else:
raise TypeError("cannot compare naive and aware datetimes")
所以,这看起来确实是有意为之的行为。其实Pandas'实现pandas.Timestamps
和类似的比较也是符合这个的。
我的问题是,推理是什么?我想,就像参数的名称所说的那样,我们可以通过这种方式过滤包含原始实例和 tz 感知实例(即“混合”)的 datetime
对象的集合。但这不会引入潜在错误和意外行为的来源吗?我错过了什么?
编辑 在 deceze 的评论之后: 这实际上仍然是“语义正确的”(即日期肯定不同)。
相等比较几乎不会引发异常。你只想知道object A是否等于object B,答案很明确。如果它们相等,但是这些对象定义相等,答案是 yes,在所有其他情况下它是 no。一个天真的 datetime
与一个有意识的人的区别至少在于一个有时区而另一个没有,所以他们显然不相等。
虽然greater-than/lower-than比较,对象显然需要可订购,否则无法回答问题。 <
比较不能 return False
只是因为无法比较对象,因为那意味着相反的 >=
比较应该 return True
,它也不能。所以在这种情况下提出错误是正确的第三种可能结果。
正如在 docs 中所见,直到 Python 3.2 在这些情况下实际提出了 TypeError
:
Changed in version 3.3: Equality comparisons between aware and naive datetime instances don’t raise TypeError.
2012 年,Python 开发人员考虑了这两个问题之间的 trade-off:
- 提出一个
TypeError
可以更容易地捕获由混合天真和有意识的datetime
对象的严重错误引起的错误。 - 在 Python 中,您几乎可以对任何对象组合使用相等比较。仅对
datetime
个对象提出TypeError
会破坏这种一致性。
下面是Python developer mailing list的相关讨论:
This is nice when your datetime objects are freshly created. It is not so nice when some of them already exist e.g. in a database (using an ORM layer). Mixing naive and aware datetimes is currently a catastrophe, since even basic operations such as equality comparison fail with a TypeError (it must be pretty much the only type in the stdlib with such poisonous behaviour).
比较有意识的和天真的日期时间对象没有多大意义,但 这是一个容易犯的错误。我会说 TypeError 是一个明智的 在简单地返回 False 时警告你的方法可能会导致很多 混乱。
你可以对同样“令人困惑”的结果说同样的话,但平等永远不会 引发类型错误(日期时间实例之间除外):
>>> () == [] False
引发异常具有非常严重的影响,例如使它 不可能将这些对象放在同一个字典中。
离家更近,
>>> date(2012,6,1) == datetime(2012,6,1) `False`
我同意,相等比较不应引发异常。
我们就这样吧。
-- --Guido van Rossum (python.org/~guido)
看起来在这次交流中关于删除异常的论据更有力。 Guido van Rossum 是 Python 语言的创造者,对此类问题拥有最终决定权。这就是为什么他曾经被称为 benevolent dicator for life。因此,在他的“让我们做到这一点”之后,行为发生了变化,因此天真且有意识的 datetime
对象总是比较不相等,而不是引发 TypeError
.