web-dev-qa-db-fra.com

Remplacement du threading python.Thread.run ()

Étant donné la documentation Python pour Thread.run():

Vous pouvez remplacer cette méthode dans une sous-classe. La méthode standard run () appelle l’objet appelable transmis au constructeur de l’objet comme argument cible, le cas échéant, avec des arguments séquentiels et des arguments mot-clé extraits des arguments args et kwargs, respectivement.

J'ai construit le code suivant:

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

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

Mais lorsque je l'exécute, je reçois le message d'erreur suivant:

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)

Il semble que je manque quelque chose d’évident, mais les divers exemples que j’ai vus fonctionnent avec cette méthodologie. En fin de compte, j'essaie simplement de passer la chaîne et le dictionnaire dans le thread, si le constructeur n'est pas la bonne manière, mais plutôt de créer une nouvelle fonction pour définir les valeurs avant de démarrer le thread, je suis ouvert à cela. 

Des suggestions sur la meilleure façon d'accomplir cela?

36
Gavin M. Roy

Vous n'avez vraiment pas besoin de sous-classer Thread. La seule raison pour laquelle l'API prend en charge cette fonctionnalité est de le rendre plus confortable pour les personnes venant de Java, où c'est le seul moyen de le faire sainement.

Le modèle que nous vous recommandons d’utiliser consiste à transmettre une méthode au constructeur Thread et à appeler simplement .start().

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

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

Voici un exemple de passage d'arguments à l'aide de threading sans extension __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()

Et voici un exemple utilisant multitraitement:

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()
8
Gavin M. Roy

La documentation de threading.Thread peut sembler impliquer que tous les arguments de position et de mot clé non utilisés sont passés à l'exécution. Ils ne sont pas.

Tous les arguments de position et le mot clé kwargs supplémentaires sont effectivement interceptés par la méthode threading.Thread.__init__ par défaut, mais ils sont UNIQUEMENT transmis à une méthode spécifiée à l'aide du mot clé target=. Ils ne sont PAS transmis à la méthode run().

En fait, la Documentation de thread) at indique clairement que est la méthode par défaut run() qui appelle la méthode target= fournie avec les arguments capturés et kwargs:

"Vous pouvez remplacer cette méthode dans une sous-classe La méthode standard run () Invoque l’objet appelable passé à Le constructeur de l’objet en tant qu’argument cible arguments de mots-clés extraits des arguments args et kwargs, respectivement. "

6
pjkundert

Si vous souhaitez conserver votre approche orientée objet et également exécuter des arguments, vous pouvez procéder comme suit:

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()

Comme mentionné ci-dessus, cela pourrait également être fait avec 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()

L'avantage de cette méthode par rapport à une approche purement fonctionnelle est qu'elle vous permet de conserver votre autre code existant orienté objet. Le seul changement est de ne pas avoir la sous-classe Thread, ce qui ne devrait pas être un gros problème, car selon la documentation threading.Thread

écrase uniquement les méthodes init () et run () de cette classe

Si vous surchargiez Thread pour pouvoir accéder à l'objet thread à partir de votre sous-classe, nous vous recommandons d'utiliser simplement threading.currentThread () à partir de votre objet. De cette façon, vous segmentez l'espace de noms du fil du vôtre, et selon le "Zen of Python" de Tim Peters:

Les espaces de noms sont une bonne idée: faisons-en plus!

2
ihm

Afin de dissiper une certaine confusion quant au fait qu'une méthode run() surchargée prenne des arguments supplémentaires, voici une implémentation d'une méthode run() surchargée qui fait ce que la méthode héritée de threading.Thread fait.

Notez, ceci juste pour voir comment on remplacerait run(); ce n'est pas censé être un exemple significatif. Si vous souhaitez simplement appeler une fonction cible à l'aide d'arguments séquentiels et/ou de mots clés, il n'est pas nécessaire de créer une sous-classe. cela a été souligné par exemple dans la réponse de Jerub à cette question.

Le code suivant prend en charge Python v2 et v3.

Bien que l'accès aux noms d'attributs mutilés dans le code Python 2 soit particulièrement moche, je ne connais pas d'autre moyen d'accéder à ces attributs (laissez-moi savoir si vous en connaissez un ...):

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()

Il imprime (testé avec Python 2.6, 2.7 et 3.4 sous Windows 7):

func(): a=1, k=2
2
Andreas Maier

Comme l'argument constructeur du thread cible est appelable, appliquez __call__ comme méthode d'exécution.

class Worker(object):
  def __call__(self, name, age):
    print('name, age : ',name,age)

if __== '__main__':

  thread = Thread(target=Worker(), args=('bob','50'))
  thread.start()

sortie:

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

Vous définissez la méthode run pour accepter 3 arguments, mais vous l'appelez avec un seul argument (python l'appelle avec la référence à l'objet).

Vous devez transmettre les arguments à exécuter à la place de __init__.

Ou demandez à la méthode __init__ d'accepter les arguments à la place.

0
Vasil