357 votes

Python est-il fortement typé ?

J'ai trouvé des liens qui disent que Python est un langage fortement typé.

Cependant, je pensais que dans les langages fortement typés, vous ne pouviez pas faire cela :

bob = 1
bob = "bob"

Je pensais qu'un langage fortement typé n'acceptait pas le changement de type à l'exécution. Peut-être ai-je une définition erronée (ou trop simpliste) des types forts/faibles.

Alors, Python est-il un langage fortement ou faiblement typé ?

548voto

larsmans Points 167484

Python est fortement et dynamiquement typé.

  • Fort Le typage signifie que le type d'une valeur ne change pas de manière inattendue. Une chaîne de caractères ne contenant que des chiffres ne devient pas magiquement un nombre, comme cela peut arriver en Perl. Chaque changement de type nécessite une conversion explicite.
  • Dynamique Le typage signifie que les objets d'exécution (valeurs) ont un type, par opposition au typage statique où les variables ont un type.

Pour ce qui est de votre exemple

bob = 1
bob = "bob"

Cela fonctionne parce que la variable n'a pas de type ; elle peut nommer n'importe quel objet. Après bob=1 vous trouverez que type(bob) renvoie à int mais après bob="bob" il renvoie str . (Notez que type est une fonction régulière, elle évalue donc son argument, puis renvoie le type de la valeur).

Comparez cela aux anciens dialectes du C, qui étaient faiblement typés statiquement, de sorte que les pointeurs et les entiers étaient pratiquement interchangeables. (Le C ISO moderne nécessite des conversions dans de nombreux cas, mais mon compilateur est encore indulgent à ce sujet par défaut).

Je dois ajouter que le typage fort ou faible est plus un continuum qu'un choix booléen. Le C++ a un typage plus fort que le C (plus de conversions requises), mais le système de type peut être subverti en utilisant des casts de pointeurs.

La force du système de types dans un langage dynamique tel que Python est réellement déterminée par la façon dont ses primitives et fonctions de bibliothèque répondent aux différents types. Par exemple, + est surchargé de manière à ce qu'il fonctionne sur deux nombres. ou deux chaînes, mais pas une chaîne et un nombre. Il s'agit d'un choix de conception fait lorsque + a été implémenté, mais n'est pas vraiment une nécessité découlant de la sémantique du langage. En fait, lorsque vous surchargez + sur un type personnalisé, vous pouvez lui faire convertir implicitement n'importe quoi en un nombre :

def to_number(x):
    """Try to convert function argument to float-type object."""
    try: 
        return float(x) 
    except (TypeError, ValueError): 
        return 0 

class Foo:
    def __init__(self, number): 
        self.number = number

    def __add__(self, other):
        return self.number + to_number(other)

Instance de la classe Foo peuvent être ajoutés à d'autres objets :

>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42

Observez que, même si Python est fortement typé, l'ajout d'objets de type int et float et renvoie un objet de type float (par exemple, int(42) + float(1) renvoie à 43.0 ). D'autre part, en raison de l'incompatibilité entre les types, Haskell se plaindrait si l'on essayait ce qui suit (42 :: Integer) + (1 :: Float) . Cela fait de Haskell un langage strictement typé, où les types sont entièrement disjoints et où seule une forme contrôlée de surcharge est possible via les classes de types.

28 votes

Un exemple que je ne vois pas très souvent, mais que je trouve important pour montrer que Python n'est pas complètement fortement typé, est celui de toutes les choses qui évaluent les booléens : docs.python.org/release/2.5.2/lib/truth.html

1 votes

@gsingh2011 : C'est vrai, cela peut être considéré comme un contre-exemple.

36 votes

Je ne suis pas sûr que ce soit un contre-exemple : Les choses peuvent être évaluées à un booléen, mais elles ne "deviennent" pas soudainement un booléen. C'est presque comme si quelqu'un avait implicitement appelé quelque chose comme as_boolean(<value>), ce qui n'est pas la même chose que le type de l'objet lui-même qui change, non ?

90voto

abarnert Points 94246

Il y a des questions importantes que, selon moi, toutes les réponses existantes ont négligées.


Le typage faible permet d'accéder à la représentation sous-jacente. En C, je peux créer un pointeur sur des caractères, puis indiquer au compilateur que je veux l'utiliser comme un pointeur sur des entiers :

char sz[] = "abcdefg";
int *i = (int *)sz;

Sur une plate-forme little-endian avec des entiers de 32 bits, cela donne i dans un tableau de nombres 0x64636261 et 0x00676665 . En fait, vous pouvez même convertir les pointeurs eux-mêmes en entiers (de la taille appropriée) :

intptr_t i = (intptr_t)&sz;

Et bien sûr, cela signifie que je peux écraser la mémoire n'importe où dans le système. *

char *spam = (char *)0x12345678
spam[0] = 0;

* Bien sûr, les systèmes d'exploitation modernes utilisent la mémoire virtuelle et la protection des pages, de sorte que je ne peux écraser que la mémoire de mon propre processus, mais il n'y a rien dans le C lui-même qui offre une telle protection, comme toute personne qui a déjà codé sur, disons, Classic Mac OS ou Win16 peut vous le dire.

