Python est fortement typé dynamiquement.
- Un typage fort signifie que le type d'une valeur ne change pas de manière inattendue. Une chaîne contenant uniquement des chiffres ne devient pas par magie un nombre, comme cela peut arriver en Perl. Chaque changement de type nécessite une conversion explicite.
- Le typage dynamique signifie que les objets d'exécution (valeurs) ont un type, par opposition au typage statique où les variables ont un type.
Quant à votre exemple
bob = 1
bob = "bob"
Cela fonctionne parce que la variable n'a pas de type; il peut nommer n'importe quel objet. Après bob=1
, vous trouverez que type(bob)
revient int
, mais après bob="bob"
, il revient str
. (Notez qu'il type
s'agit d'une fonction régulière, elle évalue donc son argument, puis renvoie le type de la valeur.)
Comparez cela à des dialectes plus anciens de C, qui étaient typés faiblement et statiquement, de sorte que les pointeurs et les entiers étaient à peu près interchangeables. (ISO C moderne nécessite des conversions dans de nombreux cas, mais mon compilateur est toujours indulgent à ce sujet par défaut.)
Je dois ajouter que le typage fort vs faible est plus un continuum qu'un choix booléen. C ++ a un typage plus fort que C (plus de conversions sont nécessaires), mais le système de type peut être inversé en utilisant des transtypages de pointeurs.
La force du système de types dans un langage dynamique tel que Python est vraiment déterminée par la façon dont ses primitives et ses fonctions de bibliothèque répondent à différents types. Par exemple, +
est surchargé de sorte qu'il fonctionne sur deux nombres ou deux chaînes, mais pas sur une chaîne et un nombre. C'est un choix de conception fait lors de l' +
implémentation, mais pas vraiment une nécessité découlant de la sémantique du langage. En fait, lorsque vous surchargez +
un type personnalisé, vous pouvez le faire implicitement convertir n'importe quoi en 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)
Une instance de classe Foo
peut être ajoutée à d'autres objets:
>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42
Notez que même si fortement typé Python est tout à fait bien avec l' ajout d' objets de type int
et float
et retourne un objet de type float
(par exemple, les int(42) + float(1)
rendements 43.0
). D'un autre côté, en raison de l'inadéquation 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 seule une forme contrôlée de surcharge est possible via les classes de types.
True
ouFalse
. Mais qu'en est-il de la promotion des numéros?1.0 + 2
fonctionne aussi bien en Python qu'en Perl ou C, même si ce"1.0" + 2
n'est pas le cas. Je suis d'accord avec @jbrendel que ce n'est pas vraiment une conversion implicite, c'est juste une surcharge - mais dans le même sens, Perl ne fait pas non plus de conversion implicite. Si les fonctions n'ont pas de types de paramètres déclarés, il n'y a aucun endroit où des conversions implicites se produisent.if isValid(value) - 1
peut fuir. Le booléen est contraint en entier, qui est ensuite évalué comme une valeur véridique.False - 1
devient véridique etTrue - 1
devient fausse, ce qui entraîne une erreur embarrassante et difficile à déboguer de deux couches. En ce sens, python est principalement fortement typé; les contraintes de type ne provoquent généralement pas d'erreurs logiques.Il y a des problèmes importants qui, je pense, n'ont pas été résolus dans toutes les réponses existantes.
Un typage faible signifie permettre l'accès à la représentation sous-jacente. En C, je peux créer un pointeur sur des caractères, puis dire au compilateur que je veux l'utiliser comme pointeur sur des entiers:
Sur une plate-forme little-endian avec des entiers 32 bits, cela se
i
transforme en un tableau des nombres0x64636261
et0x00676665
. En fait, vous pouvez même convertir des pointeurs eux-mêmes en entiers (de la taille appropriée):Et bien sûr, cela signifie que je peux écraser la mémoire n'importe où dans le système. *
* Bien sûr, les systèmes d'exploitation modernes utilisent la mémoire virtuelle et la protection des pages, je ne peux donc qu'écraser la mémoire de mon propre processus, mais il n'y a rien sur C lui-même qui offre une telle protection, comme toute personne qui a déjà codé, disons, Classic Mac OS ou Win16 peut vous le dire.
Le Lisp traditionnel permettait des types de piratage similaires; sur certaines plates-formes, les flottants à double mot et les contre-cellules étaient du même type, et vous pouviez simplement passer l'un à une fonction qui attendait l'autre et cela "fonctionnerait".
Aujourd'hui, la plupart des langages ne sont pas aussi faibles que C et Lisp, mais beaucoup d'entre eux sont encore quelque peu fuyants. Par exemple, tout langage OO qui a un "downcast" non contrôlé *, c'est une fuite de type: vous dites essentiellement au compilateur "Je sais que je ne vous ai pas donné suffisamment d'informations pour savoir que cela est sûr, mais je suis à peu près sûr c'est ", lorsque le point essentiel d'un système de type est que le compilateur a toujours suffisamment d'informations pour savoir ce qui est sûr.
* Un abaissé vérifié n'affaiblit pas le système de type de la langue simplement parce qu'il déplace la vérification vers l'exécution. Si c'était le cas, le polymorphisme de sous-type (alias appels de fonction virtuels ou entièrement dynamiques) serait la même violation du système de type, et je ne pense pas que quiconque veuille le dire.
Très peu de langages de "scripting" sont faibles dans ce sens. Même en Perl ou Tcl, vous ne pouvez pas prendre une chaîne et simplement interpréter ses octets comme un entier. * Mais il convient de noter qu'en CPython (et de même pour de nombreux autres interprètes pour de nombreuses langues), si vous êtes vraiment persistant, vous peut utiliser
ctypes
pour chargerlibpython
, lancer un objetid
en unPOINTER(Py_Object)
et forcer le système de saisie à fuir. Que cela rende le système de type faible ou non dépend de vos cas d'utilisation - si vous essayez d'implémenter un sandbox d'exécution restreinte en langage pour garantir la sécurité, vous devez faire face à ce genre d'évasions…* Vous pouvez utiliser une fonction comme
struct.unpack
pour lire les octets et construire un nouvel entier à partir de "comment C représenterait ces octets", mais ce n'est évidemment pas une fuite; même Haskell le permet.Pendant ce temps, la conversion implicite est vraiment différente d'un système de type faible ou qui fuit.
Chaque langue, même Haskell, a des fonctions pour, par exemple, convertir un entier en chaîne ou en flottant. Mais certaines langues effectueront automatiquement certaines de ces conversions pour vous - par exemple, en C, si vous appelez une fonction qui veut un
float
, et que vous la transmettezint
, elle est convertie pour vous. Cela peut certainement entraîner des bogues avec, par exemple, des débordements inattendus, mais ce ne sont pas les mêmes types de bogues que vous obtenez d'un système de type faible. Et C n'est pas vraiment plus faible ici; vous pouvez ajouter un int et un float dans Haskell, ou même concaténer un float à une chaîne, il vous suffit de le faire plus explicitement.Et avec les langages dynamiques, c'est assez trouble. Il n'y a rien de tel qu'une "fonction qui veut un flottant" en Python ou Perl. Mais il y a des fonctions surchargées qui font différentes choses avec différents types, et il y a un fort sentiment intuitif que, par exemple, ajouter une chaîne à autre chose est "une fonction qui veut une chaîne". En 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
déclenche une exception, mais1.0 + 1
vous donne2.0
*). Il est juste difficile de mettre ce sens en termes formels - pourquoi ne devrait-il pas y avoir un+
qui prend une chaîne et un int, alors qu'il y a évidemment d'autres fonctions, comme l'indexation, qui le font?* En fait, en Python moderne, cela peut être expliqué en termes de sous-typage OO, car
isinstance(2, numbers.Real)
c'est vrai. Je ne pense pas qu'il y ait un sens dans lequel se2
trouve une instance du type chaîne en Perl ou JavaScript… bien qu'en Tcl, ce soit le cas, puisque tout est une instance de chaîne.Enfin, il existe une autre définition, complètement orthogonale, du typage «fort» contre «faible», où «fort» signifie puissant / flexible / expressif.
Par exemple, Haskell vous permet de définir un type qui est un nombre, une chaîne, une liste de ce type ou une mappe de chaînes à ce type, ce qui est parfaitement un moyen 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), vous pouvez donc écrire une fonction qui prend une liste de T et savoir que les éléments sont de type T; d'autres langages, comme Java, vous obligeaient à utiliser une liste d'objets et à abattre. Mais au moins Java vous permet de créer de nouveaux types avec leurs propres méthodes; C vous permet uniquement de créer des structures. Et BCPL n'en avait même pas. Et ainsi de suite jusqu'à l'assemblage, où les seuls types sont des longueurs de bits différentes.
Donc, dans ce sens, le système de type de Haskell est plus fort que le Java moderne, qui est plus fort que le Java précédent, qui est plus fort que le C, qui est plus fort que le BCPL.
Alors, où Python s'intègre-t-il dans ce spectre? C'est un peu délicat. Dans de nombreux cas, le typage canard vous permet de simuler tout ce que vous pouvez faire dans Haskell, et même certaines choses que vous ne pouvez pas; Bien sûr, les erreurs sont détectées lors de l'exécution au lieu de la compilation, mais elles sont toujours détectées. Cependant, il y a des cas où la frappe de canard n'est pas suffisante. Par exemple, dans Haskell, vous pouvez dire qu'une liste vide d'entiers est une liste d'entiers, vous pouvez donc décider que la réduction
+
sur cette liste devrait retourner 0 *; en Python, une liste vide est une liste vide; il n'y a aucune information de type pour vous aider à décider ce que la réduction+
devrait faire.* En fait, Haskell ne vous laisse pas faire cela; si vous appelez la fonction de réduction qui ne prend pas de valeur de départ dans une liste vide, vous obtenez une erreur. Mais son système de type est suffisamment puissant pour que vous puissiez faire fonctionner cela, contrairement à Python.
la source
char sz[]
n'est pas un pointeur vers char, c'est un tableau de char, et dans l'affectation il se désintègre en pointeur.Vous confondez «fortement typé» avec «typé dynamiquement» .
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 changer cela pendant l'exécution du programme.Le contraire du typage dynamique est le typage statique; la déclaration des types de variables ne change pas pendant la durée de vie d'un programme. Le contraire d'un typage fort est un typage faible; le type de valeurs peut changer pendant la durée de vie d'un programme.
la source
Selon cet article wiki Python, Python est à la fois dynamiquement et fortement typé (fournit également une bonne explication).
Vous pensez peut-être à des langages typés statiquement où les types ne peuvent pas changer pendant l'exécution du programme et la vérification de type se produit pendant la compilation pour détecter les erreurs possibles.
Cette question SO pourrait être intéressante: les langages de types dynamiques contre les langages de types statiques et cet article Wikipedia sur les systèmes de types fournit plus d'informations
la source
TLDR;
Le typage de Python est Dynamique pour que vous puissiez changer une variable de chaîne en int
Le typage Python est Strong , vous ne pouvez donc pas fusionner les types:
En Javascript faiblement typé, cela se produit ...
Concernant l'inférence de type
Java vous oblige à déclarer explicitement vos types d'objets
Kotlin utilise l'inférence pour réaliser que c'est un
int
Mais comme les deux langues utilisent des types statiques ,
x
elles ne peuvent pas être modifiées à partir d'unint
. Aucune des deux langues ne permettrait un changement dynamique commela source
'x' + 3
peut-êtreoperator+
surcharger et faire la conversion de type derrière la scène?Il a déjà été répondu plusieurs fois, mais Python est un langage fortement typé:
Les éléments suivants en JavaScript:
C'est la différence entre une frappe faible et une frappe forte. Les types faibles essaient automatiquement de convertir d'un type à un autre, selon le contexte (par exemple Perl). Les types forts ne se convertissent jamais implicitement.
Votre confusion réside dans une mauvaise compréhension de la façon dont Python lie les valeurs aux noms (communément appelés variables).
En Python, les noms n'ont pas de types, vous pouvez donc faire des choses comme:
Et les noms peuvent être liés à n'importe quoi:
Pour en savoir plus:
https://en.wikipedia.org/wiki/Dynamic_dispatch
et le légèrement lié mais plus avancé:
http://effbot.org/zone/call-by-object.htm
la source
"3"*4
en python. Le résultat est bien sûr"3333"
. Vous ne diriez pas que cela convertit l'une ou l'autre chose. Bien sûr, cela pourrait être juste une dispute sémantique.float
partir de la combinaison defloat
etint
qu'il convertit implicitement le type. Il existe une relation naturelle entre float et int, et en effet, le type héritier le précise. Je suppose que vous pourriez faire valoir que Javascript considère'3'+4
et être'e'+4
à la fois des opérations bien définies de la même manière que Python considère3.0 + 4
être bien défini, mais à ce moment-là, il n'y a vraiment pas de types forts ou faibles, juste (non) définis opérations.Une variable Python stocke une référence non typée à l'objet cible qui représente la valeur.
Toute opération d'affectation signifie l'attribution de la référence non typée à l'objet affecté - c'est-à-dire que l'objet est partagé via l'original et les nouvelles références (comptées).
Le type de valeur est lié à l'objet cible, pas à la valeur de référence. La vérification du type (fort) est effectuée lorsqu'une opération avec la valeur est effectuée (temps d'exécution).
En d'autres termes, les variables (techniquement) n'ont pas de type - il n'est pas logique de penser en termes de type de variable si l'on veut être exact. Mais les références sont automatiquement déréférencées et nous pensons en fait en termes de type d'objet cible.
la source
Le terme "typage fort" n'a pas de définition définitive.
Par conséquent, l'utilisation du terme dépend de la personne avec qui vous parlez.
Je ne considère aucun langage dans lequel le type d'une variable n'est ni explicitement déclaré, ni typé statiquement comme fortement typé.
Un typage fort n'empêche pas seulement la conversion (par exemple, la conversion "automatique" d'un entier en une chaîne). Cela empêche l'affectation (c.-à-d. Changer le type d'une variable).
Si le code suivant compile (interprète), le langage n'est pas typé fort:
Foo = 1 Foo = "1"
Dans un langage fortement typé, un programmeur peut "compter sur" un type.
Par exemple, si un programmeur voit la déclaration,
UINT64 kZarkCount;
et il ou elle sait que 20 lignes plus tard, kZarkCount est toujours un UINT64 (tant qu'il apparaît dans le même bloc) - sans avoir à examiner le code intermédiaire.
la source
Je viens de découvrir une superbe manière concise de le mémoriser:
la source
Je pense que cet exemple simple devrait vous expliquer les différences entre la frappe forte et la frappe dynamique:
Java:
la source
Ce qui précède créerait un cauchemar de code non maintenable dans un grand système sur une longue période. Appelez ça comme vous voulez, mais la possibilité de changer "dynamiquement" un type de variable n'est qu'une mauvaise idée ...
la source