如果删除了原始的Python文件,那么是否真的不可能取消python类?

发布于 2025-02-13 09:01:27 字数 4012 浏览 3 评论 0原文

假设您有以下内容:

file ='hey.py'

class hey:
    def __init__(self):
        self.you =1

ins = hey()

temp = open("cool_class", "wb")
pickle.dump(ins, temp)
temp.close()

现在假设您删除文件hey.py,然后运行以下代码:

pkl_file = open("cool_class", 'rb')
obj = pickle.load(pkl_file)
pkl_file.close()

您会遇到错误。我知道,如果您没有文件hey.py的班级和该课程的属性,则可能无法解决问题。泡菜。但是,必须找出序列化类的属性是什么,然后我可以重建已删除的文件并打开类。我有2岁的泡菜,并且删除了用来构建它们的文件,我只需要找出这些类的属性是什么,以便我可以重新打开这些泡菜

##### update

我知道从最初包含旧类的模块的错误消息中,让我们称其为“ hey.py”。我知道班级的名字让我们称其为“你”。但是,即使在重新创建模块并建立了一个名为“您”的课程之后,我仍然无法让泡菜打开。因此,我在hey.py模块上写下了此代码:

class hey:
    def __init__(self):
        self.hey = 1

    def __setstate__(self):
        self.__dict__ = ''
        self.you = 1

但是我收到错误消息:typeError: init ()采用1个位置参数,但给出了2个

## ####### update 2:

我更改了代码,

class hey:

然后

class hey():

我得到了一个属性,但它并没有告诉我缺少什么属性。然后,我在此行上执行

obj= pickletools.dis(file)

并在Pickletools.py文件上遇到了一个错误

def _genops(data, yield_end_pos=False):
    if isinstance(data, bytes_types):
        data = io.BytesIO(data)

    if hasattr(data, "tell"):
        getpos = data.tell
    else:
        getpos = lambda: None

    while True:
        pos = getpos()
        code = data.read(1)
        opcode = code2op.get(code.decode("latin-1"))
        if opcode is None:
            if code == b"":
                raise ValueError("pickle exhausted before seeing STOP")
            else:
                raise ValueError("at position %s, opcode %r unknown" % (
                                 "<unknown>" if pos is None else pos,
                                 code))
        if opcode.arg is None:
            arg = None
        else:
            arg = opcode.arg.reader(data)
        if yield_end_pos:
            yield opcode, arg, pos, getpos()
        else:
            yield opcode, arg, pos
        if code == b'.':
            assert opcode.name == 'STOP'
            break

code = data.read(1)

say:attributeError:'str'对象没有属性'read'

我现在将尝试Pickletools

# 中的其他方法#########

​这是保存的类:

因此,这是所讨论的类:

class fss(frozenset):
    def __init__(self, *args, **kwargs):
        super(frozenset, self).__init__()

    def __str__(self):
        str1 = lbr + "{}" + rbr
        return str1.format(','.join(str(x) for x in self))

现在请记住,腌制的对象主要是字典,该类别存在于字典中。执行后,

obj= pickletools.genops(file)

我将获得以下输出:

image

image2

我看不出我将如何构造使用该数据的类别的类不知道课是什么。

##############

​我能够看到您的代码的工作原理,但是我从2年前保存的腌制文件,并且其模块和类已删除很久以来,我就无法将其打开为一个字节般的对象,对我来说似乎是必要的。

因此,文件的路径是

file ='hey.pkl' 
pkl_file = open(file, 'rb')
x = MagicUnpickler(io.BytesIO(pkl_file)).load()

返回错误:

TypeError: a bytes-like object is required, not '_io.BufferedReader'

但是我认为该对象是一个字节对象,因为我使用open(file,'rb')

########## ###更新#5

实际上,我认为在AKX的帮助下,我解决了问题。

因此,使用代码:

pkl_file = open(name, 'rb')
x = MagicUnpickler(pkl_file).load()