Le Lisp traditionnel permettait des piratages similaires ; sur certaines plates-formes, les flottants à double mot et les cellules de type cons étaient du même type, et vous pouviez simplement passer l'un à une fonction qui attendait l'autre et cela "fonctionnait".

La plupart des langages actuels ne sont pas aussi faibles que C et Lisp l'étaient, mais beaucoup d'entre eux sont encore quelque peu perméables. Par exemple, tout langage OO qui possède un "downcast" non coché*, est une fuite de type : vous dites essentiellement au compilateur "Je sais que je ne vous ai pas donné assez d'informations pour savoir que ceci est sûr, mais je suis presque sûr que ça l'est", alors que le but d'un système de types est que le compilateur ait toujours assez d'informations pour savoir ce qui est sûr.

* Un downcast vérifié ne rend pas le système de types du langage plus faible simplement parce qu'il déplace la vérification au moment de l'exécution. Si c'était le cas, alors le polymorphisme de sous-type (c'est-à-dire les appels de fonctions virtuelles ou entièrement dynamiques) serait la même violation du système de types, et je ne pense pas que quiconque veuille dire cela.

Très peu de langages de "script" sont faibles dans ce sens. Même en Perl ou en Tcl, vous ne pouvez pas prendre une chaîne de caractères et interpréter ses octets comme un nombre entier.* Mais il est intéressant de noter que dans CPython (et de la même manière pour de nombreux autres interpréteurs pour de nombreux langages), si vous êtes vraiment persévérant, vous pouvez utiliser la fonction ctypes pour charger libpython l'image de l'objet id à un POINTER(Py_Object) et forcer le système de type à fuir. Que cela rende le système de types faible ou non dépend de vos cas d'utilisation - si vous essayez d'implémenter un bac à sable d'exécution restreinte dans le langage pour assurer la sécurité, vous devez faire face à ce genre d'échappatoires

* Vous pouvez utiliser une fonction comme <code>struct.unpack</code> pour lire les octets et construire un nouveau int à partir de "comment C représenterait ces octets", mais ce n'est évidemment pas une fuite ; même Haskell le permet.


Entre-temps, la conversion implicite est vraiment différente d'un système de type faible ou peu fiable.

Chaque langage, même Haskell, possède des fonctions permettant, par exemple, de convertir un entier en chaîne de caractères ou en flottant. Mais certains langages effectuent automatiquement certaines de ces conversions pour vous - par exemple, en C, si vous appelez une fonction qui veut une valeur de float et vous le passez dans int il est converti pour vous. Cela peut certainement conduire à des bogues avec, par exemple, des débordements inattendus, mais ce ne sont pas les mêmes types de bogues que l'on obtient avec un système de types faible. Et le C n'est pas vraiment plus faible ici ; vous pouvez ajouter un int et un float en Haskell, ou même concaténer un float à une chaîne, vous devez juste le faire plus explicitement.

Et avec les langages dynamiques, c'est assez obscur. Il n'existe pas de "fonction qui veut un flottant" en Python ou en Perl. Mais il existe des fonctions surchargées qui font différentes choses avec différents types, et il y a un sens intuitif fort que, par exemple, l'ajout d'une chaîne à quelque chose d'autre est "une fonction qui veut une chaîne". Dans ce sens, Perl, Tcl et JavaScript semblent faire beaucoup de conversions implicites ( "a" + 1 vous donne "a1" ), tandis que Python en fait beaucoup moins ( "a" + 1 lève une exception, mais 1.0 + 1 vous donne 2.0 *). Il est difficile d'exprimer ce sens en termes formels - pourquoi n'y aurait-il pas un système d'échange de données ? + qui prend une chaîne et un int, alors qu'il existe manifestement d'autres fonctions, comme l'indexation, qui le font ?

* En fait, dans le Python moderne, cela peut s'expliquer en termes de sous-typage OO, car <code>isinstance(2, numbers.Real)</code> est vrai. Je ne pense pas qu'il y ait un sens dans lequel <code>2</code> est une instance du type chaîne en Perl ou en JavaScript bien qu'en Tcl, c'est en fait le cas, puisque <em>tout </em>est une instance de chaîne de caractères.


Enfin, il existe une autre définition, totalement orthogonale, de la typographie "forte" par rapport à la typographie "faible", où "forte" signifie puissante/flexible/expressive.

Par exemple, Haskell vous permet de définir un type qui est un nombre, une chaîne de caractères, une liste de ce type, ou une carte de chaînes de caractères vers ce type, ce qui est une façon parfaite de représenter tout ce qui peut être décodé à partir de JSON. Il n'y a aucun moyen de définir un tel type en Java. Mais au moins Java a des types paramétriques (génériques), donc vous pouvez écrire une fonction qui prend une liste de T et savoir que les éléments sont de type T ; d'autres langages, comme les premiers Java, vous ont forcé à utiliser une liste d'objets et à baisser les bras. Mais au moins, Java vous permet de créer de nouveaux types avec leurs propres méthodes ; le C ne vous permet que de créer des structures. Et BCPL n'avait même pas ça. Et ainsi de suite jusqu'à l'assemblage, où les seuls types sont des longueurs de bits différentes.

