6
votes

Comment les générateurs de Python savent qui appelle?

Cette question me fait sortir mes cheveux.

Si je fais: xxx

et l'appelez de mille threads, comment le générateur sait-il quoi Envoyer ensuite pour chaque fil? Chaque fois que je l'appelle, le générateur enregistre-t-il une table avec le comptoir et la référence de l'appelant ou quelque chose comme ça?

C'est bizarre.

S'il vous plaît, clarifiez mon esprit sur celui-là.


0 commentaires

3 Réponses :


2
votes

Une fonction comme celle-ci, lorsque cela est appelé, retournera un objet générateur. Si vous avez des threads distincts appelant suivant () sur le même objet générateur, ils interfèrent avec eux. C'est-à-dire que 5 threads appelant suivant () 10 fois chacun obtiendra 50 rendements différents.

Si deux threads créent chacun un générateur en appelant mygén () dans le fil, ils auront des objets de générateur distincts.

Un générateur est un objet et son état sera stocké en mémoire, de sorte que deux threads qui créent chacun un myGen () se réfèrent à des objets distincts. Ce ne serait pas différent de deux threads créant un objet à partir d'une classe , ils auront chacun un objet différent, même si la classe est la même.

Si vous arrivez à cela à partir d'une base C, ceci est pas la même chose qu'une fonction avec statique variables. L'état est maintenu dans un objet, non statique dans les variables contenues dans la fonction.


2 commentaires

Vous n'avez pas vraiment répondu à ma question. Je ne veux pas savoir ce qui va arriver, mais comment ça se passe. Le générateur conserve-t-il une certaine valeur en mémoire lorsqu'un fil crée un générateur?


@PatrickBassut Un objet générateur a un état comme n'importe quel autre objet, alors oui, son état sera stocké en mémoire.



6
votes

myGen n'a pas à se souvenir de rien. Chaque appel à myGen () renvoie un administrateur indépendant. Ces itérables, d'autre part, ont un état: chaque fois que suivant () est appelé sur un, il passe au bon endroit dans le code générateur - lorsqu'un rendement est rencontrée, le contrôle est remis à l'appelant. La mise en œuvre effective est plutôt désordonnée, mais en principe, vous pouvez imaginer qu'un tel itérateur stocke les variables locales, le bytecode et la position actuelle dans le bytecode (A.K.A. Pointeur d'instructions). Il n'y a rien de spécial sur les threads ici.


2 commentaires

Ouais, les fils étaient juste pour illustrer le problème. Considérant que les générateurs pourraient donner le mauvais aspect de la concurrence (ou quelque chose de plus magique de noir que cela) aux débutants python.


@Patrickbassut: Eh bien, vous pouvez simuler Coroutines avec eux, et avec les coroutine que vous pouvez faire threads verts .



1
votes

Cela pourrait être plus clair si vous regardez cela de cette façon. Au lieu de: xxx

Utilisez: xxx

alors vous pouvez voir que myGen () n'est appelé qu'une seule fois, et c'est crée un nouvel objet, et c'est cet objet qui est itéré. Vous pouvez créer deux séquences dans le même thread, si vous le souhaitez: xxx

Ceci imprimera 0, 0, 1, 1.

Vous pouvez accéder à Le même itérateur de deux threads si vous le souhaitez, stockez simplement l'objet générateur dans un global: xxx

thread 1: xxx p> thread 2: xxx

Cela causerait probablement toutes sortes de ravages. : -)


4 commentaires

Les générateurs sont un peu étranges. Vous pouvez leur assigner aux variables, mais au moment où vous les utilisez réellement, il commence à générer des valeurs de manière «à la demande». Si vous avez attribué l'emplacement de la mémoire d'une fonction, cela est totalement facile à comprendre. Mais autant que je sache, vous ne le faites pas là-bas (vous appelez réellement la fonction dans GEN_OBJ = MYGEN ()). Wow!


Lorsque vous avancez au niveau suivant de Python-Fu sérieux, vous pouvez transmettre des pointeurs vers les méthodes de __Next __ () __ () en tant que rappels GUI. :-)


La plupart du temps, l'instruction "DEF" crée un seul objet de fonction à partir de votre code, qui est exécuté lorsque vous appelez la fonction par nom. Si le code contient "RENDU", Toutefois, le DEF crée des objets de fonction deux : celui qui est appelé lorsque vous appelez le nom de la fonction ne vous contient aucun de votre code; Il vient de retourner l'objet générateur. L'objet de fonction qu'il crée à partir de votre code est appelé via l'attribut suivant de cet objet générateur, et cette fonction conserve son état dans l'objet générateur et sait comment enregistrer et la restaurer entre les appels.


Merci pour l'explication complète Daniel. Je comprends clairement maintenant (bien qu'il y ait d'autres choses, j'ai encore des doutes qui sortent de cette question de questions). Merci!