首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Python3.7中的泡菜破坏更改

Python3.7中的泡菜破坏更改
EN

Stack Overflow用户
提问于 2018-09-14 14:17:52
回答 1查看 3.5K关注 0票数 8

我有自定义list和字典类,在Python3.7中,这些类不再起作用。

代码语言:javascript
代码运行次数:0
运行
复制
import pickle

class A(dict):
    pass

class MyList(list): 

    def __init__(self, iterable=None, option=A):
        self.option=option
        if iterable:
            for x in iterable:
                self.append(x)

    def append(self, obj):
        if isinstance(obj, dict):
            obj = self.option(obj)
        super(MyList, self).append(obj)

    def extend(self, iterable): 
        for item in iterable:
            self.append(item)


if __name__ == '__main__':
    pickle_file = 'test_pickle'
    my_list = MyList([{'a': 1}])
    pickle.dump(my_list, open(pickle_file, 'wb'))
    loaded = pickle.load(open(pickle_file, 'rb'))
    print(isinstance(loaded[0], A))

Python2.6到3.6运行良好:

代码语言:javascript
代码运行次数:0
运行
复制
"C:\Program Files\Python36\python.exe" issue.py
True

但不再正确地将self.option设置为3.7。

代码语言:javascript
代码运行次数:0
运行
复制
"C:\Program Files\Python37\python.exe" issue.py

Traceback (most recent call last):
  File "issue.py", line 28, in <module>
    loaded = pickle.load(open(pickle_file, 'rb'))
  File "issue.py", line 21, in extend
    self.append(item)
  File "issue.py", line 16, in append
    obj = self.option(obj)
AttributeError: 'MyList' object has no attribute 'option'

但是,如果我要删除extend函数,它就会像预期的那样工作。

我也尝试过添加__setstate__,但是在extend之前没有调用它,因此option在那时仍然没有定义。

我确实必须直接从dictlist继承,我确实需要在代码中覆盖appendextend函数。是否有一种预先设置option或另一种修复的方法?这种行为的改变是否记录在案,是否合理?

谢谢您抽时间见我

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-09-14 15:15:21

取消列表对象list.extend(),因为对于某些list子类来说,这样做要快得多。

但是,随着这一变化,列表对象的未筛选代码测试的方式也发生了变化,

代码语言:javascript
代码运行次数:0
运行
复制
if (PyList_Check(list)) {

代码语言:javascript
代码运行次数:0
运行
复制
if (PyList_CheckExact(list)) {

正是这种改变影响了您的代码。上面的测试寻找一个快速路径,也就是说,如果我们有一个list类,那么使用PyList_SetSlice()加载数据,而不是显式调用新实例上的.extend().append()方法的较慢路径。旧版本(Python3.6及更高版本)接受列表和子类,新版本只接受list本身,而不是子类!

因此,对于Python3.6和更高版本,在解压缩自定义MyList.append() 方法时,并不称为,这完全是因为您对list进行了子类化。在Python3.7中,当取消自定义MyList.extend()方法时,调用是。这在很大程度上是有意的,应该允许子类提供一个自定义的.extend()方法,该方法在不进行腌制时会被调用。

解决问题很简单。您的数据已经包装好了,您不需要重新应用该包装器。当您没有self.option集时,只需跳过应用

代码语言:javascript
代码运行次数:0
运行
复制
def append(self, obj):
    if isinstance(obj, dict):
        try:
            obj = self.option(obj)
        except AttributeError:
            # something's wrong, are we unpickling on Python 3.7 or newer?
            if 'option' in self.__dict__:
                # no, we are not, because 'option' has been set, this must
                # be an error in the option() call, so re-raise
                raise
            # yes, we are, just ignore this, obj is already wrapped
    super(MyList, self).append(obj)

这一切都意味着您不能依赖任何已经恢复的实例属性。如果这是一个很大的问题(您仍然需要在解析过程中查询实例状态),那么您必须提供一个不同的 method,它不会将数据作为迭代器返回到结果元组的索引3中。协议版本2、3和4的list().__reduce_ex__()返回(copyreg.__newobj__, type(self), self.__dict__, iter(self), None)

例如,定制版本必须使用(type(self), (tuple(self), self.option), None, None, None)。这确实带来了一些额外的开销(当tuple(self)进行腌制和不腌制时,它将占用额外的内存)。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52333864

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档