Python è fortemente tipizzato?


234

Ho trovato collegamenti che dicono che Python è un linguaggio fortemente tipizzato.

Tuttavia, ho pensato che in lingue fortemente tipizzate non si potesse fare questo:

bob = 1
bob = "bob"

Pensavo che un linguaggio fortemente tipizzato non accettasse il cambio di tipo in fase di esecuzione. Forse ho una definizione sbagliata (o troppo semplicistica) di tipi forti / deboli.

Quindi, Python è un linguaggio fortemente o debolmente tipizzato?

Risposte:


359

Python è fortemente tipizzato in modo dinamico.

  • La tipizzazione forte significa che il tipo di un valore non cambia in modi inaspettati. Una stringa contenente solo cifre non diventa magicamente un numero, come può accadere in Perl. Ogni cambio di tipo richiede una conversione esplicita.
  • La tipizzazione dinamica significa che gli oggetti (valori) di runtime hanno un tipo, al contrario della tipizzazione statica dove le variabili hanno un tipo.

Per quanto riguarda il tuo esempio

bob = 1
bob = "bob"

Questo funziona perché la variabile non ha un tipo; può nominare qualsiasi oggetto. Dopo bob=1, troverai che type(bob)ritorna int, ma dopo bob="bob", ritorna str. (Si noti che typeè una funzione regolare, quindi valuta il suo argomento, quindi restituisce il tipo di valore.)

In contrasto con i vecchi dialetti di C, che erano debolmente, digitati staticamente, in modo che puntatori e numeri interi fossero praticamente intercambiabili. (La moderna ISO C richiede conversioni in molti casi, ma il mio compilatore è ancora indulgente su questo per impostazione predefinita.)

Devo aggiungere che la tipizzazione forte vs. debole è più un continuum che una scelta booleana. C ++ ha una digitazione più forte di C (sono necessarie più conversioni), ma il sistema dei tipi può essere sovvertito usando i lanci dei puntatori.

La forza del sistema di tipi in un linguaggio dinamico come Python è davvero determinata da come le sue primitive e le sue funzioni di libreria rispondono a diversi tipi. Ad esempio, +è sovraccarico in modo che funzioni su due numeri o due stringhe, ma non una stringa e un numero. Questa è una scelta progettuale fatta quando è +stata implementata, ma in realtà non è una necessità che segue dalla semantica del linguaggio. In effetti, quando sovraccarichi +su un tipo personalizzato, puoi farlo implicitamente convertire qualsiasi cosa in un numero:

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)

L'istanza della classe Foopuò essere aggiunta ad altri oggetti:

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

Si osservi che anche se fortemente tipizzato Python è completamente soddisfacente con l'aggiunta di oggetti di tipo inted floate restituisce un oggetto di tipo float(per esempio, int(42) + float(1)ritorni 43.0). D'altra parte, a causa della discrepanza tra i tipi, Haskell si lamenterebbe se si provasse quanto segue (42 :: Integer) + (1 :: Float). Questo rende Haskell un linguaggio strettamente tipizzato, in cui i tipi sono del tutto disgiunti e solo una forma controllata di sovraccarico è possibile tramite le classi di tipi.


18
Un esempio che non vedo molto spesso, ma penso che sia importante mostrare che Python non è completamente tipizzato fortemente, sono tutte le cose che valutano booleane: docs.python.org/release/2.5.2/lib/truth.html
gsingh2011,

25
Non sono sicuro se questo è un contro esempio: le cose possono essere valutate come booleane, ma improvvisamente non "diventano" un booleano. È quasi come se qualcuno avesse implicitamente chiamato qualcosa come as_boolean (<value>), che non è lo stesso del tipo di oggetto che sta cambiando, giusto?
jbrendel,

15
Essere sinceri in un contesto booleano non è un controesempio, perché in realtà nulla viene convertito in Trueo False. Ma per quanto riguarda la promozione dei numeri? 1.0 + 2funziona altrettanto bene in Python come in Perl o C, anche se "1.0" + 2non lo è. Sono d'accordo con @jbrendel che questa non è davvero una conversione implicita, è solo un sovraccarico, ma nello stesso senso, Perl non sta facendo alcuna conversione implicita. Se le funzioni non hanno dichiarato i tipi di parametri, non è possibile che avvengano conversioni implicite.
Abarnert,

