【问题标题】:Overriding python threading.Thread.run()覆盖python threading.Thread.run()
【发布时间】:2009-03-19 03:26:56
【问题描述】:

鉴于Python documentation 对应于Thread.run()

您可以在子类中覆盖此方法。标准 run() 方法调用作为目标参数传递给对象构造函数的可调用对象,如果有的话,顺序参数和关键字参数分别取自 args 和 kwargs 参数。

我已经构建了以下代码:

class DestinationThread(threading.Thread):
    def run(self, name, config):
        print 'In thread'

thread = DestinationThread(args = (destination_name, destination_config))
thread.start()

但是当我执行它时,我收到以下错误:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", line 522, in __bootstrap_inner
    self.run()
TypeError: run() takes exactly 3 arguments (1 given)

我似乎遗漏了一些明显的东西,但我看到的各种例子都适用于这种方法。最终,我试图将字符串和字典传递到线程中,如果构造函数不是正确的方法,而是在启动线程之前创建一个新函数来设置值,我对此持开放态度。

关于如何最好地完成此任务的任何建议?

【问题讨论】:

  • 像我这样的读者可能会发现this帖子直接回答了PO的问题,而选择的答案提供了更好的实用选择。

标签: python multithreading


【解决方案1】:

你真的不需要子类化 Thread。 API 支持这一点的唯一原因是为了让来自 Java 的人更舒服,这是唯一能做到这一点的方法。

我们建议您使用的模式是将方法传递给 Thread 构造函数,然后调用 .start()

 def myfunc(arg1, arg2):
     print 'In thread'
     print 'args are', arg1, arg2

 thread = Thread(target=myfunc, args=(destination_name, destination_config))
 thread.start()

【讨论】:

  • 这是我丢失的关键信息,这就是我接受它的原因,但就我而言,我实际上是在扩展类,添加所需的函数以将数据从线程中提取出来。跨度>
  • 一千次是的。我必须向 Java 开发人员解释这一点。
  • 你甚至不需要 args=(arg1,arg2)。只需执行 target=functools.partial(myfunc, arg1, arg2)。
  • 是的。我可以避免使用提供的 API,而是导入不同的不必要的 API。没错。
  • 是的,但是,这仍然没有回答关于子类化的实际问题。
【解决方案2】:

这是一个使用线程传递参数而不是扩展 __init__ 的示例:

import threading

class Example(threading.Thread):

    def run(self):
        print '%s from %s' % (self._Thread__kwargs['example'],
                              self.name)

example = Example(kwargs={'example': 'Hello World'})
example.start()
example.join()

这是一个使用多重处理的例子:

import multiprocessing

class Example(multiprocessing.Process):

    def run(self):
        print '%s from %s' % (self._kwargs['example'],
                              self.name)

example = Example(kwargs={'example': 'Hello World'})
example.start()
example.join()

【讨论】:

  • 在 Python 3 中,Thread 的属性从 private 更改为 protected,即 self.__kwargs(在 Thread 类中)变为 self._kwargs。这允许稍微不那么难看的访问,因为不再使用名称 mangling。但是,如果要同时支持 Python 2 和 3,则此更改要求对 Python 版本敏感。
  • 还有一个:我不明白如何实现一个重写的run()方法没有直接访问线程属性_target_args和@987654329 @(使用它们的 v3 名称):必须使用它们的值(参见其他响应中引用的 run() 的描述),并且它们没有访问器方法。
  • 还有一个:此答案中显示的 run() 方法不会调用某人将在构造函数中指定的目标方法,因此与其基类记录的行为不兼容。
  • 这段代码实际上做了什么?文档说“标准的 run() 方法调用作为目标参数传递给对象构造函数的可调用对象”你不这样做,所以你的线程只打印那行吗?
【解决方案3】:

threading.Thread 的文档似乎暗示任何未使用的位置和关键字参数都被传递给运行。他们不是。

任何额外的位置参数和关键字kwargs 确实被默认的threading.Thread.__init__ 方法捕获,但它们只能传递给使用target= 关键字指定的方法。它们不会传递给run() 方法。

事实上,Threading documentation at 清楚地表明它默认的run() 方法,它使用捕获的args 和kwargs 调用提供的target= 方法:

"你可以在一个 子类。标准的 run() 方法 调用传递给的可调用对象 对象的构造函数作为目标 参数,如果有的话,用顺序和 取自 args 的关键字参数 和 kwargs 参数。”

