Python çoklu işleme PicklingError: <type 'function'> seçilemiyor


244

Daha basit bir örnekle hatayı yeniden oluşturamadığım için üzgünüm ve kodumu göndermek için çok karmaşık. Programı normal Python yerine IPython kabuğunda çalıştırırsam işler iyi gider.

Bu sorunla ilgili daha önceki bazı notlara baktım. Bunların hepsine, bir sınıf işlevi içinde tanımlanan işlevi çağırmak için havuz kullanılması neden olmuştur. Ama benim için durum böyle değil.

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib64/python2.7/multiprocessing/pool.py", line 313, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Herhangi bir yardımı takdir ediyorum.

Güncelleme : Turşu yaptığım fonksiyon modülün en üst seviyesinde tanımlanır. İç içe geçmiş işlev içeren bir işlevi çağırmasına rağmen. yani, iç içe bir işlevi olan f()çağrıları g()çağırır ve ben çağırıyorum . , , Tüm üst düzeyinde tanımlanır. Bu desenle daha basit bir örnek denedim ve yine de çalışıyor.h()i()pool.apply_async(f)f()g()h()


3
Üst düzey / kabul edilen cevap iyidir, ancak kodunuzu yeniden yapılandırmanız gerektiği anlamına gelebilir, bu da acı verici olabilir. Bu sorunu olan herkes için dillve kullanarak ek cevapları okumak için tavsiye ederim pathos. Ancak, vtkobjects ile çalışırken çözümlerden herhangi bir şans yok :( Herkes paralel işleme vtkPolyData python kodu çalıştırmayı başardı?
Chris

Yanıtlar:


306

İşte turşu yapılabileceklerin bir listesi . Özellikle, işlevler yalnızca bir modülün en üst düzeyinde tanımlandıklarında seçilebilir.

Bu kod parçası:

import multiprocessing as mp

class Foo():
    @staticmethod
    def work(self):
        pass

if __name__ == '__main__':   
    pool = mp.Pool()
    foo = Foo()
    pool.apply_async(foo.work)
    pool.close()
    pool.join()

yayınladığınızla neredeyse aynı hata verir:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 315, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Sorun, poolyöntemlerin tümünün mp.SimpleQueuegörevleri çalışan işlemlere iletmek için a kullanmasıdır . Modülün en üst düzeyinde tanımlanmadığı için, içinden geçen her şey mp.SimpleQueueseçilebilir olmalı ve seçilemez olmalıdır foo.work.

En üst düzeyde bir işlev tanımlayarak düzeltilebilir, bu çağrı foo.work():

def work(foo):
    foo.work()

pool.apply_async(work,args=(foo,))

En üst düzeyde tanımlandığından ve fooseçilebilir olduğundan seçilebilir olduğuna dikkat edin .Foofoo.__dict__


2
Cevabın için teşekkürler. Sorumu güncelledim. Nedeni bu kadar düşünmüyorum
Vendetta

7
Bir PicklingError almak için sıraya alınamayan bir şey koymak gerekir. İşlev veya argümanları olabilir. Sorun hakkında daha fazla bilgi edinmek için, programınızın bir kopyasını almanızı ve ayrıştırmaya başlamanızı sağlayarak, her seferinde sorunun tekrarlanıp tekrarlanmadığını görmek için programı yeniden çalıştırmanızı öneririm. Gerçekten basitleştiğinde, sorunu kendiniz keşfetmiş olacaksınız veya burada gönderebileceğiniz bir şeye sahip olacaksınız.
unutbu

3
Ayrıca: bir modülün en üst düzeyinde bir işlev tanımlarsanız, ancak dekore edilmişse, referans dekoratörün çıktısı olacaktır ve yine de bu hatayı alırsınız.
bobpoekert

5
Sadece 5 yıl geç kaldım, ama sadece bununla karşılaştım. Bu "üst düzey" daha anlamıyla normalden daha alınması gereken çıkıyor: fonksiyon tanım önce zorundadır geliyor bana havuzun başlatma (yani pool = Pool()hat burada ). Bunu beklemiyordum ve OP'nin probleminin devam etmesinin nedeni bu olabilir.
Andras Deak

4
Özellikle, işlevler yalnızca bir modülün en üst düzeyinde tanımlandıklarında seçilebilir. functool.partialÜst düzey bir işleve başvurmanın sonucu, başka bir işlevin içinde tanımlansa da, turşu yapılabilir gibi görünüyor.
user1071847

96

Bunun pathos.multiprocesssingyerine kullanardım multiprocessing. pathos.multiprocessingbir çatal multiprocessingolduğunu kullanımları dill. dillPython'da neredeyse her şeyi serileştirebilir, böylece paralel olarak çok daha fazlasını gönderebilirsiniz. pathosSınıf yöntemleri için ihtiyaç olarak çatal da birden fazla argüman fonksiyonları ile doğrudan çalışma kapasitesine sahiptir.

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> p = Pool(4)
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> p.map(t.plus, x, y)
[4, 6, 8, 10]
>>> 
>>> class Foo(object):
...   @staticmethod
...   def work(self, x):
...     return x+1
... 
>>> f = Foo()
>>> p.apipe(f.work, f, 100)
<processing.pool.ApplyResult object at 0x10504f8d0>
>>> res = _
>>> res.get()
101

Buradan pathos(ve isterseniz dill) buraya gidin: https://github.com/uqfoundation


5
bir tedavi çalıştı. Diğer herkes için, her iki kütüphaneyi de şu şekilde yükledim: sudo pip install git+https://github.com/uqfoundation/dill.git@mastervesudo pip install git+https://github.com/uqfoundation/pathos.git@master
Alexander McFarlane

5
@AlexanderMcFarlane sudo(özellikle github gibi harici kaynaklardan) ile python paketleri yüklemezdim . Bunun yerine, çalıştırmanızı tavsiye ederim:pip install --user git+...
Chris

Kullanmak pip install pathosüzücü bir şekilde çalışmıyor ve bu mesajı veriyor:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

11
pip install pathosşimdi çalışıyor ve pathospython 3 uyumlu.
Mike McKerns

3
@DanielGoldfarb: multiprocessbir çatal multiprocessingnerede dillyerini aldı picklebu kadar yani özde kodda çeşitli yerlerde ... ama. pathosbazı ek API katmanları sağlar multiprocessve ayrıca ek arka uçlar içerir. Ama bu onun özü.
Mike McKerns

29

Diğerlerinin söylediği gibi multiprocessing, Python nesnelerini sadece işlenebilen işçi işlemlerine aktarabilir. Kodunuzu unutbu tarafından açıklandığı gibi yeniden düzenleyemezseniz, dillaşağıda gösterildiği gibi veri (özellikle kod verileri) aktarmak için genişletilmiş dekapaj / unpickling özelliklerini kullanabilirsiniz .

Bu çözüm, yalnızca şu dillkütüphanelerin yüklenmesini gerektirir ve başka hiçbir kütüphaneye gerek yoktur pathos:

import os
from multiprocessing import Pool

import dill


def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    return fun(*args)


def apply_async(pool, fun, args):
    payload = dill.dumps((fun, args))
    return pool.apply_async(run_dill_encoded, (payload,))


if __name__ == "__main__":

    pool = Pool(processes=5)

    # asyn execution of lambda
    jobs = []
    for i in range(10):
        job = apply_async(pool, lambda a, b: (a, b, a * b), (i, i + 1))
        jobs.append(job)

    for job in jobs:
        print job.get()
    print

    # async execution of static method

    class O(object):

        @staticmethod
        def calc():
            return os.getpid()

    jobs = []
    for i in range(10):
        job = apply_async(pool, O.calc, ())
        jobs.append(job)

    for job in jobs:
        print job.get()

6
Ben dillve pathosyazarım… ve haklı olduğunuzda, cevabımda olduğu gibi kullanmak çok daha hoş, daha temiz ve daha esnek değil pathosmi? Ya da belki biraz önyargılıyım ...
Mike McKerns

4
pathosYazım sırasındaki durumun farkında değildim ve cevaba çok yakın bir çözüm sunmak istedim. Artık çözümünüzü gördüğüme göre bu yolun devam ettiğini kabul ediyorum.
rocksportrocker

Çözümünüzü okudum ve gibiydi, Doh… I didn't even think of doing it like that. Yani bu çok güzeldi.
Mike McKerns

4
Gönderdiğiniz için teşekkürler, bu yaklaşımı alınamayan argümanları dilling / undilling için kullandım: stackoverflow.com/questions/27883574/…
jazzblue

@rocksportrocker. Bu örneği okuyorum ve neden açık fordöngü olduğunu anlayamıyorum . Normalde paralel rutin bir liste almak ve döngü olmadan bir liste döndürmek görecektim.
user1700890

20

Ben de üzerinde profiler kullanmaya çalışarak mükemmel çalışan bir kod parçası üzerinde tam olarak bu hata çıktısı üretebilir bulduk.

Bunun Windows'ta olduğunu (çatalın biraz daha zarif olduğu) unutmayın.

Koşuyordum:

python -m profile -o output.pstats <script> 

Ve profili çıkarmanın hatayı kaldırdığını ve profili yerleştirmenin geri yüklediğini buldu. Eskiden işe yarayan kodu biliyordum çünkü beni de batty sürüyordu. Bir şey pool.py güncellediğini görmek için kontrol ediyordum ... sonra batan bir duygu vardı ve profil oluşturma ortadan kaldırıldı ve bu oldu.

Başkasının içine girmesi durumunda arşivler için buraya gönderiyoruz.


3
VAY, bahsettiğiniz için teşekkürler! Son bir saat kadar beni deli ediyordu; Her şeyi çok basit bir örneğe kadar denedim - hiçbir şey işe yaramadı. Ama aynı zamanda benim toplu iş dosya üzerinden çalışan profiler vardı :(
tim

1
Oh, yeterince teşekkür edemem. Beklenmedik bir şekilde, bu çok aptalca geliyor. Bence bu dokümanlarda belirtilmelidir. Tüm sahip olduğum bir ithalat pdb deyimi ve sadece bir ile basit bir üst düzey işlevi pass'turşu' değildi.
0xc0de

10

Bu sorun ile geldiğinde multiprocessingbasit bir çözeltisinden anahtarına olduğu Pooliçin ThreadPool. Bu, içe aktarma dışında bir kod değişikliği olmadan yapılabilir.

from multiprocessing.pool import ThreadPool as Pool

Bu, ThreadPool'un belleği yeni bir işlem oluşturmak yerine ana iş parçacığıyla paylaşması nedeniyle çalışır - bu, dekapajın gerekli olmadığı anlamına gelir.

Bu yöntemin dezavantajı, python'un iş parçacığı kullanımıyla en büyük dil olmamasıdır; iş parçacığını güvende tutmak için Global Tercüman Kilidi adı verilen bir şey kullanır ve bu da bazı kullanım durumlarını yavaşlatabilir. Ancak, öncelikle diğer sistemlerle (HTTP komutlarını çalıştırma, bir veritabanıyla konuşma, dosya sistemlerine yazma) etkileşim kuruyorsanız, kodunuz büyük olasılıkla CPU tarafından bağlanmaz ve isabet almaz. Aslında, HTTP / HTTPS karşılaştırmaları yazarken, burada kullanılan dişli modelin daha az ek yük ve gecikmeye sahip olduğunu buldum, çünkü yeni işlemler oluşturmanın ek yükü yeni iş parçacıkları oluşturmak için ek yükten çok daha yüksek.

Python kullanıcı alanında bir sürü şey işliyorsanız, bu en iyi yöntem olmayabilir.


2
Ama sonra sadece bir CPU (en azından GIL kullanan normal Python sürümleriyle ) kullanıyorsunuz, bu da amacı yeniyor.
Endre Her ikisi de

Bu gerçekten amacın ne olduğuna bağlıdır. Global Tercüman Kilidi, bir kerede yalnızca bir örneğin python kodunu çalıştırabileceği anlamına gelir, ancak GIL'i büyük ölçüde engelleyen eylemler için (dosya sistemi erişimi, büyük veya birden fazla dosya indirme, harici kod çalıştırma) sorun çıkmaz. Bazı durumlarda, yeni işlemlerin açılmasının (iş parçacığı yerine) ek yükü GIL ek yükünden ağır basar.
tedivm

Bu doğru, teşekkürler. Yine de cevaba bir uyarı eklemek isteyebilirsiniz. İşleme gücünün arttığı günümüzde, çoğunlukla daha güçlü CPU çekirdekleri yerine daha çok CPU çekirdeği biçimindedir, çok çekirdekten tek çekirdekli yürütmeye geçmek oldukça önemli bir yan etkidir.
Her ikisi de

İyi bir nokta- Cevabı daha ayrıntılı olarak güncelledim. Gerçi, çok iş parçacıklı iş parçacığına geçişin sadece tek bir çekirdekte python işlevi görmediğini belirtmek isterim.
tedivm

4

Bu çözüm sadece dereotu kurulmasını gerektirir ve pathos olarak başka kütüphaneler gerektirmez

def apply_packed_function_for_map((dumped_function, item, args, kwargs),):
    """
    Unpack dumped function as target function and call it with arguments.

    :param (dumped_function, item, args, kwargs):
        a tuple of dumped function and its arguments
    :return:
        result of target function
    """
    target_function = dill.loads(dumped_function)
    res = target_function(item, *args, **kwargs)
    return res


def pack_function_for_map(target_function, items, *args, **kwargs):
    """
    Pack function and arguments to object that can be sent from one
    multiprocessing.Process to another. The main problem is:
        «multiprocessing.Pool.map*» or «apply*»
        cannot use class methods or closures.
    It solves this problem with «dill».
    It works with target function as argument, dumps it («with dill»)
    and returns dumped function with arguments of target function.
    For more performance we dump only target function itself
    and don't dump its arguments.
    How to use (pseudo-code):

        ~>>> import multiprocessing
        ~>>> images = [...]
        ~>>> pool = multiprocessing.Pool(100500)
        ~>>> features = pool.map(
        ~...     *pack_function_for_map(
        ~...         super(Extractor, self).extract_features,
        ~...         images,
        ~...         type='png'
        ~...         **options,
        ~...     )
        ~... )
        ~>>>

    :param target_function:
        function, that you want to execute like  target_function(item, *args, **kwargs).
    :param items:
        list of items for map
    :param args:
        positional arguments for target_function(item, *args, **kwargs)
    :param kwargs:
        named arguments for target_function(item, *args, **kwargs)
    :return: tuple(function_wrapper, dumped_items)
        It returs a tuple with
            * function wrapper, that unpack and call target function;
            * list of packed target function and its' arguments.
    """
    dumped_function = dill.dumps(target_function)
    dumped_items = [(dumped_function, item, args, kwargs) for item in items]
    return apply_packed_function_for_map, dumped_items

Aynı zamanda numpy dizileri için de çalışır.


2
Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Bu hata, zaman uyumsuz işe geçirilen model nesnesinin içinde dahili bir işlev varsa da gelir.

Bu nedenle , geçirilen model nesnelerinin dahili işlevleri olmadığını kontrol ettiğinizden emin olun . (Bizim durumumuzda , belirli bir alanı izlemek için modelin içindeki django-model-utilsFieldTracker() işlevini kullanıyorduk ). İşte bağlantı alakalı GitHub konuya.


0

@Rocksportrocker çözümü üzerine inşa edildiğinde, sonuçları gönderirken ve RECVing yaparken dereotu yapmak mantıklı olacaktır.

import dill
import itertools
def run_dill_encoded(payload):
    fun, args = dill.loads(payload)
    res = fun(*args)
    res = dill.dumps(res)
    return res

def dill_map_async(pool, fun, args_list,
                   as_tuple=True,
                   **kw):
    if as_tuple:
        args_list = ((x,) for x in args_list)

    it = itertools.izip(
        itertools.cycle([fun]),
        args_list)
    it = itertools.imap(dill.dumps, it)
    return pool.map_async(run_dill_encoded, it, **kw)

if __name__ == '__main__':
    import multiprocessing as mp
    import sys,os
    p = mp.Pool(4)
    res = dill_map_async(p, lambda x:[sys.stdout.write('%s\n'%os.getpid()),x][-1],
                  [lambda x:x+1]*10,)
    res = res.get(timeout=100)
    res = map(dill.loads,res)
    print(res)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.