6

プログラムの正確な状態を保存し、後で再ロードするオプションをユーザーに提供したい pygame で描画プログラムを作成しています。この時点で、グローバル dict のコピーを保存し、すべてのオブジェクトをピクルして繰り返します。pygame にはピクルできないオブジェクトがいくつかありますが、文字列に変換してその方法でピクルすることができます。私のコードはこれを行うように設定されていますが、これらの unpicklable オブジェクトの一部は参照によって到達されています。つまり、それらはグローバル ディクショナリにはありませんが、グローバル ディクショナリ内のオブジェクトによって参照されます。この再帰でそれらをピクルしたいのですが、ピクルに問題があったオブジェクトを返し、変更してから再度ピクルするように指示する方法がわかりません。私のコードは、私がやったことを行うための別の優れた方法がある場合、本当に非常に面倒です.


surfaceStringHeader = 'PYGAME.SURFACE_CONVERTED:'
imageToStringFormat = 'RGBA'
def save_project(filename=None):
    assert filename != None, "Please specify path of project file"
    pickler = pickle.Pickler(file(filename,'w'))
    for key, value in globals().copy().iteritems():
        #There's a bit of a kludge statement here since I don't know how to 
        #access module type object directly
        if type(value) not in [type(sys),type(None)]   and \
        key not in ['__name__','value','key']          and \
        (key,value) not in pygame.__dict__.iteritems() and \
        (key,value) not in sys.__dict__.iteritems()    and \
        (key,value) not in pickle.__dict__.iteritems(): 
        #Perhaps I should add something to the above to reduce redundancy of
        #saving the program defaults?
            #Refromat unusable objects:
            if type(value)==pygame.Surface:
                valueString = pygame.image.tostring(value,imageToStringFormat)
                widthString = str(value.get_size()[0]).zfill(5)
                heightString = str(value.get_size()[1]).zfill(5)
                formattedValue = surfaceStringHeader+widthString+heightString+valueString
            else:
                formattedValue = value

            try:
                pickler.dump((key,formattedValue))
            except Exception as e:
                print key+':' + str(e)

def open_project(filename=None):
    assert filename != None, "Please specify path to project file"
    unpickler = pickle.Unpickler(file(filename,'r'))
    haventReachedEOF = False
    while haventReachedEOF:
        try:
            key,value = unpickler.load()
            #Rework the unpicklable objects stored 
            if type(value) == str and value[0:25]==surfaceStringHeader:
                value = pygame.image.frombuffer(value[36:],(int(value[26:31]),int(value[31:36])),imageToStringFormat)
            sys.modules['__main__'].__setattr__(key,value)
        except EOFError:
            haventReachedEOF = True
4

5 に答える 5

5

要するに:これをしないでください。

アプリケーション内のすべてをピクルするのは面倒で、問題が発生する可能性があります。プログラムから必要なデータを取得し、適切なデータ形式で手動で保存し、そのデータから必要なものを作成してロードします。

于 2012-12-03T20:33:05.893 に答える
4

後で再ロードできるように、プログラム全体の状態を保存したいとします。これは Pickle の完璧な使用例です。使用例に問題はまったくありません。ただし、globals() 名前空間をピクルし、sys、pygame、および pickle を除外するアプローチは不安定です。通常のパターンは、ピクルする 1 つのセッション オブジェクトを持つことです。

また、ピクルスにする方法について混乱があるかもしれないと思います:

  1. オブジェクトをピクルすると、そのメンバー変数によって参照されるすべてのオブジェクトが自動的にピクル/ピクル解除されます。これは良いことです
  2. pickle がオブジェクトをシリアル化できない場合、pickle に、pickle しないオブジェクトのカスタムgetstate およびsetstateメソッドを記述して、そのオブジェクトを保存および復元する方法を指示する必要があります。セッション間で明らかに異なるファイルハンドルなどのデバイスを再度開くなどのことを行うためのカスタムget/setstate関数があります
  3. バイナリ シリアライゼーションを行う必要がある場合は、オブジェクトを文字列にキャストする必要はありません。そのオブジェクトのget/setstateメソッドでバイナリ シリアライゼーション プロトコルを使用するだけです (つまり、プロトコル 1 を使用します) 。