13
Un modo migliore di pensare alla tipizzazione forte è che quel tipo è importante quando si eseguono operazioni su una variabile. Se il tipo non è come previsto, un linguaggio che si lamenta è fortemente tipizzato (python / java) e uno che non lo è è digitato debolmente (javascript) I linguaggi tipizzati dinamicamente (python) sono quelli che consentono al tipo di una variabile di cambiare in runtime mentre i linguaggi tipizzati staticamente (java) non lo consentono una volta dichiarata una variabile.
Kashif,

2
@ gsingh2011 La verità è utile e non è una digitazione debole da sola, ma if isValid(value) - 1può verificarsi una perdita accidentale . Il booleano viene forzato in numero intero, che viene quindi valutato come un valore di verità. False - 1diventa verità e True - 1diventa falsa, portando a un errore imbarazzante a due livelli off-one-one da debug. In questo senso, Python è per lo più fortemente tipizzato; le coercizioni di tipo di solito non causano errori logici.
Aaron3468,

57

Ci sono alcune questioni importanti che penso che tutte le risposte esistenti abbiano perso.


Digitazione debole significa consentire l'accesso alla rappresentazione sottostante. In C, posso creare un puntatore a caratteri, quindi dire al compilatore che voglio usarlo come puntatore a numeri interi:

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

Su una piattaforma little-endian con numeri interi a 32 bit, questo idiventa un array di numeri 0x64636261e 0x00676665. In effetti, puoi persino lanciare i puntatori stessi su numeri interi (della dimensione appropriata):

intptr_t i = (intptr_t)&sz;

E ovviamente questo significa che posso sovrascrivere la memoria ovunque nel sistema. *

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

* Naturalmente i moderni sistemi operativi usano la memoria virtuale e la protezione della pagina in modo che io possa solo sovrascrivere la memoria del mio processo, ma non c'è nulla nella stessa C che offra tale protezione, come chiunque può mai aver codificato, diciamo, Classic Mac OS o Win16 può dirti.

Il Lisp tradizionale permetteva simili tipi di pirateria informatica; su alcune piattaforme, i float e le contro celle a doppia parola erano dello stesso tipo, e potevi semplicemente passare l'uno a una funzione aspettandoti l'altro e avrebbe "funzionato".

La maggior parte delle lingue oggi non è così debole come lo erano C e Lisp, ma molte di esse sono ancora in qualche modo trapelate. Ad esempio, qualsiasi linguaggio OO che ha un "downcast" non selezionato, * è una perdita di tipo: stai essenzialmente dicendo al compilatore "So che non ti ho dato abbastanza informazioni per sapere che è sicuro, ma sono abbastanza sicuro è "quando il punto centrale di un sistema di tipi è che il compilatore ha sempre abbastanza informazioni per sapere cosa è sicuro.

* Un downcast verificato non rende il sistema di tipi di lingua più debole solo perché sposta il controllo in fase di esecuzione. In tal caso, il polimorfismo dei sottotipi (ovvero chiamate di funzione virtuali o completamente dinamiche) sarebbe la stessa violazione del sistema dei tipi, e non credo che nessuno voglia dirlo.

Pochissimi linguaggi di "scripting" sono deboli in questo senso. Anche in Perl o Tcl, non puoi prendere una stringa e interpretare i suoi byte come un intero. * Ma vale la pena notare che in CPython (e allo stesso modo per molti altri interpreti per molte lingue), se sei davvero persistente, tu può utilizzare ctypesper caricare libpython, eseguire il cast di un oggetto su ida POINTER(Py_Object)e forzare la fuoriuscita del sistema di tipi. Il fatto che ciò renda debole il sistema dei tipi o meno dipende dai tuoi casi d'uso: se stai cercando di implementare un sandbox di esecuzione limitata in lingua per garantire la sicurezza, devi affrontare questo tipo di escape ...

* È possibile utilizzare una funzione come struct.unpackleggere i byte e creare un nuovo int su "come C rappresenterebbe questi byte", ma ovviamente non ha perdite; anche Haskell lo consente.


