为什么 datetime.utcnow 的行为与我对 freezegun 的预期不同?

Why does datetime.utcnow not behave as I'd expect with freezegun?

我注意到一些我不明白的东西,我想知道是否有人可以阐明它。

简而言之:如果

我希望 x()y() 的行为始终相同。但是,当涉及 freezegun 时,情况显然并非如此 - 它冻结 y 而不是 x,我想知道为什么。 (无论如何,如果 xy 是在 freezegun 上下文之外定义的,则为真;在这样的上下文中,它们的行为似乎相同。)

示例:

from datetime import datetime
import freezegun

# I'd expect these two to behave the same, always.
x = datetime.utcnow
y = lambda: datetime.utcnow()

with freezegun.freeze_time('2019-01-02 03:04:05'):
    # Here their behaviours diverge
    print('Time from x:', x())
    print('Time from y:', y())

    # This behaves as I'd expect, however.
    z = datetime.utcnow
    print('Time from z:', z())))

结果:

Time from x: 2019-10-18 12:21:37.508590
Time from y: 2019-01-02 03:04:05
Time from z: 2019-01-02 03:04:05

这里 Time from x 是现在 运行 的时间,即它不受 freezegun 的控制。

任何人都可以阐明这一点吗?这只是 freezegun 的一些奇怪之处,或者当我假设 xy 应该总是等价时,我是否误解了关于 python 的更基本的东西?我看到 utcnow 是一个绑定的 class 方法,但我不明白为什么这会暗示这种行为。


后记:time.time不是这样的

看看 utcnow()'s source 它基本上只是 time.time() 的包装器 — 但 time.timelambda: time.time() 分歧以这种方式......所以我猜这 doesutcnow() being a bound class methiod有关 - 但我不知道是什么.

import time
import freezegun

r = time.time
s = lambda: time.time()

print('Time outside freezegun:', time.time())
with freezegun.freeze_time('2019-01-02 03:04:05'):
    print('Time from r:', r())
    print('Time from s:', s())

给出:

Time outside freezegun: 1571401765.2612312
Time from r: 1546398245.0
Time from s: 1546398245.0

正在播放的版本

$ python --version
Python 3.7.3

$ pip list | grep freezegun
freezegun               0.3.12

So I'm guessing this does have something to do with utcnow() being a bound class methiod — but I don't know what.

看来你的直觉是正确的。 Freezegun 不修补 datetime class 的个别方法 - 而是 it replaces the class entirely 用它自己的 FakeDatetime class.

通过做作业:

x = datetime.utcnow

x 存储对原始 utcnow() 方法的引用,即使在 freezegun.freeze_time() 上下文管理器中也保持不变。

另一方面,lambda: datetime.utcnow()datetime class 上调用 utcnow(),它在当前上下文中可用,并且 FakeDatetimefreezegun.freeze_time().

time.time()patched by freezegun with fake_time(). Freezegun even searches through loaded modules and patches variables 存储对 time.time() 的引用,但它仅限于模块变量,例如它不检查内部列表:

import time
import freezegun

r = [time.time]

with freezegun.freeze_time('2019-01-02 03:04:05'):
    print('Time inside freezegun:', time.time())
    time_inside_list = r[0]
    print('Time from list:', time_inside_list())

输出:

Time inside freezegun: 1546398245.0
Time from list: 1571669871.8807676

Bonus:如果 freezegun 如此细致地查找存储在模块变量中的 time.time() 引用,为什么它不修补 time.time()datetime.utcnow()?

在搜索 sys.modulesdeliberately omits datetimetime 模块不覆盖源函数,作为副作用 time.timedatetime 模块保持未打补丁。