Ainsi, dans ce sens, le système de types de Haskell est plus fort que celui de Java moderne, qui est plus fort que celui de Java antérieur, qui est plus fort que celui de C, qui est plus fort que celui de BCPL.

Alors, où se situe Python dans ce spectre ? C'est un peu délicat. Dans de nombreux cas, le duck typing vous permet de simuler tout ce que vous pouvez faire en Haskell, et même certaines choses que vous ne pouvez pas faire ; bien sûr, les erreurs sont attrapées au moment de l'exécution plutôt qu'au moment de la compilation, mais elles sont quand même attrapées. Cependant, il existe des cas où le typage en canard n'est pas suffisant. Par exemple, en Haskell, vous pouvez dire qu'une liste vide d'ints est une liste d'ints, et vous pouvez donc décider que la réduction d'une liste d'ints à une liste d'ints n'est pas suffisante. + sur cette liste devrait retourner 0* ; en Python, une liste vide est une liste vide ; il n'y a pas d'information de type pour vous aider à décider ce qui réduit + sur ce qu'il devrait faire.

* En fait, Haskell ne vous permet pas de le faire ; si vous appelez la fonction reduce qui ne prend pas de valeur de départ sur une liste vide, vous obtenez une erreur. Mais son système de types est suffisamment puissant pour que vous <em>pourrait </em>pour que ça marche, et celle de Python ne l'est pas.

4 votes

Cette réponse est brillante ! Dommage qu'elle soit restée si longtemps en bas de la liste.

4 votes

Juste un petit commentaire sur votre exemple C : char sz[] n'est pas un pointeur sur un char, c'est un tableau de chars, et dans l'affectation, il se transforme en pointeur.

47voto

Martijn Pieters Points 271458

Vous confondez fortement typé". avec typage dynamique .

Je ne peux pas changer le type de 1 en ajoutant la chaîne '12' mais je peux choisir les types que je stocke dans une variable et les modifier pendant l'exécution du programme.

L'opposé du typage dynamique est le typage statique. déclaration des types de variables ne change pas pendant la durée de vie d'un programme. L'opposé du typage fort est le typage faible ; le type de valeurs peuvent changer pendant la durée de vie d'un programme.

0 votes

La description dans le lien fortement typé : "Généralement, un langage fortement typé a des règles de typage plus strictes au moment de la compilation, ce qui implique que les erreurs et les exceptions sont plus susceptibles de se produire pendant la compilation." implique que Python est un langage faiblement typé..., wiki se trompe-t-il ?

1 votes

@secret : ce n'est pas du tout implicite. Python a des règles de typage strictes à la compilation, chaque objet créé n'a qu'un seul type. Et "généralement" n'implique rien, cela signifie simplement que Python est une exception à cette règle.

23voto

Levon Points 34085

Selon cette wiki Python article Python est à la fois dynamiquement et fortement typé (fournit également une bonne explication).

Vous pensez peut-être à _typée statiquement_ les langages où les types ne peuvent pas changer pendant l'exécution du programme et où la vérification des types se fait au moment de la compilation pour détecter les erreurs éventuelles.

Cette question de l'OS pourrait vous intéresser : Langages de types dynamiques et langages de types statiques et cet article de Wikipedia sur Systèmes de type fournit plus d'informations

11voto

Wayne Werner Points 10172

On a déjà répondu plusieurs fois à cette question, mais Python est un langage fortement typé :

>>> x = 3
>>> y = '4'
>>> print(x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Ce qui suit en JavaScript :

var x = 3    
var y = '4'
alert(x + y) //Produces "34"

C'est la différence entre la typographie faible et la typographie forte. Les types faibles essaient automatiquement de convertir un type en un autre, en fonction du contexte (par exemple, Perl). Les types forts jamais convertir implicitement.

Votre confusion réside dans une mauvaise compréhension de la manière dont Python lie les valeurs aux noms (communément appelés variables).

En Python, les noms n'ont pas de types, donc vous pouvez faire des choses comme :

bob = 1
bob = "bob"
bob = "An Ex-Parrot!"

Et les noms peuvent être liés à n'importe quoi :

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

Pour en savoir plus :

https://en.wikipedia.org/wiki/Dynamic_dispatch

et celle, légèrement connexe mais plus avancée :

http://effbot.org/zone/call-by-object.htm

1 votes

Plusieurs années plus tard - une autre ressource utile et pertinente : youtu.be/_AEJHKGk9ns

0 votes

Le typage fort ou faible n'a rien à voir avec le type de résultat d'expressions comme 3+'4'. JavaScript est tout aussi fort que Python pour cet exemple.

0 votes

@qznc en quoi Javasript est-il aussi fort ? Je ne pense pas avoir laissé entendre que cela avait quelque chose à voir avec le type résultant, en effet, je déclare explicitement Les types faibles tentent automatiquement de se convertir d'un type à un autre .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X