Подтвердить что ты не робот

Переопределение потоков 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)

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

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

4b9b3361

Ответ 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')