2014-10-08 13 views
6

Python işlevlerini (kod + kapanışları) serileştirmeye çalışıyorum ve daha sonra bunları yeniden eski durumuna getiriyorum. Bu yazının en altındaki kodu kullanıyorum.Kapaması Python'da döngüleri olan bir işlevi geri yükleyebilir miyim?

Bu çok esnek bir koddur. İşlevinizden kapatılması işlevler olsa bile

def f1(arg): 
    def f2(): 
     print arg 

    def f3(): 
     print arg 
     f2() 

    return f3 

x = SerialiseFunction(f1(stuff)) # a string 
save(x) # save it somewhere 

# later, possibly in a different process 

x = load() # get it from somewhere 
newf2 = DeserialiseFunction(x) 
newf2() # prints value of "stuff" twice 

Bu çağrılar çalışacaktır: İç fonksiyonların seri ve deserialisation sağlar ve böyle kendi bağlamı ihtiyacı olanlar olarak kapanışları olan fonksiyonlar eski haline getirilmeye kapanışlarında ve benzeri işlevlerde (kapanışların, daha fazla işlev içeren ve benzer işlevlere sahip kapaklar içeren işlevler içeren bir grafiğe sahibiz).

Bununla birlikte, bu grafikler döngüleri içerebilir çıkıyor:

def g1(): 
    def g2(): 
     g2() 
    return g2() 

g = g1() 

I (g ile) g2 'nin kapatma bakarsak, İçinde g2 görebilir:

>>> g 
<function g2 at 0x952033c> 
>>> g.func_closure[0].cell_contents 
<function g2 at 0x952033c> 

Bu fonksiyonun işlevini kaldırmaya çalıştığımda ciddi bir soruna neden olur, çünkü her şey değişmezdir. aşağıdaki gibi newg2closure oluşturulur

newg2 = types.FunctionType(g2code, globals, closure=newg2closure) 

: Ne yapmak gerek işlevini newg2 yapmaktır

newg2closure = (make_cell(newg2),) 

elbette yapılamaz hangi; Her kod satırı diğerine dayanır. Hücreler değişmezdir, tupler değişmezdir, fonksiyon tipleri değişmezdir.

Peki, ne bulmaya çalışıyorum, yukarıda newg2 yaratmak için bir yol var mı? Bu nesnenin kendi kapanış grafiğinde yer aldığı bir işlev türü nesnesi oluşturabilmem için bir yol var mı?

Python 2.7 kullanıyorum (App Engine'deyim, bu yüzden Python 3'e gidemiyorum).

Başvuru için

, benim seri fonksiyonları:

def SerialiseFunction(aFunction): 
    if not aFunction or not isinstance(c, types.FunctionType): 
     raise Exception ("First argument required, must be a function") 

    def MarshalClosureValues(aClosure): 
     logging.debug(repr(aClosure)) 
     lmarshalledClosureValues = [] 
     if aClosure: 
      lclosureValues = [lcell.cell_contents for lcell in aClosure] 
      lmarshalledClosureValues = [ 
       [marshal.dumps(litem.func_code), MarshalClosureValues(litem.func_closure)] if hasattr(litem, "func_code") 
       else [marshal.dumps(litem)] 
       for litem in lclosureValues 
      ] 
     return lmarshalledClosureValues 

    lmarshalledFunc = marshal.dumps(aFunction.func_code) 
    lmarshalledClosureValues = MarshalClosureValues(aFunction.func_closure) 
    lmoduleName = aFunction.__module__ 

    lcombined = (lmarshalledFunc, lmarshalledClosureValues, lmoduleName) 

    retval = marshal.dumps(lcombined) 

    return retval 


