提问人:user2246849 提问时间:4/30/2022 最后编辑:user2246849 更新时间:5/31/2022 访问量:233
使用幼稚和 tz 感知的日期时间实例测试相等性时出现意外行为
Unexpected behaviour when testing equality with naive and tz-aware datetime instances
问:
以下内容是在 Python 3.9.7 中生成的。
我很清楚 Python 中不允许在 tz 感知实例和幼稚实例之间进行比较,并引发 .但是,当测试相等性(使用 and 运算符)时,情况并非如此。事实上,比较总是返回:datetime
TypeError
==
!=
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)
我检查了 datetime
库的源代码,用于比较 datetime 对象的函数有一个参数叫做(默认为):allow_mixed
False
def _cmp(self, other, allow_mixed=False)
当设置为 时,使用运算符进行比较时,可以比较 tz 感知实例和朴素实例。否则,它会引发 TypeError:True
==
datetime
# 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 对 comparison of 和 similar 的实现与此是一致的。pandas.Timestamps
我的问题是,原因是什么?我想,就像参数的名称所说的那样,通过这种方式,我们可以过滤包含朴素和 tz 感知实例(即“混合”)的对象集合。但这难道不会引入潜在错误和意外行为的来源吗?我错过了什么?datetime
在 deceze 的评论后编辑: 事实上,这仍然是“语义上正确的”(即,日期肯定是不同的)。
答:
平等比较几乎从不会引起例外。你只想知道对象 A 是否等于对象 B,它有一个明确的答案。如果它们是相等的,因为这些对象定义了相等,那么答案是肯定的,在所有其他情况下都是否定的。天真的人与有意识的人不同,至少就一个有时区而另一个没有时区而言,所以它们显然是不平等的。datetime
但是,对于大于/小于的比较,对象显然需要是可排序的,否则无法回答问题。不能仅仅因为无法比较对象而返回比较,因为这意味着相反的比较应该返回,它也不能。因此,在这种情况下提出错误是正确的第三种可能结果。<
False
>=
True
从文档中可以看出,直到 Python 3.2 之前,在这些情况下实际上提出了 a:TypeError
在 3.3 版更改: 意识和幼稚之间的平等比较 datetime 实例不会引发 TypeError。
2012 年,Python 开发人员考虑了这两个问题之间的权衡:
- 提高一个可以更容易地捕捉由混合幼稚和有意识对象的严重错误引起的错误。
TypeError
datetime
- 在 Python 中,您几乎可以对任何对象组合使用相等性比较。仅为 object 提出 a 会破坏这种一致性。
TypeError
datetime
以下是 Python 开发人员邮件列表上的相关讨论:
当您的日期时间对象是新创建的时,这很好。事实并非如此 当其中一些已经存在时,例如在数据库中(使用 ORM) 层)。混合幼稚和有意识的日期时间目前是一场灾难,因为 甚至基本操作(如相等比较)也会因 TypeError (它 一定是 stdlib 中唯一具有这种毒性的类型 行为)。
比较感知和幼稚的日期时间对象没有多大意义,但是 这是一个容易犯的错误。我会说 TypeError 是明智的 警告您的方式,而简单地返回 False 可能会导致很多 混乱。
你可以对同样“令人困惑”的结果说同样的话,但平等从来都不是 引发 TypeError(日期时间实例之间除外):
>>> () == [] False
提出异常具有非常严重的影响,例如使例外 不可能将这些对象放在同一个字典中。
甚至离家更近,
>>> date(2012,6,1) == datetime(2012,6,1) `False`
我同意,平等比较不应该引起例外。
让我们这样吧。
-- --Guido van Rossum (python.org/~guido)
看起来在这次交流中,删除异常的论据更强。Guido van Rossum 是 Python 语言的创造者,对此类问题拥有最终决定权。这就是为什么他曾经被称为终生的仁慈者。因此,在他的“让我们让它如此”之后,行为发生了变化,因此天真和有意识的对象总是比较不相等,而不是提出一个.datetime
TypeError
评论