使用Dill加载对象时出现Python TypeError
我想把一个很大且可能很复杂的对象保存到文件里,以便以后使用。
在使用 dill.dump(file)
的时候没有遇到任何问题:
In [1]: import echonest.remix.audio as audio
In [2]: import dill
In [3]: audiofile = audio.LocalAudioFile("/Users/path/Track01.mp3")
en-ffmpeg -i "/Users/path/audio/Track01.mp3" -y -ac 2 -ar 44100 "/var/folders/X2/X2KGhecyG0aQhzRDohJqtU+++TI/-Tmp-/tmpWbonbH.wav"
Computed MD5 of file is b3820c166a014b7fb8abe15f42bbf26e
Probing for existing analysis
In [4]: with open('audio_object_dill.pkl', 'wb') as f:
...: dill.dump(audiofile, f)
...:
In [5]:
但是在尝试加载这个 .pkl
文件时:
In [1]: import dill
In [2]: with open('audio_object_dill.pkl', 'rb') as f:
...: audio_object = dill.load(f)
...:
出现了以下错误:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-203b696a7d73> in <module>()
1 with open('audio_object_dill.pkl', 'rb') as f:
----> 2 audio_object = dill.load(f)
3
/Users/mikekilmer/Envs/GLITCH/lib/python2.7/site-packages/dill-0.2.2.dev-py2.7.egg/dill/dill.pyc in load(file)
185 pik = Unpickler(file)
186 pik._main_module = _main_module
--> 187 obj = pik.load()
188 if type(obj).__module__ == _main_module.__name__: # point obj class to main
189 try: obj.__class__ == getattr(pik._main_module, type(obj).__name__)
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.pyc in load(self)
856 while 1:
857 key = read(1)
--> 858 dispatch[key](self)
859 except _Stop, stopinst:
860 return stopinst.value
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.pyc in load_newobj(self)
1081 args = self.stack.pop()
1082 cls = self.stack[-1]
-> 1083 obj = cls.__new__(cls, *args)
1084 self.stack[-1] = obj
1085 dispatch[NEWOBJ] = load_newobj
TypeError: __new__() takes at least 2 arguments (1 given)
这个 AudioObject 比上面提到的 class object
要复杂得多(而且也大得多),我不太清楚是否需要通过 dill
发送第二个参数,如果需要的话,这个参数是什么,或者怎么判断这种对象是否适合用这种方式保存。
稍微检查了一下这个对象:
In [4]: for k, v in vars(audiofile).items():
...: print k, v
...:
返回结果是:
is_local False
defer False
numChannels 2
verbose True
endindex 13627008
analysis <echonest.remix.audio.AudioAnalysis object at 0x103c61bd0>
filename /Users/mikekilmer/Envs/GLITCH/glitcher/audio/Track01.mp3
convertedfile /var/folders/X2/X2KGhecyG0aQhzRDohJqtU+++TI/-Tmp-/tmp9ADD_Z.wav
sampleRate 44100
data [[0 0]
[0 0]
[0 0]
...,
[0 0]
[0 0]
[0 0]]
而且 audiofile.analysis
似乎包含一个叫做 audiofile.analysis.source
的属性,这个属性包含(或者显然指向) audiofile.analysis.source.analysis
1 个回答
在这个情况下,答案就在模块内部。
LocalAudioFile
类提供了自己的 save
方法,每个这个类的实例都可以使用它。你可以通过 LocalAudioFile.save
或者更常见的 the_audio_object_instance.save
来调用这个方法。
对于一个 .mp3
文件来说,LocalAudioFile
的实例包含一个指向临时 .wav
文件的指针,这个 .wav
文件是解压后的 .mp3
版本,还有一堆从最初的音频文件中提取的分析数据,这些数据是通过与(基于互联网的)Echonest API
交互后得到的。
LocalAudioFile.save 方法会调用 shutil.copyfile(path_to_wave, wav_path)
来保存 .wav
文件,文件名和路径与原始音频文件相同,如果文件已经存在,它会返回一个错误。同时,它还会调用 pickle.dump(self, f)
将分析数据保存到一个文件中,这个文件也在最初音频对象文件的目录下。
你可以通过 pickle.load()
简单地重新加载 LocalAudioFile
对象。
这里有一个 iPython
会话,我使用了 dill
,这是一个非常有用的工具,它提供了大部分标准的 pickle
方法,还有很多其他功能:
audiofile = audio.LocalAudioFile("/Users/mikekilmer/Envs/GLITCH/glitcher/audio/Track01.mp3")
In [1]: import echonest.remix.audio as audio
In [2]: import dill
# create the audio_file object
In [3]: audiofile = audio.LocalAudioFile("/Users/mikekilmer/Envs/GLITCH/glitcher/audio/Track01.mp3")
en-ffmpeg -i "/Users/path/audio/Track01.mp3" -y -ac 2 -ar 44100 "/var/folders/X2/X2KGhecyG0aQhzRDohJqtU+++TI/-Tmp-/tmp_3Ei0_.wav"
Computed MD5 of file is b3820c166a014b7fb8abe15f42bbf26e
Probing for existing analysis
#call the LocalAudioFile save method
In [4]: audiofile.save()
Saving analysis to local file /Users/path/audio/Track01.mp3.analysis.en
#confirm the object is valid by calling it's duration method
In [5]: audiofile.duration
Out[5]: 308.96
#delete the object - there's probably a "correct" way to do this
in [6]: audiofile = 0
#confirm it's no longer an audio_object
In [7]: audiofile.duration
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-12-04baaeda53a4> in <module>()
----> 1 audiofile2.duration
AttributeError: 'int' object has no attribute 'duration'
#open the pickled version (using dill)
In [8]: with open('/Users/path/audio/Track01.mp3.analysis.en') as f:
....: audiofile = dill.load(f)
....:
#confirm it's a valid LocalAudioFile object
In [8]: audiofile.duration
Out[8]: 308.96