def DeserialiseFunction(aSerialisedFunction): 
    lmarshalledFunc, lmarshalledClosureValues, lmoduleName = marshal.loads(aSerialisedFunction) 

    lglobals = sys.modules[lmoduleName].__dict__ 

    def make_cell(value): 
     return (lambda x: lambda: x)(value).func_closure[0] 

    def UnmarshalClosureValues(aMarshalledClosureValues): 
     lclosure = None 
     if aMarshalledClosureValues: 
      lclosureValues = [ 
        marshal.loads(item[0]) if len(item) == 1 
        else types.FunctionType(marshal.loads(item[0]), lglobals, closure=UnmarshalClosureValues(item[1])) 
        for item in aMarshalledClosureValues if len(item) >= 1 and len(item) <= 2 
       ] 
      lclosure = tuple([make_cell(lvalue) for lvalue in lclosureValues]) 
     return lclosure 

    lfunctionCode = marshal.loads(lmarshalledFunc) 
    lclosure = UnmarshalClosureValues(lmarshalledClosureValues) 
    lfunction = types.FunctionType(lfunctionCode, lglobals, closure=lclosure) 
    return lfunction 
+1

Bu çok ilginç bir soru ve yanıtlayacağımı bilmiyorum. C api'ye gitmek hiç yardımcı olur mu? İnsanların bu tür kodları çalıştırdığımdan önce bu şekilde kapanışlarını gördüm http://pythondoeswhat.blogspot.com/2012/11/back-porting-non-local.html Bir segfault yüzden bu linkin ne kadar okunaklı olduğundan emin değilim. –

+0

Python için C kodunu okuyorum, bu çalışmayı o seviyede yapabilirsiniz - bütün nesneler değişebilir - ama oraya giremiyorum. Ben appengine'ı hedefliyorum, bu yüzden yaptığım her şeyin saf python olması gerektiğini düşünüyorum. –

+0

Not: Kendi nesnelerinizi hücre yerine koyamazsınız. Denedim :-) . Types.FunctionType() c uygulanması dikkatlice bir tuple hücreleri geçti, ördek yazarak izin verilen kontrol eder. –

cevap

3

Burada çalışan bir yöntemdir bu.

Bu değişmez nesneleri düzeltemezsiniz, ancak yapabileceğiniz şey, dairesel referanslar yerine proxy işlevlerini yapıştırır ve bunların, gerçek bir sözlükte gerçek işlevi görmesini sağlamasıdır.

1: Serileştirirken, gördüğünüz tüm işlevleri takip edin. Aynı olanı tekrar görürseniz, yeniden göndermeyin, bunun yerine bir sentinel değeri serileştirin.

Bir dizi kullandım:

lfunctionHashes = set() 

ve her tefrika öğe için, aksi takdirde sette ise, yani eğer bir nöbetçi ile gitmek kontrol düzgün seti ve Mareşal ekleyin:

lhash = hash(litem) 
if lhash in lfunctionHashes: 
    lmarshalledClosureValues.append([lhash, None]) 
else: 
    lfunctionHashes.add(lhash) 
    lmarshalledClosureValues.append([lhash, marshal.dumps(litem.func_code), MarshalClosureValues(litem.func_closure, lfullIndex), litem.__module__]) 

2: deserial sırasında işlevini

gfunctions = {} 

: deserialising zaman functionhash küresel dicti tutmak isation, bir işlevi kaldırdığınız her zaman, bunu fonksiyonlara ekler. Burada, madde (hash kodu, closurevalues, modulename) 'dir:

lfunction = types.FunctionType(marshal.loads(item[1]), globals, closure=UnmarshalClosureValues(item[2])) 
gfunctions[item[0]] = lfunction 

Ve sen, bir fonksiyon için gözcü değeri rastlamak zaman fonksiyonunun karma geçen, vekil kullanın:

lfunction = make_proxy(item[0]) 

İşte proxy. incelemek olabilir, bazı yerlerde yerine mareşal turşu kullandım

  • :

    def make_proxy(f_hash): 
        def f_proxy(*args, **kwargs): 
         global gfunctions 
         f = lfunctions[f_hash] 
         f(*args, **kwargs) 
    
        return f_proxy 
    

    Ben de birkaç başka değişiklikler yapmak zorunda kalmıştım: Bu karma dayalı gerçek fonksiyonunu arar Bu daha fazla

  • Seri hale getirme işlevinin yanı sıra kod ve kapama işlevinin modül adını ekliyorum, böylece serileştirme sırasında işlev için doğru globals'ı arayabilirim. 1, basit bir değer, 2 için tam tefrika fonksiyonu İşte

