Je me demande (maintenant que j'ai commencé avec C++ qui a besoin d'un compilateur) pourquoi Python n'a pas besoin d'un compilateur?
Je viens d'entrer le code, de l'enregistrer en tant qu'exécuteur et de l'exécuter. En C++, je dois faire des builds et toutes ces autres choses amusantes.
Python a un compilateur! Vous ne le remarquez simplement pas car il s'exécute automatiquement. Vous pouvez dire que c'est là, cependant: regardez le .pyc
(ou .pyo
si l'optimiseur est activé) les fichiers générés pour les modules que vous import
.
En outre, il ne se compile pas dans le code de la machine native. Au lieu de cela, il compile en un code d'octet utilisé par une machine virtuelle. La machine virtuelle est elle-même un programme compilé. Ceci est très similaire à la façon dont Java fonctionne; si similaire, en fait, qu'il existe une variante Python ( Jython ) qui compile vers le Java à la place! Il y a aussi IronPython , qui compile vers le CLR de Microsoft (utilisé par .NET). (Le normal Python octet est parfois appelé CPython pour le dissocier de ces alternatives.)
C++ doit exposer son processus de compilation car le langage lui-même est incomplet; il ne spécifie pas tout ce que l'éditeur de liens doit savoir pour construire votre programme, ni ne peut spécifier les options de compilation de manière portable (certains compilateurs vous permettent d'utiliser #pragma
, mais ce n'est pas standard). Vous devez donc faire le reste du travail avec des makefiles et éventuellement auto hell (autoconf/automake/libtool). C'est vraiment juste un souvenir de la façon dont C l'a fait. Et C l'a fait de cette façon parce qu'il a rendu le compilateur simple, ce qui est l'une des principales raisons pour lesquelles il est si populaire (n'importe qui pouvait lancer un simple compilateur C dans les années 80).
Certaines choses qui peuvent affecter le fonctionnement du compilateur ou de l'éditeur de liens mais qui ne sont pas spécifiées dans la syntaxe C ou C++:
Certains d'entre eux peuvent être détectés, mais ils ne peuvent pas être spécifiés; par exemple. Je peux détecter quel C++ est utilisé avec __cplusplus
, mais je ne peux pas spécifier que C++ 98 est celui utilisé pour mon code dans le code lui-même; Je dois le passer comme indicateur au compilateur dans le Makefile, ou faire un réglage dans une boîte de dialogue.
Bien que vous puissiez penser qu'un système de "résolution de dépendance" existe dans le compilateur, générant automatiquement des enregistrements de dépendance, ces enregistrements indiquent uniquement les fichiers d'en-tête utilisés par un fichier source donné. Ils ne peuvent pas indiquer quels modules de code source supplémentaires sont nécessaires pour se lier à un programme exécutable, car il n'y a pas de moyen standard en C ou C++ pour indiquer qu'un fichier d'en-tête donné est la définition d'interface pour un autre module de code source par opposition à juste un tas de les lignes que vous souhaitez afficher à plusieurs endroits afin de ne pas vous répéter. Il existe des traditions dans les conventions de dénomination des fichiers, mais elles ne sont ni connues ni appliquées par le compilateur et l'éditeur de liens.
Plusieurs d'entre eux peuvent être définis à l'aide de #pragma
, mais ce n'est pas standard, et je parlais de la norme. Toutes ces choses pourraient être spécifiées par une norme, mais n'ont pas été dans l'intérêt de la compatibilité descendante. La sagesse qui prévaut est que les makefiles et les IDE ne sont pas cassés, alors ne les corrigez pas.
Python gère tout cela dans le langage. Par exemple, import
spécifie une dépendance de module explicite, implique l'arbre de dépendance et les modules ne sont pas divisés en en-têtes et fichiers source (c'est-à-dire interface et implémentation).
Python est un langage interprété. Cela signifie qu'il existe un logiciel sur votre ordinateur qui lit le code Python, et envoie les "instructions" à la machine. Le article Wikipedia sur les langages interprétés pourrait être de l'intérêt.
Lorsqu'un langage comme C++ (un langage compilé) est compilé, cela signifie qu'il est converti en code machine pour être lu directement par le matériel lors de son exécution. Le article Wikipedia sur les langues compilées pourrait fournir un contraste intéressant.
Toutes les langues compilées n'ont pas un cycle d'édition-compilation-lien-exécution en face à face.
Ce que vous rencontrez est une fonctionnalité/limitation de C++ (ou au moins des implémentations C++).
Pour faire quoi que ce soit, vous devez stocker votre code dans des fichiers et créer une image monolithique par un processus appelé liaison.
En particulier, c'est ce processus de liaison monolithique qui est confondu avec la distinction entre compilation et interprétation.
Certaines langues font tout cela de manière beaucoup plus dynamique, en éliminant l'étape de liaison monolithique maladroite, pas en éliminant la compilation en code machine. La source est toujours compilée en fichiers objet, mais ceux-ci sont chargés dans une image d'exécution, plutôt que liés dans un exécutable monolithique.
Vous dites "recharger ce module", et il charge la source et l'interprète, ou le compile, en fonction d'un commutateur de mode.
La programmation du noyau Linux a une partie de cette saveur même si vous travaillez en C. Vous pouvez recompiler un module et le charger et le décharger. Bien sûr, vous savez toujours que vous produisez quelque chose d'exécutable, et il est géré par un système de construction complexe, avec encore quelques étapes manuelles. Mais le fait est qu'à la fin, vous pouvez décharger et recharger juste ce petit module et ne pas avoir à redémarrer tout le noyau.
Certains langages ont une modularisation encore plus fine que celle-ci, et la construction et le chargement sont effectués à partir de leur temps d'exécution, de sorte qu'il est plus transparent.
quel détournement de la question initiale ... Un point non mentionné est que la source d'un programme python est ce que vous utilisez et distribuez, du point de vue de l'utilisateur IS le programme. Nous avons tendance à simplifier les choses en catégories qui ne sont pas bien définies.
Les programmes compilés sont généralement considérés comme des fichiers autonomes de code machine. (il est vrai qu'il contient souvent des liens vers des bibliothèques de liens dynamiques associées à des systèmes d'exploitation spécifiques). Cela dit ... il existe des variantes de la plupart des langages de programmation qui pourraient être décrits comme compilés ou interprétés.
Python n'a pas besoin d'un compilateur car il s'appuie sur une application (appelée interprète) qui compile et exécute le code sans stocker le code machine en cours de création sous une forme que vous pouvez facilement accéder ou distribuer.
Tous les langages de programmation nécessitent la traduction de concepts humains en un code machine cible. Même le langage d'assemblage doit être traduit en code machine. Cette traduction se déroule généralement dans les phases suivantes:
Phase 1: Analyse et traduction (analyse) en un code intermédiaire. Phase 2: Traduction du code intermédiaire en code machine cible avec des espaces réservés pour les références externes. Phase 3: Résolution des références externes et du packaging dans un programme exécutable machine.
Cette traduction est souvent appelée pré-compilation et "Just in time" (JIT) ou compilation au moment de l'exécution.
Les langages tels que C, C++, COBOL, Fortran, Pascal (pas tous) et Assembly sont des langages précompilés qui peuvent être exécutés directement par le système d'exploitation sans avoir besoin d'un interprète.
Des langages comme Java, BASIC, C # et Python sont interprétés. Ils utilisent tous ce code intermédiaire créé dans la phase 1, mais diffèrent parfois dans la façon dont ils le traduisent en code machine. Les formes les plus simples utilisent cela code intermédiaire pour exécuter des routines de code machine qui font le travail attendu. D'autres compileront le code intermédiaire en code machine et effectueront la correction des dépendances externes pendant l'exécution. Une fois compilé, il peut être immédiatement exécuté. De plus, le code machine est stocké dans un cache de code machine réutilisable précédemment compilé qui peut être réutilisé ultérieurement si la fonction est à nouveau nécessaire ultérieurement. Si une fonction a déjà été mise en cache, l'interpréteur n'a pas besoin de la recompiler.
La plupart des langages de haut niveau modernes entrent dans la catégorie interprétée (avec JIT). Ce sont surtout les langages plus anciens comme C & C++ qui sont précompilés.