1

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

简而言之:如果

  • 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 的一些奇怪之处,或者当我假设x并且y应该始终等价时,我是否误解了关于 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
4

1 回答 1

2

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

看起来你的直觉是正确的。Freezegun 不会修补datetime类的单个方法 - 相反,它会用自己的类完全替换FakeDatetime该类。

通过做作业:

x = datetime.utcnow

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

另一方面,lambda: datetime.utcnow()调用当前上下文中可用的类utcnow(),并由.datetimeFakeDatetimefreezegun.freeze_time()

time.time()freezegunfake_time()修补。Freezegun 甚至搜索已加载的模块并修补存储对 的引用的变量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()存储在模块变量中的引用,为什么它不修补time.time()内部使用的datetime.utcnow()

在搜索sys.modules它时故意省略 datetime和模块不覆盖源函数,并且作为在模块中导入time的副作用保持不修补。time.timedatetime

于 2019-10-21T15:19:56.750 回答