Nel frattempo, la conversione implicita è davvero una cosa diversa da un sistema di tipo debole o che perde.

Ogni lingua, anche Haskell, ha funzioni per, diciamo, convertire un numero intero in una stringa o in un float. Ma alcune lingue eseguiranno automaticamente alcune di quelle conversioni per te, ad esempio in C, se chiami una funzione che vuole una floate la passi int, viene convertita per te. Questo può sicuramente portare a bug con, ad esempio, overflow imprevisti, ma non sono gli stessi tipi di bug che si ottengono da un sistema di tipo debole. E C non sta davvero diventando più debole qui; puoi aggiungere un int e un float in Haskell, o anche concatenare un float a una stringa, devi solo farlo in modo più esplicito.

E con linguaggi dinamici, questo è piuttosto oscuro. Non esiste una cosa come "una funzione che vuole un float" in Python o Perl. Ma ci sono funzioni sovraccariche che fanno cose diverse con tipi diversi, e c'è un forte senso intuitivo che, ad esempio, aggiungere una stringa a qualcos'altro è "una funzione che vuole una stringa". In tal senso, Perl, Tcl e JavaScript sembrano fare molte conversioni implicite ( "a" + 1ti dà "a1"), mentre Python ne fa molte meno ( "a" + 1solleva un'eccezione, ma 1.0 + 1ti dà 2.0*). È solo difficile esprimere questo senso in termini formali: perché non dovrebbe esserci uno +che prende una stringa e un int, quando ci sono ovviamente altre funzioni, come l'indicizzazione, che lo fanno?

* In realtà, nel moderno Python, ciò può essere spiegato in termini di sottotipo OO, poiché isinstance(2, numbers.Real)è vero. Non penso che abbia senso in quale 2istanza sia del tipo stringa in Perl o JavaScript ... anche se in Tcl, in realtà lo è, poiché tutto è un'istanza di stringa.


Infine, c'è un'altra definizione, completamente ortogonale, di tipizzazione "forte" contro "debole", dove "forte" significa potente / flessibile / espressivo.

Ad esempio, Haskell ti consente di definire un tipo che è un numero, una stringa, un elenco di questo tipo o una mappa dalle stringhe a questo tipo, che è un modo perfetto per rappresentare tutto ciò che può essere decodificato da JSON. Non c'è modo di definire un tale tipo in Java. Ma almeno Java ha tipi parametrici (generici), quindi puoi scrivere una funzione che prende un Elenco di T e sapere che gli elementi sono di tipo T; altre lingue, come i primi Java, ti costrinsero a usare un Elenco di oggetti e downcast. Ma almeno Java ti consente di creare nuovi tipi con i loro metodi; C ti consente solo di creare strutture. E BCPL non aveva nemmeno quello. E così via fino all'assemblaggio, in cui gli unici tipi hanno lunghezze di bit diverse.

Quindi, in questo senso, il sistema di tipi di Haskell è più forte di quello moderno di Java, che è più forte di quello precedente di Java, che è più forte di quello di C, che è più forte di quello di BCPL.

Quindi, dove si inserisce Python in quello spettro? È un po 'complicato. In molti casi, la digitazione di anatre ti consente di simulare tutto ciò che puoi fare in Haskell e anche alcune cose che non puoi; certo, gli errori vengono rilevati in fase di esecuzione anziché in fase di compilazione, ma vengono comunque rilevati. Tuttavia, ci sono casi in cui la tipizzazione anatra non è sufficiente. Ad esempio, in Haskell, puoi dire che un elenco vuoto di ints è un elenco di ints, quindi puoi decidere che la riduzione +su quell'elenco dovrebbe restituire 0 *; in Python, un elenco vuoto è un elenco vuoto; non ci sono informazioni sul tipo per aiutarti a decidere cosa +dovrebbe fare la riduzione su di essa.

* In effetti, Haskell non ti consente di farlo; se si chiama la funzione di riduzione che non accetta un valore iniziale in un elenco vuoto, viene visualizzato un errore. Ma il suo sistema di tipi è abbastanza potente da permetterti di farlo funzionare, e quello di Python no.


3
Questa risposta è geniale! Peccato che sia rimasto così a lungo in fondo alla lista.
LeoR,

1
Solo un piccolo commento al tuo esempio C: char sz[]non è un puntatore a char, è un array di char e nell'assegnazione decade in puntatore.
majkel.mk,

39

Stai confondendo "fortemente tipizzato" con "tipizzato dinamicamente" .

Non posso cambiare il tipo di 1aggiungendo la stringa '12', ma posso scegliere quali tipi memorizzare in una variabile e cambiarlo durante il tempo di esecuzione del programma.

L'opposto della tipizzazione dinamica è la tipizzazione statica; la dichiarazione di tipi variabili non cambia durante la durata di un programma. L'opposto della tipizzazione forte è la digitazione debole; il tipo di valori può cambiare durante la durata di un programma.


La descrizione nel collegamento è fortemente tipizzata: "In genere, un linguaggio fortemente tipizzato ha regole di digitazione più rigorose in fase di compilazione, il che implica che è più probabile che si verifichino errori ed eccezioni durante la compilazione". implica che Python è un linguaggio debolmente tipizzato ..., wiki è sbagliato?
Piove

1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: non è affatto implicito. Python ha regole di digitazione rigide al momento della compilazione, ogni oggetto creato ha solo un tipo. E 'generalmente' non implica nulla, significa solo che Python è un'eccezione.
Martijn Pieters

24

Secondo questo articolo Python wiki, Python è sia tipicamente che fortemente tipizzato (fornisce anche una buona spiegazione).

Forse stai pensando a linguaggi tipicamente statici in cui i tipi non possono cambiare durante l'esecuzione del programma e il controllo del tipo si verifica durante il tempo di compilazione per rilevare possibili errori.

Questa domanda SO potrebbe essere di interesse: linguaggi di tipo dinamico rispetto a linguaggi di tipo statico e questo articolo di Wikipedia sui sistemi di tipo fornisce ulteriori informazioni


18

TLDR;

La digitazione di Python è Dinamica, quindi puoi cambiare una variabile stringa in un int

x = 'somestring'
x = 50

La digitazione Python è Forte, quindi non puoi unire i tipi:

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

Nel Javascript debolmente tipato questo succede ...

 'foo'+3 = 'foo3'

Per quanto riguarda l'inferenza del tipo

Java ti costringe a dichiarare esplicitamente i tuoi tipi di oggetto

int x = 50

Kotlin usa l'inferenza per rendersi conto che è unint

x = 50

Ma poiché entrambe le lingue utilizzano tipi statici , xnon può essere modificata da un int. Nessuna delle due lingue consentirebbe un cambiamento dinamico come

x = 50
x = 'now a string'

Non conosco i dettagli di Javascript ma 'x' + 3potrebbe essere operator+sovraccarico e fare la conversione del tipo dietro la scena?
Piove

3
Ad ogni modo, la tua risposta è in realtà più concisa e di facile comprensione rispetto a quelle sopra.
Piove

8

Ha già ricevuto risposta alcune volte, ma Python è un linguaggio fortemente tipizzato:

>>> 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'

Quanto segue in JavaScript:

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

Questa è la differenza tra digitazione debole e digitazione forte. I tipi deboli provano automaticamente a convertire da un tipo a un altro, a seconda del contesto (ad es. Perl). I tipi forti non si convertono mai implicitamente.

La tua confusione sta in un malinteso su come Python lega i valori ai nomi (comunemente indicati come variabili).

In Python, i nomi non hanno tipi, quindi puoi fare cose come:

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

E i nomi possono essere associati a qualsiasi cosa:

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

Per ulteriori letture:

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

e leggermente correlato ma più avanzato:

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


1
Diversi anni dopo - un'altra risorsa utile e pertinente: youtu.be/_AEJHKGk9ns
Wayne Werner

La digitazione forte o debole non ha nulla a che fare con il tipo di espressioni risultante come 3 + '4'. JavaScript è forte quanto Python per questo esempio.
qznc,

@qznc come è Javasript altrettanto forte? Non credo di aver intimato che avesse qualcosa a che fare con il tipo risultante, anzi dichiaro esplicitamente che i tipi deboli provano automaticamente a convertire da un tipo a un altro .
Wayne Werner,

2
@oneloop non è necessariamente vero, è solo che il comportamento per combinare float e ints è ben definito e si traduce in un float. Puoi farlo anche "3"*4in Python. Il risultato ovviamente è "3333". Non diresti che sta convertendo entrambe le cose. Naturalmente questo potrebbe essere solo discutere la semantica.
Wayne Werner,

1
@oneloop Non è necessariamente vero che, poiché Python produce floatdalla combinazione di floate intche sta convertendo il tipo implicitamente. Esiste una relazione naturale tra float e int, e in effetti il tipo di gerarchia lo spiega. Suppongo che potresti argomentare che Javascript considera '3'+4ed 'e'+4essere entrambe operazioni ben definite nello stesso modo in cui Python considera 3.0 + 4ben definite, ma a quel punto non ci sono davvero tipi forti o deboli, solo (non) definiti operazioni.
Wayne Werner,

6

Una variabile Python memorizza un riferimento non tipizzato all'oggetto target che rappresenta il valore.

Qualsiasi operazione di assegnazione significa assegnare il riferimento non tipizzato all'oggetto assegnato, ovvero l'oggetto viene condiviso tramite i riferimenti originali e nuovi (contati).

Il tipo di valore è associato all'oggetto target, non al valore di riferimento. Il controllo (forte) del tipo viene eseguito quando viene eseguita un'operazione con il valore (tempo di esecuzione).

In altre parole, le variabili (tecnicamente) non hanno alcun tipo - non ha senso pensare in termini di un tipo di variabile se si vuole essere esatti. Ma i riferimenti sono automaticamente dedotti e in realtà pensiamo in termini di tipo dell'oggetto target.


6

Il termine "tipizzazione forte" non ha una definizione definita.

Pertanto, l'uso del termine dipende da con chi stai parlando.

Non considero alcuna lingua, in cui il tipo di una variabile non sia esplicitamente dichiarato, né tipizzato staticamente per essere fortemente tipizzato.

La tipizzazione forte non preclude solo la conversione (ad esempio, la conversione "automatica" da un numero intero in una stringa). Impedisce l'assegnazione (ovvero la modifica del tipo di una variabile).

Se il seguente codice viene compilato (interpreta), la lingua non è tipizzata in modo forte:

Foo = 1 Foo = "1"

In una lingua fortemente tipizzata, un programmatore può "contare" su un tipo.

Ad esempio, se un programmatore vede la dichiarazione,

UINT64 kZarkCount;

e lui o lei sa che dopo 20 righe, kZarkCount è ancora un UINT64 (purché si verifichi nello stesso blocco) - senza dover esaminare il codice intermedio.


1

Ho appena scoperto un modo superbo e conciso per memorizzarlo:

Espansione tipizzata dinamica / statica; valore tipizzato fortemente / debolmente.


0

penso, questo semplice esempio dovresti spiegare le differenze tra la tipizzazione forte e dinamica:

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

Giava:

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

Il codice Python mostra la digitazione dinamica mentre java mostra la scrittura statica. Un esempio migliore sarebbe $ var = '2' + 1 // il risultato è 3
erichlf

@ivleph sono d'accordo. è anche possibile scrivere qualcosa del genere: "a" * 3 == "aaa"
Dmitry Zagorulkin,

-4
class testme(object):
    ''' A test object '''
    def __init__(self):
        self.y = 0

def f(aTestMe1, aTestMe2):
    return aTestMe1.y + aTestMe2.y




c = testme            #get a variable to the class
c.x = 10              #add an attribute x inital value 10
c.y = 4               #change the default attribute value of y to 4

t = testme()          # declare t to be an instance object of testme
r = testme()          # declare r to be an instance object of testme

t.y = 6               # set t.y to a number
r.y = 7               # set r.y to a number

print(f(r,t))         # call function designed to operate on testme objects

r.y = "I am r.y"      # redefine r.y to be a string

print(f(r,t))         #POW!!!!  not good....

Quanto sopra creerebbe un incubo di codice non mantenibile in un sistema di grandi dimensioni per un lungo periodo di tempo. Chiamalo come vuoi, ma la possibilità di "dinamicamente" cambiare un tipo di variabile è solo una cattiva idea ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.