2012-02-17 24 views
13

ben sonradan bir çoklu işlem havuzuna geçecek bir fonksiyonu üzerine bir dekoratör kullanmak istiyorum başarısız olur. Ancak, kod başarısız olur "PicklingError: turşu edilemez: nitelik arama __builtin__ .function başarısız oldu". Burada neden başarısız olduğunu anlamıyorum. Bunun basit bir şey olduğundan eminim, ama bunu bulamıyorum. Aşağıda minimal "çalışan" bir örnek. Bunun işe yaraması için functools işlevini kullanmanın yeterli olacağını düşündüm. Ben fonksiyon dekorasyon dışarı yorum yaparsanızPython dekoratör

, bu bir sorun olmadan çalışır. Burada yanlış anlamadığım multiprocessing hakkında nedir? Bu işi yapmanın bir yolu var mı?

Düzenleme: Bir çağrılabilir sınıf dekoratör ve bir işlev dekoratör hem ekledikten sonra, çıkıyor bu beklendiği gibi çalıştığını fonksiyon dekoratör. Kalibre sınıf dekoratör başarısız olmaya devam ediyor. Kaldırabileceğiniz sınıf versiyonu hakkında ne söyleniyor?

import random 
import multiprocessing 
import functools 

class my_decorator_class(object): 
    def __init__(self, target): 
     self.target = target 
     try: 
      functools.update_wrapper(self, target) 
     except: 
      pass 

    def __call__(self, elements): 
     f = [] 
     for element in elements: 
      f.append(self.target([element])[0]) 
     return f 

def my_decorator_function(target): 
    @functools.wraps(target) 
    def inner(elements): 
     f = [] 
     for element in elements: 
      f.append(target([element])[0]) 
     return f 
    return inner 

@my_decorator_function 
def my_func(elements): 
    f = [] 
    for element in elements: 
     f.append(sum(element)) 
    return f 

if __name__ == '__main__': 
    elements = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)] 
    pool = multiprocessing.Pool(processes=4) 
    results = [pool.apply_async(my_func, ([e],)) for e in elements] 
    pool.close() 
    f = [r.get()[0] for r in results] 
    print(f) 
+0

Bu gönderi, süslemeli süslenmiş nesnelerin zor olduğunu gösteriyor: http://gael-varoquaux.info/blog/?p = 120 – Daenyth

+0

Evet, bu sayfayı da buldum. Bu yüzden 'functools' sarıcısını ekledim. Ama hiçbir fark yaratmıyor gibi görünüyor. Bunun neden başarısız olduğunu anlamak için ne olduğunu anlamadığımı itiraf ediyorum. – agarrett

cevap

8

Sorun şu ki; salamura nelerin listesi için buraya bakınız:

  • my_decorator_class bir örneği denilen my_func

    :

    http://docs.python.org/library/pickle.html#what-can-be-pickled-and-unpickled

    my_func dekapaj aşağıdaki bileşenleri salamura edilmesi gereken Bu iyi. Pickle, sınıfın adını depolayacak ve __dict__ içeriğini seçecektir. Unpickling yaparken, sınıfı bulmak için adı kullanır, ardından bir örnek oluşturur ve __dict__ içindekiler doldurur. Ancak, __dict__ içindekiler bir sorun teşkil ...

  • Bu kadar iyi değil my_func.target

    depolanır orijinal my_func örneği. Üst düzeydeki bir işlevdir ve normalde bunlar turlanabilir. Turşu, işlevin adını saklar. Bununla birlikte problem, "my_func" isminin artık eklenmemiş işleve bağlı olmaması, dekore edilmiş işleve bağlı olmasıdır. Bu, pikle nesneyi yeniden oluşturmak için düzenlenmemiş işlevi arayamayacak anlamına gelir. Ne yazık ki, turşu o turşu çalışıyor bu nesne her zaman adı ana .my_func altında bulunabilir bilmek herhangi bir şekilde bulunmamaktadır.

Böyle değiştirebilirsiniz ve işe yarayacak: Sen sınıfı değil yaptığında dekoratör işlevi çalıştığını gözlemledim

import random 
import multiprocessing 
import functools 

class my_decorator(object): 
    def __init__(self, target): 
     self.target = target 
     try: 
      functools.update_wrapper(self, target) 
     except: 
      pass 

    def __call__(self, candidates, args): 
     f = [] 
     for candidate in candidates: 
      f.append(self.target([candidate], args)[0]) 
     return f 

def old_my_func(candidates, args): 
    f = [] 
    for c in candidates: 
     f.append(sum(c)) 
    return f 

my_func = my_decorator(old_my_func) 

if __name__ == '__main__': 
    candidates = [[random.randint(0, 9) for _ in range(5)] for _ in range(10)] 
    pool = multiprocessing.Pool(processes=4) 
    results = [pool.apply_async(my_func, ([c], {})) for c in candidates] 
    pool.close() 
    f = [r.get()[0] for r in results] 
    print(f) 

. Bunun, functools.wraps'un dekore edilmiş işlevi değiştirdiği için, sardığı işlevin adını ve diğer özelliklerini içermesi gerektiğine inanıyorum. Turşu modülünün anlayabildiği kadarıyla, normal bir üst düzey fonksiyondan ayırt edilemez, bu yüzden adını saklayarak onu turşar. Unpickling üzerine, isim dekore işlevine bağlanır, böylece her şey çalışır.

+0

Tamam. Yani eğer bu şeyleri pes etmek istiyorsam ve eğer dekoratörüm olarak callable bir sınıf kullanmak istersem, o zaman '@' dekorasyon yaklaşımını kullanamayacağım. Dersi hazırlıyormuşum gibi kullanmam gerekecek. Bu doğru mu? – agarrett

+0

Doğru olduğuna inanıyorum. Alternatif olarak, sadece dekore edilmiş işleve giden, önemsiz, süslenmemiş, üst düzey bir işlev yaratarak, onu seçmekten kaçınabilirsiniz. – Weeble

+0

Çok açık. Çok teşekkürler. – agarrett

İlgili konular