web-dev-qa-db-fra.com

Python est-il fortement typé?

Je suis tombé sur des liens qui disent que Python est un langage fortement typé.

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

bob = 1
bob = "bob"

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

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

208
Pacane

Python est fortement typé et dynamiquement.

  • Fort le fait de taper signifie que le type d'une valeur ne change pas de manière inattendue. Une chaîne contenant uniquement des chiffres ne devient pas comme par magie 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.

Comme pour votre exemple

bob = 1
bob = "bob"

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

Contrastez ceci avec les dialectes plus anciens du C, qui étaient faiblement typés statiquement, de sorte que les pointeurs et les entiers étaient pratiquement interchangeables. (La norme 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 davantage un continuum qu'un choix booléen. Le C++ a un typage plus fort que le C (plus de conversions sont nécessaires), mais le système de types peut être subverti en utilisant des conversions par pointeur.

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 une chaîne et un nombre. Il s'agit d'un choix de conception effectué lorsque + a été implémenté, mais ce 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 le convertir implicitement en un nombre:

def to_number(x):
    """Try to convert x to a number."""
    if x is None:
        return 0
    # more special cases here
    else:
        return float(x)  # works for numbers and strings

class Foo(object):
    def __add__(self, other):
        other = to_number(other)
        # now do the addition

(Le seul langage que je connaisse qui soit complètement typé, alias strict, est Haskell, où les types sont entièrement disjoints et où seule une forme contrôlée de surcharge est possible via des classes de types.)

322
Fred Foo

Je pense que toutes les réponses existantes ont été omises.


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:

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

Sur une plate-forme little-endian avec des entiers 32 bits, ceci transforme i en un tableau des nombres 0x64636261 et 0x00676665. En fait, vous pouvez même convertir les pointeurs eux-mêmes en entiers (de 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 protection de la mémoire virtuelle et des pages afin que je ne puisse écraser que la mémoire de mon propre processus, mais rien ne dit que C lui-même offre une telle protection, comme le dit Classic Mac OS ou Win16.

Le LISP traditionnel permettait des types de piratage similaires; sur certaines plates-formes, les flottants double-mot et les cellules par contre étaient du même type, et vous pouvez simplement passer l’un à une fonction qui attend l’autre et cela "fonctionnerait".

Aujourd'hui, la plupart des langues ne sont pas aussi faibles que le C et le LISP, mais beaucoup d'entre elles ont encore des fuites. Par exemple, tout langage OO ayant 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 c'est sûr, mais je suis presque sûr que c’est le cas "lorsque l’intérêt 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 typage de la langue plus faible simplement parce qu'il déplace le contrôle vers le temps d'exécution. Si tel était le cas, le polymorphisme de sous-type (appels de fonction virtuels ou entièrement dynamiques) constituerait 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 en ce sens. Même en Perl ou en Tcl, vous ne pouvez pas interpréter une chaîne et interpréter ses octets comme un entier. * Il convient toutefois de noter que dans CPython (et de même pour de nombreux autres interprètes pour de nombreuses langues), si vous êtes vraiment persistant, pouvez utiliser ctypes pour charger libpython, transtyper un objet id en POINTER(Py_Object) et forcer le système de saisie à fuir. Que cela affaiblisse ou non le système de types dépend de vos cas d'utilisation - si vous essayez d'implémenter un sandbox d'exécution restreinte dans le langage pour assurer la sécurité, vous devez gérer ce type d'évasions…

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


Pendant ce temps, la conversion implicite est vraiment différente du système de type faible ou qui fuit.

Chaque langue, même Haskell, a pour fonction de convertir un entier en chaîne ou en float. Mais certaines langues effectueront automatiquement certaines de ces conversions, par exemple en C, si vous appelez une fonction qui veut un float et que vous le transmettez dans int, elle est convertie pour vous. Cela peut certainement conduire à des bogues avec, par exemple, des débordements inattendus, mais ce ne sont pas les mêmes bogues que ceux que vous obtenez avec 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 de manière plus explicite.

Et avec les langages dynamiques, c'est assez trouble. Il n’existe pas de "fonction qui veut un flottant" dans Python ou Perl. Mais il existe des fonctions surchargées qui font différentes choses avec différents types, et il existe une forte impression intuitive 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 fait beaucoup moins ("a" + 1 déclenche une exception, mais 1.0 + 1 vous donne 2.0 *). Il est difficile de mettre ce sens en termes formels - pourquoi ne devrait-il pas exister un + prenant une chaîne et un int, alors qu'il existe évidemment d'autres fonctions, telles que l'indexation, qui le font?

* En fait, en Python moderne, cela peut être expliqué en termes de OO sous-typage, puisque isinstance(2, numbers.Real) est vrai. Je ne pense pas qu'il y ait de sens dans lequel 2 est une instance du type de chaîne en Perl ou JavaScript… bien que dans Tcl, c'est en fait, puisque tout est une instance de chaîne.


Enfin, il existe une autre définition, totalement orthogonale, du typage "fort" ou "faible", où "fort" signifie puissant/flexible/expressif.

Par exemple, Haskell vous permet de définir un type représentant un nombre, une chaîne, une liste de ce type ou une mappe de chaînes à ce type, ce qui constitue un moyen idéal 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 prenant une liste de T et savoir que les éléments sont de type T; d'autres langages, comme le Java ancien, vous obligeaient à utiliser une liste d'objets et à être abaissée. Mais au moins Java vous permet de créer de nouveaux types avec leurs propres méthodes; C vous permet seulement de créer des structures. Et BCPL n'avait même pas cela. Et ainsi de suite jusqu'à Assembly, 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, la frappe à l'aide de canards vous permet de simuler tout ce que vous pouvez faire à Haskell et même certaines choses que vous ne pouvez pas; Bien sûr, les erreurs sont interceptées au moment de l'exécution au lieu de la compilation, mais elles le sont toujours. Cependant, dans certains cas, la frappe à l'aide de canards n'est pas suffisante. Par exemple, dans Haskell, vous pouvez indiquer qu'une liste vide d'ints est une liste d'ints. Vous pouvez donc décider que réduire + sur cette liste doit renvoyer 0 *; en Python, une liste vide est une liste vide; il n'y a aucune information de type pour vous aider à décider de ce que devrait être la réduction de +.

* En fait, Haskell ne vous laisse pas faire cela. si vous appelez la fonction de réduction qui ne prend pas une valeur de départ sur une liste vide, vous obtenez une erreur. Mais son système de types est suffisamment puissant pour que vous puissiez le faire, ce que Python ne fait pas.

50
abarnert

Vous confondez 'fortement typé' avec 'dynamiquement typé' .

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 le modifier pendant l'exécution du programme.

Le contraire du typage dynamique est le typage statique; la déclaration des types de variable ne change pas pendant la durée de vie d'un programme. Le contraire du typage fort est le typage faible; le type de valeurs peut changer pendant la durée de vie d'un programme.

32
Martijn Pieters

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

Vous pensez peut-être à statiquement langues où les types ne peuvent pas changer pendant l'exécution du programme et où la vérification du type a lieu pendant la compilation pour détecter erreurs possibles.

Cette question SO pourrait présenter un intérêt: Langages de types dynamiques versus langages de types statiques et cet article de Wikipedia sur Type Systems fournit plus d'informations

24
Levon

TLDR;

Le typage de Python est dynamique afin que vous puissiez changer une variable int en chaîne

x = 'somestring'
x = 50

Le typage Python est Fort , vous ne pouvez donc pas fusionner les types:

'x' + 3 --> TypeError: cannot concatenate 'str' and 'int' objects

Cela arrive en Javascript faiblement typé ...

 'x'+3 = 'x3'

Concernant l'inférence de type

Java vous oblige à déclarer explicitement vos types d'objet

int x = 50

Kotlin utilise l'inférence pour se rendre compte qu'il s'agit d'un int

x = 50

Mais comme les deux langages utilisent des types statiques , x ne peut pas être modifié par rapport à un int. Aucune langue ne permettrait un changement dynamique comme

x = 50
x = 'now a string'
14
Adam Hughes

Il a déjà été répondu à quelques reprises, 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'

Les éléments suivants en JavaScript:

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

C'est la différence entre un typage faible et un typage fort. Les types faibles essaient automatiquement de convertir d'un type à un autre, en fonction du contexte (par exemple, Perl). Les types forts ne convertissent jamais de manière implicite.

Votre confusion réside dans une incompré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 type, vous pouvez donc faire les choses suivantes:

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 le légèrement lié mais plus avancé:

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

8
Wayne Werner

Le terme "typage fort" n'a pas de définition précise.

Par conséquent, l'utilisation du terme dépend de votre interlocuteur.

Je ne considère aucune langue dans laquelle le type d'une variable n'est pas explicitement déclaré ou typé de manière statique soit fortement typée.

Le typage fort n’empêche pas uniquement la conversion (par exemple, la conversion "automatique" d’un entier en une chaîne). Cela exclut l’affectation (c’est-à-dire changer le type d’une variable).

Si le code suivant est compilé (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 se trouve dans le même bloc), sans avoir à examiner le code intervenant.

6
user5330045

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’affectation de la référence non typée à l’objet affecté, c’est-à-dire que l’objet est partagé via les références d'origine 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 de type (forte) est effectuée lorsqu'une opération avec la valeur est effectuée (durée d'exécution).

En d’autres termes, les variables (techniquement) n’ont pas de type - cela n’a aucun sens 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 réellement en termes de type d'objet cible.

6
pepr

Je viens de découvrir un superbe moyen concis de le mémoriser:

.

je pense que cet exemple simple devrait vous expliquer les différences entre le typage fort et dynamique:

>>> tup = ('1', 1, .1)
>>> for item in tup:
...     type(item)
...
<type 'str'>
<type 'int'>
<type 'float'>
>>>

Java:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }
0
Dmitry Zagorulkin