Переопределение потоков python.Thread.run()
Учитывая документацию 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)
Кажется, я упускаю что-то очевидное, но различные примеры, которые я видел, работают с этой методологией. В конечном счете, я пытаюсь просто передать строку и словарь в поток, если конструктор не правильный путь, а вместо этого создать новую функцию для установки значений до запуска потока, я открыт для этого.
Любые предложения о том, как лучше всего добиться этого?
Ответы
Ответ 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()
Ответ 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()
И вот пример использования mutliprocessing:
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()
Ответ 3
Документация threading.Thread
может показаться подразумевающей, что любые неиспользуемые аргументы positional и keyword передаются для запуска. Это не так.
Любые дополнительные позиционные аргументы и ключевое слово kwargs
действительно попадают в ловушку по умолчанию threading.Thread.__init__
, но они ТОЛЬКО передаются методу, указанному с помощью ключевого слова target=
. Они НЕ передаются методу run()
.
Фактически, Threading documentation дает понять, что это метод run()
по умолчанию, который вызывает предоставленный метод target=
с захваченными args и kwargs
:
"Вы можете переопределить этот метод в подкласс. Стандартный метод run() вызывает вызываемый объект, переданный конструктор объектов в качестве целевого аргумент, если таковой имеется, с последовательными и аргументы ключевых слов, взятые из аргументов и аргументы kwargs, соответственно."
Ответ 4
Чтобы устранить некоторую путаницу в том, что переопределенный метод run()
принимает дополнительные аргументы, вот реализация переопределенного метода run()
, который выполняет то, что делает метод, унаследованный от threading.Thread
.
Обратите внимание, что это просто посмотреть, как можно переопределить run()
; он не должен быть значимым примером. Если все, что вы хотите сделать, это вызов целевой функции с последовательными и/или ключевыми аргументами, нет необходимости иметь подкласс; это было указано, например, в Jerub ответьте на этот вопрос.
Следующий код поддерживает как 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
Ответ 5
Если вы хотите сохранить свой объектно-ориентированный подход и также запустить аргументы, вы можете сделать следующее:
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() из вашего объекта. Таким образом, вы отделяете пространство имен потоков от своих собственных и по "Zen of Python" от Тима Петерса:
Пространства имен - одна хорошая идея - позвольте сделать больше из них!
Ответ 6
Вы определяете метод run для приема 3 аргументов, но вы вызываете его одним аргументом (python вызывает его со ссылкой на объект).
Вам нужно передать аргументы для запуска вместо __init__
.
Или вместо метода __init__
принять аргументы.
Ответ 7
Поскольку цель аргумента конструктора __call__
является вызываемой, примените __call__
в качестве метода run
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')