Quand les fichiers .pyc sont-ils actualisés?

91

Je comprends que les fichiers ".pyc" sont des versions compilées des fichiers ".py" en texte brut, créés au moment de l'exécution pour accélérer l'exécution des programmes. Cependant, j'ai observé quelques choses:

  1. Lors de la modification des fichiers "py", le comportement du programme change. Cela indique que les fichiers "py" sont compilés ou au moins passent par une sorte de processus de hachage ou comparent les horodatages afin de dire s'ils doivent ou non être recompilés.
  2. Lors de la suppression de tous les fichiers ".pyc" ( rm *.pyc), le comportement du programme changera parfois. Ce qui indiquerait qu'ils ne sont pas compilés lors de la mise à jour des ".py".

Des questions:

  • Comment décident-ils du moment de la compilation?
  • Existe-t-il un moyen de s'assurer qu'ils ont des contrôles plus stricts pendant le développement?
Aaron Schif
la source
14
Méfiez-vous de la suppression des fichiers .pyc avec rm *.pyc. Cela ne supprimera pas les fichiers .pyc dans les dossiers imbriqués. Utiliser à la find . -name '*.pyc' -deleteplace
Zags
6
Peut-être une note sur votre question: un programme ne s'exécute pas plus rapidement lorsqu'il est lu à partir d'un fichier «.pyc» ou «.pyo» que lorsqu'il est lu à partir d'un fichier «.py»; la seule chose qui est plus rapide avec les fichiers «.pyc» ou «.pyo» est la vitesse à laquelle ils sont chargés. lien
maggie
@maggie Quelle est la différence entre le temps de chargement et le temps d'exécution?
Daniel Springer
3
@Dani loading est le temps qu'il faut pour lire puis compiler le programme. Le temps d'exécution correspond au moment où le programme est en cours d'exécution, ce qui se produit après le chargement. Si vous voulez être technique, les types de temps sont le temps de chargement, le temps de compilation, le temps de liaison et le temps d'exécution. Faire un .pyc élimine la partie du temps de compilation.
Eric Klien
@EricKlien merci homme
Daniel Springer

Réponses:

79

Les .pycfichiers sont créés (et éventuellement écrasés) uniquement lorsque ce fichier python est importé par un autre script. Si l'importation est appelée, Python vérifie si l' .pychorodatage interne du fichier n'est pas plus ancien que le .pyfichier correspondant . Si c'est le cas, il charge le .pyc; si ce n'est pas le cas ou si le .pycn'existe pas encore, Python compile le .pyfichier en a .pycet le charge.

Qu'entendez-vous par «contrôle plus strict»?

DaveTheScientist
la source
3
Je suis capable de résoudre les problèmes avec rm *.pyc. Je sais que si je force tous les fichiers à être recréés, certains problèmes sont résolus, indiquant que les fichiers ne sont pas recompilés par eux-mêmes. Je suppose que s'ils utilisent les horodatages, il n'y a aucun moyen de rendre ce comportement plus strict, mais le problème persiste.
Aaron Schif
13
Ce n'est pas tout à fait correct. Les horodatages n'ont pas besoin de correspondre (et ils ne le font généralement pas). L' .pychorodatage doit être plus ancien que l' .pyhorodatage correspondant pour déclencher une recompilation.
Tim Pietzcker
4
@Aaron, est-ce que vous modifiez éventuellement les fichiers .py, et dans le processus de les rendre plus anciens (par exemple en les copiant depuis un autre répertoire, en utilisant une opération qui préserve le «temps de modification»)?
greggo
1
@greggo, j'utilise git et je mets à jour à partir d'un référentiel, donc oui d'une manière que je suis. Cela pourrait le faire. Merci.
Aaron Schif
1
Bon à savoir. Et si vous corrigiez votre réponse?
Piotr Dobrogost
29

