Переопределение Python Threading.Thread.run () - PullRequest
36 голосов
/ 19 марта 2009

С учетом документации Python для 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)

Кажется, мне не хватает чего-то очевидного, но различные примеры, которые я видел, работают с этой методологией. В конечном итоге я пытаюсь просто передать строку и словарь в поток, если конструктор не правильный путь, а вместо этого создать новую функцию для установки значений до запуска потока, я открыт для этого.

Какие-либо предложения о том, как лучше всего это сделать?

Ответы [ 7 ]

67 голосов
/ 19 марта 2009

Вам действительно не нужно создавать подкласс 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()
8 голосов
/ 12 февраля 2011

Вот пример передачи аргументов с использованием потоков и без расширения __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()
7 голосов
/ 03 февраля 2011

Документация threading.Thread может показаться, что любые неиспользуемые позиционные и ключевые аргументы передаются в запуск. Это не так.

Любые дополнительные позиционные аргументы и ключевое слово kwargs действительно перехватываются по умолчанию методом threading.Thread.__init__, но они ТОЛЬКО передаются методу, указанному с помощью ключевого слова target=. Они НЕ передаются методу run().

Фактически, документация Threading at проясняет, что является методом run() по умолчанию, который вызывает предоставленный метод target= с захваченными аргументами и kwargs :

"Вы можете переопределить этот метод в подкласс. Стандартный метод run () вызывает вызываемый объект, переданный конструктор объекта как цель аргумент, если таковой имеется, с последовательным и ключевые аргументы взяты из аргументов и аргументы kwargs соответственно. "

2 голосов
/ 24 апреля 2015

Чтобы устранить некоторую путаницу относительно того, принимает ли переопределенный метод run() дополнительные аргументы, ниже приведена реализация переопределенного метода run(), который делает то, что делает метод, унаследованный от threading.Thread.

Обратите внимание, это просто чтобы посмотреть, как можно переопределить run(); это не должно быть значимым примером. Если все, что вы хотите сделать, это вызвать целевую функцию с последовательными и / или ключевыми аргументами, то необязательно иметь подкласс; на это было указано, например, в ответ Иерува на этот вопрос.

Следующий код поддерживает 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()

Он печатает (протестировано с Python 2.6, 2.7 и 3.4 на Windows 7):

func(): a=1, k=2
2 голосов
/ 13 ноября 2013

Если вы хотите сохранить свой объектно-ориентированный подход, а также иметь аргументы запуска, вы можете сделать следующее:

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, чтобы вы могли получить доступ к объекту thread из вашего подкласса, то я бы рекомендовал просто использовать threading.currentThread () из вашего объекта. Таким образом, вы сегментируете пространство имен потока из своего собственного, и в соответствии с «Zen of Python» Тимом Питерсом:

Пространства имен - одна из отличных идей - давайте сделаем больше таких!

0 голосов
/ 17 января 2019

Поскольку целевой аргумент конструктора потока является вызываемым, примените __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')
0 голосов
/ 19 марта 2009

Вы определяете метод run для приема 3 аргументов, но вызываете его с одним аргументом (python вызывает его со ссылкой на объект).

Вам нужно передать аргументы для запуска вместо __init__.

Или заставьте метод __init__ принимать аргументы взамен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...