Python đa xử lý PicklingError: Không thể chọn <type 'function'>


243

Tôi xin lỗi vì tôi không thể tái tạo lỗi bằng một ví dụ đơn giản hơn và mã của tôi quá phức tạp để đăng. Nếu tôi chạy chương trình trong IPython shell thay vì Python thông thường, mọi thứ sẽ hoạt động tốt.

Tôi đã tra cứu một số lưu ý trước đây về vấn đề này. Tất cả đều do sử dụng pool để gọi hàm được định nghĩa trong hàm class. Nhưng đây không phải là trường hợp của tôi.

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

Tôi sẽ đánh giá cao sự giúp đỡ nào.

Cập nhật : Hàm tôi chọn được xác định ở cấp cao nhất của mô-đun. Mặc dù nó gọi một hàm chứa hàm lồng nhau. tức là f()gọi các cuộc g()gọi h()có chức năng lồng nhau i()và tôi đang gọi pool.apply_async(f). f(), g(), h()Tất cả đều được xác định ở cấp cao nhất. Tôi đã thử ví dụ đơn giản hơn với mẫu này và nó hoạt động mặc dù.


3
Câu trả lời cấp cao nhất / được chấp nhận là tốt, nhưng điều đó có thể có nghĩa là bạn cần cấu trúc lại mã của mình, điều này có thể gây đau đớn. Tôi muốn giới thiệu cho bất cứ ai có vấn đề này cũng đọc các câu trả lời bổ sung sử dụng dillpathos. Tuy nhiên, tôi không gặp may với bất kỳ giải pháp nào khi làm việc với vtkobjects :( Bất cứ ai cũng có thể chạy mã python trong xử lý song song vtkPolyData?
Chris

Câu trả lời:


305

Dưới đây là danh sách những gì có thể được ngâm . Cụ thể, các chức năng chỉ có thể chọn được nếu chúng được xác định ở cấp cao nhất của mô-đun.

Đoạn mã này:

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()

mang lại một lỗi gần giống với lỗi bạn đã đăng:

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

Vấn đề là pool tất cả các phương thức đều sử dụng a mp.SimpleQueueđể truyền các tác vụ cho các tiến trình worker. Tất cả mọi thứ đi qua mp.SimpleQueueđều phải được chọn và foo.workkhông thể chọn được vì nó không được xác định ở cấp cao nhất của mô-đun.

Nó có thể được sửa bằng cách định nghĩa một hàm ở cấp cao nhất, gọi foo.work() :

def work(foo):
    foo.work()

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

Lưu ý rằng foocó thể chọn, vì Foođược xác định ở cấp cao nhất và foo.__dict__có thể chọn được.


2
Cảm ơn vì đã trả lời. Tôi cập nhật câu hỏi của tôi. Tuy nhiên, tôi không hiểu đó là nguyên nhân
Vendetta

7
Để có được PicklingError, một cái gì đó phải được đặt vào Hàng đợi không thể chọn được. Nó có thể là hàm hoặc đối số của nó. Để tìm hiểu thêm về vấn đề, tôi khuyên bạn nên tạo một bản sao của chương trình của bạn và bắt đầu giảm bớt nó, làm cho nó đơn giản và đơn giản hơn, mỗi lần chạy lại chương trình để xem vấn đề còn tồn tại không. Khi nó trở nên thực sự đơn giản, bạn sẽ tự mình phát hiện ra vấn đề hoặc sẽ có một cái gì đó mà bạn có thể đăng ở đây.
unutbu

3
Ngoài ra: nếu bạn xác định một chức năng ở cấp cao nhất của mô-đun, nhưng nó được trang trí, thì tham chiếu sẽ là đầu ra của trình trang trí và dù sao bạn cũng sẽ gặp lỗi này.
bobpoekert

5
Chỉ trễ 5 năm, nhưng tôi mới gặp phải chuyện này. Nó chỉ ra rằng "cấp cao nhất" phải được thực hiện theo nghĩa đen nhiều hơn bình thường: đối với tôi, định nghĩa hàm phải đi trước khởi tạo của nhóm (tức là pool = Pool()dòng ở đây ). Tôi không mong đợi điều đó và đây có thể là lý do khiến vấn đề của OP vẫn tồn tại.
Andras Deak

4
Cụ thể, các chức năng chỉ có thể chọn được nếu chúng được xác định ở cấp cao nhất của mô-đun. Dường như kết quả của việc áp dụng functool.partialcho một chức năng cấp cao nhất cũng có thể được xử lý, ngay cả khi nó được xác định bên trong một chức năng khác.
dùng1071847

96

Tôi sẽ sử dụng pathos.multiprocesssing, thay vì multiprocessing. pathos.multiprocessinglà một ngã ba multiprocessingsử dụng dill. dillcó thể tuần tự hóa hầu hết mọi thứ trong python, vì vậy bạn có thể gửi nhiều hơn xung quanh song song. Các pathosngã ba cũng có khả năng làm việc trực tiếp với nhiều chức năng lập luận, như bạn cần cho các phương pháp lớp.

>>> 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

Nhận pathos(và nếu bạn thích, dill) tại đây: https://github.com/uqfoundation


5
làm việc một điều trị. Đối với bất kỳ ai khác, tôi đã cài đặt cả hai thư viện thông qua: sudo pip install git+https://github.com/uqfoundation/dill.git@mastersudo pip install git+https://github.com/uqfoundation/pathos.git@master
Alexander McFarlane

5
@AlexanderMcFarlane Tôi sẽ không cài đặt các gói python với sudo(từ các nguồn bên ngoài như github). Thay vào đó, tôi khuyên bạn nên chạy:pip install --user git+...
Chris

Chỉ sử dụng pip install pathoskhông hoạt động buồn và đưa ra thông báo này:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

11
pip install pathosBây giờ hoạt động, và pathoslà python 3 tương thích.
Mike McKerns 21/07/2016

3
@DanielGoldfarb: multiprocesslà một ngã ba của multiprocessingnơi dillđã thay thế pickleở một số nơi trong mã ... nhưng về cơ bản, đó là nó. pathoscung cấp một số lớp API bổ sung trên multiprocessvà cũng có các phụ trợ bổ sung. Nhưng, đó là ý chính của nó.
Mike McKern

29

Như những người khác đã nói multiprocessingchỉ có thể chuyển các đối tượng Python sang các tiến trình worker có thể được xử lý. Nếu bạn không thể sắp xếp lại mã của mình như được mô tả bởi unutbu, bạn có thể sử dụng dillcác khả năng chọn lọc / tháo gỡ mở rộng để truyền dữ liệu (đặc biệt là dữ liệu mã) như tôi trình bày bên dưới.

Giải pháp này chỉ yêu cầu cài đặt dill và không có thư viện nào khác như 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
Tôi là tác giả dillpathostác giả và trong khi bạn nói đúng, không phải nó đẹp hơn và sạch hơn và linh hoạt hơn để sử dụng pathosnhư trong câu trả lời của tôi sao? Hoặc có thể tôi là một người thiên vị nhỏ bé
Mike McKerns

4
Tôi đã không nhận thức được tình trạng pathostại thời điểm viết và muốn trình bày một giải pháp rất gần với câu trả lời. Bây giờ tôi đã thấy giải pháp của bạn, tôi đồng ý rằng đây là cách để đi.
rockportrocker

Tôi đọc giải pháp của bạn và giống như, Doh… I didn't even think of doing it like that. Thật tuyệt vời.
Mike McKerns

4
Cảm ơn cho đăng tải, tôi đã sử dụng phương pháp này cho Dilling / lập luận undilling mà không thể được ngâm: stackoverflow.com/questions/27883574/...
jazzblue

@rocksportrocker. Tôi đang đọc ví dụ này và không thể hiểu tại sao có forvòng lặp rõ ràng . Tôi thường sẽ thấy thói quen song song lấy một danh sách và trả về một danh sách không có vòng lặp.
dùng1700890

20

Tôi đã thấy rằng tôi cũng có thể tạo chính xác đầu ra lỗi đó trên một đoạn mã hoạt động hoàn hảo bằng cách cố gắng sử dụng trình lược tả trên nó.

Lưu ý rằng đây là trên Windows (trong đó việc rèn là kém thanh lịch hơn một chút).

Tôi đã đang chạy:

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

Và thấy rằng loại bỏ hồ sơ đã loại bỏ lỗi và đặt hồ sơ khôi phục nó. Đã lái xe cho tôi quá nhiều vì tôi biết mã được sử dụng để làm việc. Tôi đang kiểm tra xem có thứ gì đã cập nhật pool.py không ... sau đó có cảm giác chìm và loại bỏ hồ sơ và đó là nó.

Đăng ở đây để lưu trữ trong trường hợp có ai khác chạy vào đó.


3
WOW, cảm ơn vì đã đề cập! Nó đã cho tôi ăn hạt dẻ trong khoảng một giờ qua; Tôi đã thử mọi thứ cho đến một ví dụ rất đơn giản - dường như không có gì hoạt động. Nhưng tôi cũng có trình tạo hồ sơ chạy qua batchfile của mình :(
tim

1
Ồ, không thể cảm ơn bạn đủ. Điều này nghe có vẻ rất ngớ ngẩn, vì nó quá bất ngờ. Tôi nghĩ rằng nó nên được đề cập trong các tài liệu. Tất cả những gì tôi có là một câu lệnh pdb nhập khẩu và một hàm cấp cao đơn giản chỉ với một pass'không thể chọn được'.
0xc0de

10

Khi vấn đề này xuất hiện với multiprocessingmột giải pháp đơn giản là chuyển từ Poolsang ThreadPool. Điều này có thể được thực hiện mà không thay đổi mã nào ngoài việc nhập-

from multiprocessing.pool import ThreadPool as Pool

Điều này hoạt động vì ThreadPool chia sẻ bộ nhớ với luồng chính, thay vì tạo một quy trình mới - điều này có nghĩa là không cần phải tẩy.

Nhược điểm của phương pháp này là python không phải là ngôn ngữ lớn nhất với việc xử lý các luồng - nó sử dụng một thứ gọi là Khóa phiên dịch toàn cầu để giữ an toàn cho luồng, có thể làm chậm một số trường hợp sử dụng ở đây. Tuy nhiên, nếu bạn chủ yếu tương tác với các hệ thống khác (chạy các lệnh HTTP, nói chuyện với cơ sở dữ liệu, ghi vào hệ thống tệp) thì mã của bạn có thể không bị ràng buộc bởi CPU và sẽ không bị ảnh hưởng nhiều. Trong thực tế, tôi đã tìm thấy khi viết các điểm chuẩn HTTP / HTTPS rằng mô hình luồng được sử dụng ở đây có ít chi phí và độ trễ hơn, vì chi phí từ việc tạo các quy trình mới cao hơn nhiều so với chi phí để tạo các luồng mới.

Vì vậy, nếu bạn đang xử lý một tấn công cụ trong không gian người dùng python thì đây có thể không phải là phương pháp tốt nhất.


2
Nhưng sau đó, bạn chỉ sử dụng một CPU (ít nhất là với các phiên bản Python thông thường sử dụng GIL ), loại này đánh bại mục đích.
Endre Cả

Điều đó thực sự phụ thuộc vào mục đích là gì. Khóa phiên dịch toàn cầu có nghĩa là chỉ một phiên bản tại một thời điểm có thể chạy mã python, nhưng đối với các hành động chặn nhiều (truy cập hệ thống tệp, tải xuống lớn hoặc nhiều tệp, chạy mã bên ngoài) thì GIL cuối cùng không phải là vấn đề. Trong một số trường hợp, chi phí từ việc mở các quy trình mới (chứ không phải là các luồng) vượt xa chi phí GIL.
tedivm

Đó là sự thật, cảm ơn. Tuy nhiên, bạn có thể muốn bao gồm một cảnh báo trong câu trả lời. Ngày nay, khi công suất xử lý tăng chủ yếu ở dạng lõi CPU mạnh hơn là mạnh hơn, việc chuyển đổi từ đa lõi sang thực thi lõi đơn là một tác dụng phụ khá quan trọng.
Endre Cả hai

Điểm hay - Tôi đã cập nhật câu trả lời với nhiều chi tiết hơn. Tôi muốn chỉ ra mặc dù việc chuyển sang xử lý đa luồng không làm cho python chỉ hoạt động trên một lõi đơn.
tedivm

4

Giải pháp này chỉ yêu cầu cài đặt thì là và không có thư viện nào khác làm pathos

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

Nó cũng hoạt động cho các mảng numpy.


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

Lỗi này cũng sẽ xuất hiện nếu bạn có bất kỳ chức năng sẵn có nào bên trong đối tượng mô hình được truyền cho công việc không đồng bộ.

Vì vậy, hãy đảm bảo kiểm tra các đối tượng mô hình được thông qua không có chức năng sẵn có. (Trong trường hợp của chúng tôi, chúng tôi đã sử dụng FieldTracker()chức năng của django-model-utils bên trong mô hình để theo dõi một trường nhất định). Đây là liên kết đến vấn đề GitHub có liên quan.


0

Dựa trên giải pháp @rocksportrocker, sẽ rất hợp lý khi gửi và NHẬN kết quả.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.