使用namedtuple的多进程对象 - Pickling错误

7 投票
2 回答
3527 浏览
提问于 2025-04-17 21:39

我在使用命名元组(namedtuples)时遇到了麻烦,特别是在我想把它们放到多进程(multiprocessing)中使用的时候。我收到了一个关于序列化(pickling)的错误。我尝试了从其他StackOverflow帖子上找到的一些方法,但都没有成功。以下是我代码的结构:

package_main, test_module

 import myprogram.package_of_classes.data_object_module
 import ....obj_calculate

 class test(object):
       if __name__ == '__main__':
             my_obj=create_obj('myobject',['f1','f2'])
             input = multiprocessing.Queue()
             output = multiprocessing.Queue()
             input.put(my_obj)
             j=Process(target=obj_calculate, args=(input,output))
             j.start()

package_of_classes, data_object_module

 import collections
 import ....load_flat_file

 def get_ntuple_format(obj):
     nt_fields=''
     for fld in obj.fields:
         nt_fields=nt_fields+fld+', '
     nt_fields=nt_fields[0:-2]
     ntuple=collections.namedtuple('ntuple_format',nt_fields)
     return ntuple

 Class Data_obj:
    def __init__(self, name,fields):
        self.name=name
        self.fields=fields
        self.ntuple_form=get_ntuple_format(self)  

    def calculate(self):
        self.file_read('C:/files','division.txt')

    def file_read(self,data_directory,filename):
        output=load_flat_file(data_directory,filename,self.ntuple_form)
        self.data=output

utils_package, utils_module

def create_dataobj(name,fields):
    locals()[name]=Data_Obj(name,fields)
    return locals()[name]  

def obj_calculate(input,output):   
    obj=input.get()
    obj.calculate()
    output.put(obj)

loads_module

def load_flat_file(data_directory,filename,ntuple_form):
     csv.register_dialect('csvrd', delimiter='\t', quoting=csv.QUOTE_NONE)
     ListofTuples=[]
     with open(os.path.join(data_directory,filename), 'rb') as f:
          reader = csv.reader(f,'csvrd')
          for line in reader:
               if line:
                   ListofTuples.append(ntuple_form._make(line))
     return ListofTuples

我遇到的错误是:

PicklingError: PicklingError: Can't pickle  class '__main__ . ntuple_format: it's not the same object as __ main __. ntuple_format

附注:因为我从一个大项目中提取了这段示例代码,所以请忽略一些小的不一致之处。

2 个回答

3

我认为你可以对一个 namedtuple 进行序列化,就像对一个在 __main__ 中定义的 class 一样。

>>> import dill as pickle
>>> import collections
>>> 
>>> thing = collections.namedtuple('thing', ['a','b'])
>>> pickle.loads(pickle.dumps(thing))
<class '__main__.thing'>

这里是同样的内容,放在一个类的方法里使用。

>>> class Foo(object):
...   def bar(self, a, b):
...     thing = collections.namedtuple('thing', ['a','b'])     
...     thing.a = a 
...     thing.b = b
...     return thing 
... 
>>> f = Foo()
>>> q = f.bar(1,2)
>>> q.a
1
>>> q.b
2
>>> q._fields
('a', 'b')
>>> 
>>> pickle.loads(pickle.dumps(Foo.bar))
<unbound method Foo.bar>
>>> pickle.loads(pickle.dumps(f.bar))
<bound method Foo.bar of <__main__.Foo object at 0x10dbf5450>>

你只需要用 dill 代替 pickle 就可以了。

你可以在这里获取 dillhttps://github.com/uqfoundation

7

你不能对动态创建的类(在这里指的是命名元组)进行序列化(也就是“腌制”)。为了让一个类可以被序列化,它必须在一个可以被导入的模块的最顶层定义。

如果你只需要支持几种元组,可以考虑提前在模块的最顶层定义好它们,然后在需要的时候动态选择合适的。如果你需要一个完全动态的容器格式,可以考虑直接使用一个 dict(字典)。

撰写回答