için proxy, 4 ihtiyacı olan bir işlev için yeni dolu: deserialisation olarak

  • , başlığın uzunluğu ne deserialising ediyoruz anlatır kodu.

    lfunctions = {} 
    
    def DeserialiseFunction(aSerialisedFunction): 
        lmarshalledFunc, lmarshalledClosureValues, lmoduleName = pickle.loads(aSerialisedFunction) 
    
        lglobals = sys.modules[lmoduleName].__dict__ 
        lglobals["lfunctions"] = lfunctions 
    
        def make_proxy(f_hash): 
         def f_proxy(*args, **kwargs): 
          global lfunctions 
          f = lfunctions[f_hash] 
          f(*args, **kwargs) 
    
         return f_proxy 
    
        def make_cell(value): 
         return (lambda x: lambda: x)(value).func_closure[0] 
    
        def UnmarshalClosureValues(aMarshalledClosureValues): 
         global lfunctions 
    
         lclosure = None 
         if aMarshalledClosureValues: 
          lclosureValues = [] 
          for item in aMarshalledClosureValues: 
           ltype = len(item) 
           if ltype == 1: 
            lclosureValues.append(pickle.loads(item[0])) 
           elif ltype == 2: 
            lfunction = make_proxy(item[0]) 
            lclosureValues.append(lfunction) 
           elif ltype == 4: 
            lfuncglobals = sys.modules[item[3]].__dict__ 
            lfuncglobals["lfunctions"] = lfunctions 
            lfunction = types.FunctionType(marshal.loads(item[1]), lfuncglobals, closure=UnmarshalClosureValues(item[2])) 
            lfunctions[item[0]] = lfunction 
            lclosureValues.append(lfunction) 
          lclosure = tuple([make_cell(lvalue) for lvalue in lclosureValues]) 
         return lclosure 
    
        lfunctionCode = marshal.loads(lmarshalledFunc) 
        lclosure = UnmarshalClosureValues(lmarshalledClosureValues) 
        lfunction = types.FunctionType(lfunctionCode, lglobals, closure=lclosure) 
        return lfunction 
    
    def SerialiseFunction(aFunction): 
        if not aFunction or not hasattr(aFunction, "func_code"): 
         raise Exception ("First argument required, must be a function") 
    
        lfunctionHashes = set() 
    
        def MarshalClosureValues(aClosure, aParentIndices = []): 
         lmarshalledClosureValues = [] 
         if aClosure: 
          lclosureValues = [lcell.cell_contents for lcell in aClosure] 
    
          lmarshalledClosureValues = [] 
          for index, litem in enumerate(lclosureValues): 
           lfullIndex = list(aParentIndices) 
           lfullIndex.append(index) 
    
           if isinstance(litem, types.FunctionType): 
            lhash = hash(litem) 
            if lhash in lfunctionHashes: 
             lmarshalledClosureValues.append([lhash, None]) 
            else: 
             lfunctionHashes.add(lhash) 
             lmarshalledClosureValues.append([lhash, marshal.dumps(litem.func_code), MarshalClosureValues(litem.func_closure, lfullIndex), litem.__module__]) 
           else: 
            lmarshalledClosureValues.append([pickle.dumps(litem)]) 
    
        lmarshalledFunc = marshal.dumps(aFunction.func_code) 
        lmarshalledClosureValues = MarshalClosureValues(aFunction.func_closure) 
        lmoduleName = aFunction.__module__ 
    
        lcombined = (lmarshalledFunc, lmarshalledClosureValues, lmoduleName) 
    
        retval = pickle.dumps(lcombined) 
    
        return retval 
    
  • +0

    İşlevi içine yerleştirdiğiniz make_proxy işlevinin içinde değişken olmayan bir yerel (ör. 1 öğeli liste) oluşturuyorsanız, genel bir aramaya ihtiyacınız yoktur. Bu önemli olursa arama zamanlarını kaydetmelidir. İyi düşünmek olsa da. – Veedrac

    İlgili konular