【问题标题】:Different cloudpickle everytime the program is run每次运行程序时都有不同的cloudpickle
【发布时间】:2021-12-08 13:10:45
【问题描述】:

考虑以下python代码:

import cloudpickle


class Foo:
    def __init__(self, num):
        self.num = num


def outer(num):
    return Foo(num)


print(cloudpickle.dumps(outer))

这会在您每次运行代码时产生不同的泡菜。使用 pickletools 分析 pickle 文件显示以下差异:

144c144
<   552: \x8c         SHORT_BINUNICODE '2e3db4572bb349268962a75a8a6f034c'
---
>   552: \x8c         SHORT_BINUNICODE '89ee770de9b745c4bbe83c353f1debba'

现在,我了解到 cloudpickle 不能保证 pickle 文件的确定性。 (link),但我很好奇为什么这两个泡菜文件不同。看起来上面的差异是因为 Foo 类的某种不同的哈希值。

请注意,我使用固定的PYTHONHASHSEED 运行 python 程序。

PS: 这足以重现问题:

import pickletools
import cloudpickle


class Foo:
    def __init__(self, num):
        self.num = num


pickletools.dis(cloudpickle.dumps(Foo))

所以似乎每个类都有一个属性,它被烘焙到 cloudpickle 中,但我不知道那个属性是什么。

【问题讨论】:

    标签: python pickle cloudpickle


    【解决方案1】:

    好奇!

    我深入研究了源代码,发现它不是类的属性,甚至不是计算的哈希值,它是just a random identifier generated with uuid4() per class

    那个函数被_class_getnewargs() here调用,被_dynamic_class_reduce() here调用,有注释

    保存一个不能作为模块全局存储的类。 此方法用于序列化在函数内部定义的类,或者无法序列化为属性查找的类 来自全局模块。

    如果类不在__main__ 模块中,事情马上就简单多了(因为__main__ 从最终的unpickler 的角度来看可能是任何东西);如果你这样做 from b import outer 和 cloudpickle 那个 outer,你会得到

        0: \x80 PROTO      5
        2: \x95 FRAME      15
       11: \x8c SHORT_BINUNICODE 'b'
       14: \x94 MEMOIZE    (as 0)
       15: \x8c SHORT_BINUNICODE 'outer'
       22: \x94 MEMOIZE    (as 1)
       23: \x93 STACK_GLOBAL
       24: \x94 MEMOIZE    (as 2)
       25: .    STOP
    

    作为泡菜而不是所有巫毒云泡菜所做的泡菜是在__main__中的东西。

    【讨论】:

    • > if you do from b import outer and cloudpickle that outer 这正是我解决问题的方法。对于更大的上下文,Kubeflow 缓存依赖于云酸洗的确定性。
    • 从一开始就正确 (Python-) 打包您的 ML 代码的另一个好理由 :)
    猜你喜欢
    • 2011-12-06
    • 2014-12-06
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多