然后,我创建了两个空白模块,这些模块曾经包含在保存泡菜中找到的类,但是我不必将这些类放在上面。我在文件pickle.py.py中遇到错误:

def load_reduce(self):
    stack = self.stack
    args = stack.pop()
    func = stack[-1]
    try:
        stack[-1] = func(*args)
    except TypeError:
        pass
dispatch[REDUCE[0]] = load_reduce

因此,除了错误之后,一切都起作用。我真的要感谢AKX帮助我。实际上,我已经尝试解决这个问题已有5年了,因为我使用泡菜的频率要比大多数程序员多得多。我曾经不明白,如果您更改课程,那么这会破坏与该类别保存的任何腌制文件,因此我一次又一次地遇到了这个问题。但是,现在我要回顾一些已有2年的代码,并且看起来有些文件已被删除,将来我将非常需要此代码。因此,我非常感谢您为解决这个问题解决的帮助。

Suppose you have the following:

file = 'hey.py'

class hey:
    def __init__(self):
        self.you =1

ins = hey()

temp = open("cool_class", "wb")
pickle.dump(ins, temp)
temp.close()

Now suppose you delete the file hey.py and you run the following code:

pkl_file = open("cool_class", 'rb')
obj = pickle.load(pkl_file)
pkl_file.close()

You'll get an error. I get that it's probably the case that you can't work around the problem of if you don't have the file hey.py with the class and the attributes of that class in the top level then you can't open the class with pickle. But it has to be the case that I can find out what the attributes of the serialized class are and then I can reconstruct the deleted file and open the class. I have pickles that are 2 years old and I have deleted the file that I used to construct them and I just have to find out what what the attributes of those classes are so that I can reopen these pickles

#####UPDATE

I know from the error messages that the module that originally contained the old class, let's just call it 'hey.py'. And I know the name of the class let's call it 'you'. But even after recreating the module and building a class called 'you' I still can't get the pickle to open. So I wrote this code on the hey.py module like so:

class hey:
    def __init__(self):
        self.hey = 1

    def __setstate__(self):
        self.__dict__ = ''
        self.you = 1

But I get the error message: TypeError: init() takes 1 positional argument but 2 were given

#########UPDATE 2:

I Changed the code from

class hey:

to

class hey():

I then got an AttributeError but it doesn't tell me what attribute is missing. I then performed

obj= pickletools.dis(file)

And got an error on the pickletools.py file here

def _genops(data, yield_end_pos=False):
    if isinstance(data, bytes_types):
        data = io.BytesIO(data)

    if hasattr(data, "tell"):
        getpos = data.tell
    else:
        getpos = lambda: None

    while True:
        pos = getpos()
        code = data.read(1)
        opcode = code2op.get(code.decode("latin-1"))
        if opcode is None:
            if code == b"":
                raise ValueError("pickle exhausted before seeing STOP")
            else:
                raise ValueError("at position %s, opcode %r unknown" % (
                                 "<unknown>" if pos is None else pos,
                                 code))
        if opcode.arg is None:
            arg = None
        else:
            arg = opcode.arg.reader(data)
        if yield_end_pos:
            yield opcode, arg, pos, getpos()
        else:
            yield opcode, arg, pos
        if code == b'.':
            assert opcode.name == 'STOP'
            break

At this line:

code = data.read(1)

saying: AttributeError: 'str' object has no attribute 'read'

I will now try the other methods in the pickletools

########### UPDATE 3

I wanted to see what happened when I saved an object composed mostly of dictionary but some of the values in the dictionaries were classes. This is the class that was saved:

so here is the class in question:

class fss(frozenset):
    def __init__(self, *args, **kwargs):
        super(frozenset, self).__init__()

    def __str__(self):
        str1 = lbr + "{}" + rbr
        return str1.format(','.join(str(x) for x in self))

Now keep in mind that the object pickled is mostly a dictionary and that class exists within the dictionary. After performing

obj= pickletools.genops(file)