【讨论】:

  • 我同意。原始问题的其他一些答案会出错(那些显示run() 方法除了self 之外还有参数)。
  • 这其实是正确的答案。在您知道这一点之前,文档很微妙且容易被误解。这意味着子类应该要覆盖run()。实际上,想要传递参数的子类应该在对Thread.__init__ 的调用中设置target=self.myRun。即使阅读此页面上的前几个答案,我也很挣扎,直到我崩溃并阅读 threading.py
【解决方案4】:

如果您想保留面向对象的方法并且还需要运行参数,您可以执行以下操作:

import threading
class Destination:
    def run(self, name, config):
        print 'In thread'

destination = Destination()
thread = threading.Thread(target=destination.run,
    args=(destination_name, destination_config))
thread.start()

如上所述,也可以使用partial完成

from functools import partial
import threading
class Destination:
    def run(self, name, config):
        print 'In thread'

destination = Destination()
thread = threading.Thread(target=partial(
    destination.run, destination_name, destination_config))
thread.start()

与纯函数式方法相比,这样做的优势在于它可以让您保持其他现有的面向对象代码不变。唯一的变化是让它不继承 Thread,这应该没什么大不了的,因为根据 threading.Thread 文档:

只覆盖这个类的init()和run()方法

如果您要覆盖 Thread 以便可以从子类中访问线程对象,那么我建议您只在对象中使用 threading.currentThread()。这样,您就可以根据 Tim Peters 的“Python 之禅”将线程的命名空间与您自己的命名空间分割开来:

命名空间是一个很棒的主意——让我们做更多这样的事!

【讨论】:

    【解决方案5】:

    为了解决一些关于被覆盖的run() 方法是否需要额外参数的困惑,这里是一个被覆盖的run() 方法的实现,它执行从threading.Thread 继承的方法所做的事情。

    注意,这只是为了看看如何覆盖run();这并不是一个有意义的例子。如果您只想使用顺序和/或关键字参数调用目标函数,则不需要子类;这已被指出,例如在Jerub's answer 这个问题。

    以下代码同时支持 Python v2 和 v3。

    虽然在 Python 2 代码中访问损坏的属性名称特别难看,但我不知道访问这些属性的另一种方法(如果你知道的话,请告诉我......):

    import sys
    import threading
    
    class DestinationThread(threading.Thread):
    
        def run(self):
            if sys.version_info[0] == 2:
                self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else: # assuming v3
                self._target(*self._args, **self._kwargs)
    
    def func(a, k):
        print("func(): a=%s, k=%s" % (a, k))
    
    thread = DestinationThread(target=func, args=(1,), kwargs={"k": 2})
    thread.start()
    thread.join()
    

    它可以打印(在 Windows 7 上使用 Python 2.6、2.7 和 3.4 测试):

    func(): a=1, k=2
    

    【讨论】:

      【解决方案6】:

      由于 Thread 构造函数参数目标是可调用的,因此将 __call__ 应用为运行方法

      class Worker(object):
        def __call__(self, name, age):
          print('name, age : ',name,age)
      
      if __name__ == '__main__':
      
        thread = Thread(target=Worker(), args=('bob','50'))
        thread.start()
      

      输出:

      ('name, age : ', 'bob', '50')
      

      【讨论】:

        【解决方案7】:

        如果真的需要子类,可以这样使用

        >>> class DestinationThread(threading.Thread):
            def __init__(self,name, config):
                super().__init__()
                self.name = name
                self.config = config
            def run(self):
                print("In thread")
        

        您可以通过 self.name & self.config 访问名称和配置

        >>> thread = DestinationThread(destination_name, destination_config)
        >>> thread.start()
        

        它会给你接受的输出。

        输出:

        In thread
        

        或者你可以使用,目标参数。

        thread = Thread(target=some_func, args=[arg1, arg2])
        thread.start()
        

        【讨论】:

          【解决方案8】:

          你定义 run 方法接受 3 个参数,但你用一个参数调用它(python 用对对象的引用调用它)。

          你需要传递参数来运行而不是__init__

          或者让__init__ 方法接受参数。

          【讨论】:

          • 你确定这是对的吗?我认为您不能将任何参数传递给 start()。
          • 你不能,我是说跑。谢谢。
          • 不,这不起作用。你不能将参数传递给 start(),关键是你不能直接调用 run()。
          猜你喜欢
          • 2014-01-10
          • 1970-01-01
          • 1970-01-01
          • 2019-04-05
          • 2020-06-16
          • 2012-02-01
          • 2011-10-17
          • 2016-01-14
          • 1970-01-01
          相关资源
          最近更新 更多