Fichiers .pyc générés chaque fois que les éléments de code correspondants sont importés et mis à jour si les fichiers de code correspondants ont été mis à jour. Si les fichiers .pyc sont supprimés, ils seront automatiquement régénérés. Cependant, ils ne sont pas automatiquement supprimés lorsque les fichiers de code correspondants sont supprimés.

Cela peut provoquer des bugs vraiment amusants lors des refactors au niveau des fichiers.

Tout d'abord, vous pouvez finir par pousser du code qui ne fonctionne que sur votre machine et sur personne d'autre. Si vous avez des références en suspens à des fichiers que vous avez supprimés, celles-ci fonctionneront toujours localement si vous ne supprimez pas manuellement les fichiers .pyc pertinents, car les fichiers .pyc peuvent être utilisés dans les importations. Cela est aggravé par le fait qu'un système de contrôle de version correctement configuré ne poussera que les fichiers .py vers le référentiel central, pas les fichiers .pyc, ce qui signifie que votre code peut passer le "test d'importation" (tout est-il bien importé) très bien et non travailler sur l'ordinateur de quelqu'un d'autre.

Deuxièmement, vous pouvez avoir des bugs assez terribles si vous transformez des paquets en modules. Lorsque vous convertissez un package (un dossier avec un __init__.pyfichier) en un module (un fichier .py), les fichiers .pyc qui représentaient autrefois ce package restent. En particulier, les __init__.pycrestes. Donc, si vous avez le package foo avec un code qui n'a pas d'importance, supprimez plus tard ce package et créez un fichier foo.py avec une fonction def bar(): passet exécutez:

from foo import bar

vous obtenez:

ImportError: cannot import name bar

car python utilise toujours les anciens fichiers .pyc du package foo, dont aucun ne définit bar. Cela peut être particulièrement problématique sur un serveur Web, où un code totalement fonctionnel peut être interrompu à cause de fichiers .pyc.

En raison de ces deux raisons (et éventuellement d'autres), votre code de déploiement et votre code de test doivent supprimer les fichiers .pyc, comme avec la ligne suivante de bash:

find . -name '*.pyc' -delete

De plus, à partir de python 2.6, vous pouvez exécuter python avec l' -Bindicateur pour ne pas utiliser de fichiers .pyc. Voir Comment éviter les fichiers .pyc? pour plus de détails.

Voir aussi: Comment supprimer tous les fichiers .pyc d'un projet?

Zags
la source
"Lorsque vous convertissez un module (un dossier avec un __init__.pyfichier) ...". Ce serait un package, pas un module.
Robert David Grant
2
En particulier, les __init__.pycrestes. - Comment venir? Comme un paquet est un répertoire, la suppression d'un paquet signifie la suppression du répertoire donc il n'y a plus de fichiers…
Piotr Dobrogost
3
@PiotrDobrogost Un contrôle de source correctement géré implique de ne pas vérifier vos fichiers pyc dans la source. Ainsi, même si vous pouvez supprimer le dossier, y compris les fichiers pyc, dans votre copie locale, il ne sera pas supprimé pour quelqu'un d'autre qui fait un git pull. Cela peut planter votre serveur si votre déploiement implique également une extraction git.
Zags le
Il existe de nombreuses raisons de ne pas faire confiance à votre environnement de développement pour être représentatif de l'endroit où votre code sera déployé. Ce .pycproblème est également une raison: dépendances cachées sur les niveaux de correctifs du système d'exploitation et des utilitaires, .sofichiers , fichiers de configuration, autres bibliothèques Python (si vous ne travaillez pas dans un environnement virtuel), obscur env vars ... la liste est longue. Pour être minutieux et trouver tous ces problèmes, vous devez faire une copie propre de votre code dans un référentiel git ou le publier en tant que package sur un serveur de style PyPi, et effectuer un clonage complet ou une configuration sur une nouvelle machine virtuelle. Certains de ces problèmes potentiels rendent ce .pycproblème pâle en comparaison.
Chris Johnson le