I get the following output:

image

image2

I don't see how I would be able to construct the class referred to with that data if I hadn't known what the class was.

############### UPDATE #4

@AKK

Thanks for helping me out. I am able to see how your code works but my pickled file saved from 2 years ago and whose module and class have long since been deleted, I cannot open it into a bytes-like object which to me seems to be a necessity.

So the path of the file is

file ='hey.pkl' 
pkl_file = open(file, 'rb')
x = MagicUnpickler(io.BytesIO(pkl_file)).load()

This returns the error:

TypeError: a bytes-like object is required, not '_io.BufferedReader'

But I thought the object was a bytes object since I opened it with open(file, 'rb')

############ UPDATE #5

Actually, I think with AKX's help I've solved the problem.

So using the code:

pkl_file = open(name, 'rb')
x = MagicUnpickler(pkl_file).load()

I then created two blank modules which once contained the classes found in the save pickle, but I did not have to put the classes on them. I was getting an error in the file pickle.py here:

def load_reduce(self):
    stack = self.stack
    args = stack.pop()
    func = stack[-1]
    try:
        stack[-1] = func(*args)
    except TypeError:
        pass
dispatch[REDUCE[0]] = load_reduce

So after excepting that error, everything worked. I really want to thank AKX for helping me out. I have actually been trying to solve this problem for about 5 years because I use pickles far more often than most programmers. I used to not understand that if you alter a class then that ruins any pickled files saved with that class so I ran into this problem again and again. But now that I'm going back over some code which is 2 years old and it looks like some of the files were deleted, I'm going to need this code a lot in the future. So I really appreciate your help in getting this problem solved.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

稍尽春風 2025-02-20 09:01:27

好吧,有了一些黑客和魔术,当然,您可以补充缺少的课程,但是我不能保证这对您可能遇到的所有泡菜数据都有效;首先,这不会触及__ setstate __/__降低__协议,所以我不知道它们是否有效。

给定一个脚本文件(so72863050.py在我的情况下):

import io
import pickle
import types
from logging import Formatter

# Create a couple empty classes. Could've just used `class C1`,
# but we're coming back to this syntax later.
C1 = type('C1', (), {})
C2 = type('C2', (), {})

# Create an instance or two, add some data...
inst = C1()
inst.child1 = C2()
inst.child1.magic = 42
inst.child2 = C2()
inst.child2.mystery = 'spooky'
inst.child2.log_formatter = Formatter('heyyyy %(message)s')  # To prove we can unpickle regular classes still
inst.other_data = 'hello'
inst.some_dict = {'a': 1, 'b': 2}

# Pickle the data!
pickle_bytes = pickle.dumps(inst)

# Let's erase our memory of these two classes:

del C1
del C2

try:
    print(pickle.loads(pickle_bytes))
except Exception as exc:
    pass  # Can't get attribute 'C1' on <module '__main__'> – yep, it certainly isn't there!

我们现在成功创建了一些我们无法加载的泡菜数据,因为我们忘记了这两个类。现在,由于 unflowlow noreferrer“面对某些失败(或至少一个属性)的魔术毫无疑问,综合了一个简单的班级AIR:

# Could derive from Unpickler, but that may be a C class, so our tracebacks would be less helpful
class MagicUnpickler(pickle._Unpickler):
    def __init__(self, fp):
        super().__init__(fp)
        self._magic_classes = {}

    def find_class(self, module, name):
        try:
            return super().find_class(module, name)
        except AttributeError:
            return self._create_magic_class(module, name)

    def _create_magic_class(self, module, name):
        cache_key = (module, name)
        if cache_key not in self._magic_classes:
            cls = type(f'<<Emulated Class {module}:{name}>>', (types.SimpleNamespace,), {})
            self._magic_classes[cache_key] = cls
        return self._magic_classes[cache_key]

现在,当我们从上述pickle_bytes plin ol'pickle.loads()无法加载...

