【问题标题】:Why does datetime.utcnow not behave as I'd expect with freezegun?为什么 datetime.utcnow 的行为不像我对 freezegun 的预期那样?
【发布时间】:2019-10-18 12:35:16
【问题描述】:

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

简而言之:如果

  • x = datetime.datetime.utcnow

  • y = lambda: datetime.datetime.utcnow()

我希望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 是一个绑定的类方法,但我不明白为什么这会暗示这种行为。


后记:time.time 不是这样的

看看utcnow()'s source,它基本上只是time.time()的一个包装——但是time.timelambda: time.time() 不要以这种方式发散......所以我猜这个确实与utcnow() 是绑定类方法有关——但我不知道是什么。

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

【问题讨论】:

    标签: python datetime freezegun


    【解决方案1】:

    所以我猜这确实与 utcnow() 是一个绑定类方法有关——但我不知道是什么。

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

    通过做作业:

    x = datetime.utcnow
    

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

    另一方面,lambda: datetime.utcnow() 在当前上下文中可用的datetime 类上调用utcnow(),即由freezegun.freeze_time() 修补的FakeDatetime

    time.time()patched by freezegunfake_time()。 Freezegun 甚至搜索加载的模块和 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
    

    奖励:如果 freezegun 如此细致地找到存储在模块变量中的 time.time() 引用,为什么不修补 datetime.utcnow() 内部使用的 time.time()

    在搜索 sys.modules 时,deliberately omits datetimetime 模块不会覆盖源函数,并且作为副作用 time.time 导入到 datetime 模块中时不会打补丁。

    【讨论】:

    • 很棒的解释-谢谢!显然,面对无情的猴子补丁,所有的赌注都落空了。 :-)
    猜你喜欢
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2020-03-11
    • 2016-01-28
    • 2018-12-15
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    相关资源
    最近更新 更多