最終的に、コードは次のようになります。

session = None
import pickle
def startsession():
    globals session
    session = pickle.Unpickler(sessionfilehandle('r')).load()
    if session is None: session = Session() 

def savesession(filename=None):
    globals session
    pickle.Pickler.dump(session,sessionfilehandle('w'))

class Session(object):
    def __init__(self):
        self.someobject=NewObject1()
        #.... plus whole object tree representing the whole game
        self.somedevicehandlethatcannotbepickled=GetDeviceHandle1()  #for example
    def __getstate__(self):
        odict = self.__dict__.copy()
        del odict['somedevicehandlethatcannotbepickled'] #don't pickle this
        return odict
    def __setstate__(self, dict):
        self.__dict__.update(dict)
        self.somedevicehandlethatcannotbepickled=GetDeviceHandle1()
于 2012-12-03T21:52:03.093 に答える
2

あなたのコメントから、あなたがやろうとしていることの難しい部分は、ユーザーにライブインタプリタを提供し、その状態を保存することであるように思えます

では、そのライブインタープリターをサブプロセスとして実行するのはどうでしょうか。スクリプトに公開したいオブジェクトモデルからの情報は、明示的に(multiprocessing共有メモリまたは何らかのメッセージパッシングAPIのいずれかによって)公開します。

そうすれば、自分のインタプリタの完全な状態を保存する必要はありません。これは非常に難しいか不可能です。通常の方法でデータモデルを保存すると、サブインタープリターを内部ではなく外部からフリーズできます。

これは明らかにあなたがやろうとしていることよりもはるかに複雑ですが、単純なことは実際にはうまくいくとは思いません。たとえば、ユーザーがあなたのコードを使ったライブインタプリタを持っている場合、ピクルスコードも含めて何でもモンキーパッチを適用できます。その後はどうなりますか?保存および復元できるものについて、いくつかの制限を定義する必要があります。それらの制限が十分に広い場合は、外部から行う必要があると思います。

一方、コメントで述べたように、両方scipy(またはEnthoughtに付属するいくつかの関連プロジェクト)とipython、限られたユースケースの保存と復元の機能があります。これにより、少なくとも調査するコードが提供されますが、それらのユースケースはそうではない可能性があります。あなたと同じです。

于 2012-12-03T20:52:01.960 に答える
2

選択できないオブジェクトタイプをすべて知っている場合は、この質問への回答のコードが役立つ可能性があります。「Pythonオブジェクトを再帰的にdir()して、特定のタイプまたは特定の値を持つ値を検索する」-ピクルスできないオブジェクトタイプはすべて知っていたが、データ構造内のどこにあるのかわからなかった同様の状況。このコードを使用してそれらを見つけ、それらを別のものに置き換えてから、選択を解除するときに同様のコードを使用してそれらを元に戻すことができます。

于 2012-12-03T21:01:50.263 に答える
2

これを行うには、Python でほとんど何でもシリアル化できるdillを使用します。Dill には、コードが失敗したときに pickle 化が失敗する原因を理解するのに役立ついくつかの優れたツールもあります。また、objgraphは、テスト スイートに対する非常に便利な補完物でもあります。

>>> import dill
>>> # blah blah blah... your session code here
>>> dill.dump_session('pygame.pkl')
>>>
>>> # and if you get a pickling error, use dill's tools to discover a workaround
>>> dill.detect.badobjects(your_bad_object, depth=1)
>>>
>>> # visualize the references in your bad objects
>>> objgraph.show_refs(your_bad_object, filename='pygame_bad_object.png')
于 2013-10-14T13:13:55.340 に答える