x = MagicUnpickler(io.BytesIO(pickle_bytes)).load()
print(x)
print(x.child1.magic)
print(x.child2.mystery)
print(x.child2.log_formatter._style._fmt)

打印出来

<<Emulated Class __main__:C1>>(child1=<<Emulated Class __main__:C2>>(magic=42), child2=<<Emulated Class __main__:C2>>(mystery='spooky'), other_data='hello', some_dict={'a': 1, 'b': 2})
42
spooky
heyyyy %(message)s

时, 魔法!

Well, with a bit of hacking and magic, sure, you can hydrate missing classes, but I'm not guaranteeing this will work for all pickle data you may encounter; for one, this doesn't touch the __setstate__/__reduce__ protocols, so I don't know if they work.

Given a script file (so72863050.py in my case):

import io
import pickle
import types
from logging import Formatter

# Create a couple empty classes. Could've just used `class C1`,
# but we're coming back to this syntax later.
C1 = type('C1', (), {})
C2 = type('C2', (), {})

# Create an instance or two, add some data...
inst = C1()
inst.child1 = C2()
inst.child1.magic = 42
inst.child2 = C2()
inst.child2.mystery = 'spooky'
inst.child2.log_formatter = Formatter('heyyyy %(message)s')  # To prove we can unpickle regular classes still
inst.other_data = 'hello'
inst.some_dict = {'a': 1, 'b': 2}

# Pickle the data!
pickle_bytes = pickle.dumps(inst)

# Let's erase our memory of these two classes:

del C1
del C2

try:
    print(pickle.loads(pickle_bytes))
except Exception as exc:
    pass  # Can't get attribute 'C1' on <module '__main__'> – yep, it certainly isn't there!

we now have successfully created some pickle data that we can't load anymore, since we forgot about those two classes. Now, since the unpickling mechanism is customizable, we can derive a magic unpickler, that in the face of certain defeat (or at least an AttributeError), synthesizes a simple class from thin air:

# Could derive from Unpickler, but that may be a C class, so our tracebacks would be less helpful
class MagicUnpickler(pickle._Unpickler):
    def __init__(self, fp):
        super().__init__(fp)
        self._magic_classes = {}

    def find_class(self, module, name):
        try:
            return super().find_class(module, name)
        except AttributeError:
            return self._create_magic_class(module, name)

    def _create_magic_class(self, module, name):
        cache_key = (module, name)
        if cache_key not in self._magic_classes:
            cls = type(f'<<Emulated Class {module}:{name}>>', (types.SimpleNamespace,), {})
            self._magic_classes[cache_key] = cls
        return self._magic_classes[cache_key]

Now, when we run that magic unpickler against a stream from the aforebuilt pickle_bytes that plain ol' pickle.loads() couldn't load...

x = MagicUnpickler(io.BytesIO(pickle_bytes)).load()
print(x)
print(x.child1.magic)
print(x.child2.mystery)
print(x.child2.log_formatter._style._fmt)

prints out

<<Emulated Class __main__:C1>>(child1=<<Emulated Class __main__:C2>>(magic=42), child2=<<Emulated Class __main__:C2>>(mystery='spooky'), other_data='hello', some_dict={'a': 1, 'b': 2})
42
spooky
heyyyy %(message)s

Hey, magic!

无声情话 2025-02-20 09:01:27

函数load_reduce(self)中的错误可以通过以下方式重新创建:

class Y(set):
    pass


pickle_bytes = io.BytesIO(pickle.dumps(Y([2, 3, 4, 5])))

del Y

print(MagicUnpickler(pickle_bytes).load())

AKX的答案在类从基本类别的基础类,dict,list,...

The error in function load_reduce(self) can be re-created by:

class Y(set):
    pass


pickle_bytes = io.BytesIO(pickle.dumps(Y([2, 3, 4, 5])))

del Y

print(MagicUnpickler(pickle_bytes).load())

AKX's answer do not solve cases when the class inherit from base classes as set, dict, list,...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文