【问题标题】:Python Multiprocessing Pool.map Causes Error in __new__Python Multiprocessing Pool.map 在 __new__ 中导致错误
【发布时间】:2020-01-20 17:43:17
【问题描述】:

在下面的简单 Python 3 示例中,我们使用 multiproessing 模块来处理列表 friends,是什么导致了错误:

TypeError: new() 缺少 1 个必需的位置参数:'name'

简单运行不会出错

tom = Friend(tom)
say_hello(tom)

有什么想法可以解决这个问题吗?谢谢!

代码

import multiprocessing

def say_hello(friend):
    print('Hello', friend.name, '!')

class Friend:
    friends = {}
    def __new__(cls, name):
        if name not in cls.friends:
            cls.friends[name] = super(Friend, cls).__new__(cls)
        return cls.friends[name]

    def __init__(self, name):
        self.name = name

jack = Friend('jack')
ryan = Friend('ryan')
friends = [jack, ryan]
multiprocessing.Pool(2).map(say_hello, friends)

完整的错误跟踪

Traceback (most recent call last):
  File "/Users/nyxynyx/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/Users/nyxynyx/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/nyxynyx/opt/anaconda3/lib/python3.7/multiprocessing/pool.py", line 110, in worker
    task = get()
  File "/Users/nyxynyx/opt/anaconda3/lib/python3.7/multiprocessing/queues.py", line 354, in get
    return _ForkingPickler.loads(res)
TypeError: __new__() missing 1 required positional argument: 'name'

【问题讨论】:

  • @Darkonaut 感谢您接受这个。更新了原始问题。

标签: python python-3.x multiprocessing python-multiprocessing


【解决方案1】:

在 unpickling 期间出现错误,因为 name 在 unpickling 期间重新创建对象时没有准备好传递。

它已经可以通过以下方式复制:

pickle.loads(pickle.dumps(jack))

Traceback (most recent call last): 
  ...
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-9-239857af5731>", line 1, in <module>
    pickle.loads(pickle.dumps(jack))
TypeError: __new__() missing 1 required positional argument: 'name'

解决方案是实现object.__getnewargs__()object.__getnewargs_ex__()

object.getnewargs()

此方法的目的与 getnewargs_ex() 类似,但 仅支持位置参数。它必须返回一个元组 参数 args 将传递给 new() 方法 解酸。

如果定义了 getnewargs_ex(),则不会调用

getnewargs()。

3.6 版更改:在 Python 3.6 之前,getnewargs() 被调用 而不是协议 2 和 3 中的 getnewargs_ex()。

所以在你的情况下:

def __getnewargs__(self):
    return self.name,

【讨论】:

    【解决方案2】:

    定义 __reduce__ 以使 Friend 类的对象可腌制(可序列化)以发送到其他进程。

    import multiprocessing
    
    def say_hello(friend):
        print('Hello', friend.name, '!')
    
    class Friend:
        friends = {}
        def __new__(cls, name):
            if name in cls.friends:
                return cls.friends[name]
            else:
                return super(Friend, cls).__new__(cls)
    
        def __init__(self, name):
            self.name = name
    
        def __reduce__(self):
            return self.__class__, (self.name,)
    
    jack = Friend('jack')
    ryan = Friend('ryan')
    friends = [jack, ryan]
    multiprocessing.Pool(2).map(say_hello, friends)
    

    【讨论】:

    • "虽然功能强大,但直接在类中实现 __reduce__() 很容易出错。因此,类设计者应该使用高级接口(即 __getnewargs_ex__()、__getstate__() 和 __setstate__( )) 只要有可能。” docs
    • @Darkonaut 你是对的。虽然 Saim Raza 和您的解决方案似乎都解决了这个问题中描述的问题,但我的实际代码中的问题(简化了这个最小的完整可验证问题)源于过时的 __reduce__() 定义!
    • 虽然文档指出__reduce__ 容易出错,但使用起来更直观,并且正如文档中提到的“是唯一的选择或导致更有效的酸洗或两者兼而有之”。在某些情况下。在当前情况下,通过__reduce__ 酸洗模拟 Friend() 调用。此外,使用像__setstate__ 这样的方法还有其他复杂性。例如,仅定义 __setstate__ 在 unpickling 期间永远不会调用 __init__
    猜你喜欢
    • 1970-01-01
    • 2023-02-23
    • 2013-08-21
    • 1970-01-01
    • 2021-11-02
    • 2020-06-02
    • 1970-01-01
    • 2014-11-16
    • 1970-01-01
    相关资源
    最近更新 更多