Python é fortemente digitado?

234

Encontrei links que dizem que o Python é uma linguagem fortemente tipada.

No entanto, pensei que em idiomas fortemente tipados você não poderia fazer isso:

bob = 1
bob = "bob"

Eu pensei que uma linguagem fortemente tipada não aceitava alteração de tipo em tempo de execução. Talvez eu tenha uma definição errada (ou simplista) de tipos fortes / fracos.

Então, o Python é uma linguagem de tipo forte ou fraco?

Pacane
fonte

Respostas:

359

Python é fortemente digitado dinamicamente.

  • Digitação forte significa que o tipo de um valor não muda de maneira inesperada. Uma string contendo apenas dígitos não se torna magicamente um número, como pode acontecer no Perl. Toda mudança de tipo requer uma conversão explícita.
  • A digitação dinâmica significa que os objetos (valores) de tempo de execução têm um tipo, em oposição à digitação estática, onde as variáveis ​​têm um tipo.

Quanto ao seu exemplo

bob = 1
bob = "bob"

Isso funciona porque a variável não possui um tipo; pode nomear qualquer objeto. Depois bob=1, você verá que type(bob)retorna int, mas depois bob="bob"retorna str. (Observe que typeé uma função regular, portanto, ele avalia seu argumento e, em seguida, retorna o tipo do valor.)

Compare isso com os dialetos mais antigos de C, que eram fracamente digitados estaticamente, de modo que ponteiros e números inteiros eram praticamente intercambiáveis. (O ISO C moderno requer conversões em muitos casos, mas meu compilador ainda é indulgente com isso por padrão.)

Devo acrescentar que a tipagem forte versus fraca é mais um continuum do que uma escolha booleana. O C ++ possui digitação mais forte que o C (são necessárias mais conversões), mas o sistema de tipos pode ser subvertido usando conversões de ponteiro.

A força do sistema de tipos em uma linguagem dinâmica como Python é realmente determinada pela forma como suas primitivas e funções de biblioteca respondem a diferentes tipos. Por exemplo, +está sobrecarregado para funcionar em dois números ou duas strings, mas não em uma string e um número. Essa é uma escolha de design feita quando +foi implementada, mas não é realmente uma necessidade decorrente da semântica da linguagem. De fato, quando você sobrecarrega +um tipo personalizado, é possível convertê-lo implicitamente em um número:

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)

A instância da classe Foopode ser adicionada a outros objetos:

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

Observe que, apesar de fortemente tipado Python é completamente bem com a adição de objetos do tipo inte floate retorna um objeto do tipo float(por exemplo, int(42) + float(1)retornos 43.0). Por outro lado, devido à incompatibilidade entre os tipos, Haskell se queixaria se alguém tentasse o seguinte (42 :: Integer) + (1 :: Float). Isso faz do Haskell uma linguagem estritamente digitada, onde os tipos são totalmente disjuntos e apenas uma forma controlada de sobrecarga é possível através de classes de tipos.

Fred Foo
fonte
18
Um exemplo que não vejo com muita frequência, mas acho importante mostrar que o Python não é completamente fortemente tipado, são todas as coisas que avaliam como booleanas: docs.python.org/release/2.5.2/lib/truth.html
precisa saber é o seguinte
25
Não tenho tanta certeza se este é um exemplo contrário: as coisas podem ser avaliadas como booleanas, mas de repente não se tornam "booleanas". É quase como se alguém implicitamente chamasse algo como as_boolean (<valor>), que não é o mesmo que o tipo de objeto que está mudando, certo?
jbrendel
15
Ser sincero no contexto booleano não é um contra-exemplo, porque nada está realmente sendo convertido em Trueou False. Mas e a promoção de números? 1.0 + 2funciona tão bem em Python quanto em Perl ou C, mesmo que "1.0" + 2não. Concordo com @jbrendel que essa não é realmente uma conversão implícita, é apenas uma sobrecarga - mas, no mesmo sentido, o Perl também não está fazendo nenhuma conversão implícita. Se as funções não tiverem tipos de parâmetro declarados, não haverá lugar para conversões implícitas.
abarnert
13
Uma maneira melhor de pensar sobre digitação forte é que esse tipo importa ao executar operações em uma variável. Se o tipo não for o esperado, uma linguagem que reclama é fortemente tipada (python / java) e uma que não é, é tipicamente fracamente (javascript). Linguagens dinamicamente tipadas (python) são aquelas que permitem que o tipo de uma variável seja alterado em tempo de execução, enquanto os idiomas estaticamente tipados (java) não permitem isso depois que uma variável é declarada.
Kashif
2
@ gsingh2011 A verdade é útil e não é digitação fraca por si só, mas um acidente if isValid(value) - 1pode vazar. O booleano é coagido a um número inteiro, que é então avaliado como um valor de verdade. False - 1torna- True - 1se verdadeiro e falso, levando a um erro de duas camadas embaraçosamente difícil de depurar. Nesse sentido, o python é tipicamente fortemente tipado; coerções de tipo geralmente não causam erros lógicos.
Aaron3468
57

Há algumas questões importantes que acho que todas as respostas existentes perderam.


Digitação fraca significa permitir acesso à representação subjacente. Em C, posso criar um ponteiro para caracteres e dizer ao compilador que quero usá-lo como ponteiro para números inteiros:

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

Em uma plataforma little-endian com números inteiros de 32 bits, isso cria iuma matriz dos números 0x64636261e 0x00676665. De fato, você pode até converter ponteiros para números inteiros (do tamanho apropriado):

intptr_t i = (intptr_t)&sz;

E é claro que isso significa que posso substituir a memória em qualquer lugar do sistema. *

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

* É claro que os sistemas operacionais modernos usam memória virtual e proteção de página para que eu possa sobrescrever a memória do meu próprio processo, mas não há nada no C que ofereça essa proteção, como qualquer pessoa que já tenha codificado, digamos, no Classic Mac OS ou no Win16 pode lhe dizer.

Lisp tradicional permitia tipos semelhantes de hackery; em algumas plataformas, as células flutuantes e contras de duas palavras eram do mesmo tipo, e você poderia simplesmente passar uma para uma função que espera a outra e ela funcionaria.

Atualmente, a maioria dos idiomas não é tão fraca quanto C e Lisp, mas muitos deles ainda são um pouco vazados. Por exemplo, qualquer linguagem OO que tenha um "downcast" desmarcado, * é um vazamento de tipo: você está basicamente dizendo ao compilador "Eu sei que não lhe dei informações suficientes para saber que isso é seguro, mas tenho certeza é "quando o ponto principal de um sistema de tipos é que o compilador sempre tem informações suficientes para saber o que é seguro.

* Um downcast verificado não torna o sistema de tipos do idioma mais fraco apenas porque move a verificação para o tempo de execução. Se isso acontecesse, o subtipo de polimorfismo (também conhecido como chamadas de função virtuais ou totalmente dinâmicas) seria a mesma violação do sistema de tipos, e acho que ninguém quer dizer isso.

Pouquíssimas linguagens de "script" são fracas nesse sentido. Mesmo em Perl ou Tcl, você não pode pegar uma string e apenas interpretar seus bytes como um número inteiro. * Mas vale a pena notar que no CPython (e da mesma forma para muitos outros intérpretes para muitas linguagens), se você é realmente persistente, pode ser usado ctypespara carregar libpython, converter um objeto em ida POINTER(Py_Object)e forçar o vazamento do sistema de tipos. Se isso enfraquece ou não o sistema de tipos depende de seus casos de uso - se você estiver tentando implementar uma sandbox de execução restrita no idioma para garantir a segurança, precisará lidar com esses tipos de escapes ...

* Você pode usar uma função como struct.unpackler os bytes e criar um novo int de "como C representaria esses bytes", mas obviamente isso não é vazado; até Haskell permite isso.


Enquanto isso, a conversão implícita é realmente algo diferente de um sistema do tipo fraco ou com vazamento.

Todo idioma, mesmo Haskell, tem funções para, digamos, converter um número inteiro em uma string ou um float. Porém, alguns idiomas farão algumas dessas conversões automaticamente para você - por exemplo, em C, se você chamar uma função que deseja a floate passá-la int, ela será convertida para você. Definitivamente, isso pode levar a erros com, por exemplo, estouros inesperados, mas eles não são os mesmos tipos de erros que você obtém de um sistema de tipos fracos. E C realmente não está sendo mais fraco aqui; você pode adicionar um int e um float no Haskell ou até concatenar um float em uma string, basta fazer isso de forma mais explícita.

E com linguagens dinâmicas, isso é bastante obscuro. Não existe algo como "uma função que queira flutuar" em Python ou Perl. Mas existem funções sobrecarregadas que fazem coisas diferentes com tipos diferentes, e há uma forte sensação intuitiva de que, por exemplo, adicionar uma string a outra coisa é "uma função que deseja uma string". Nesse sentido, Perl, Tcl e JavaScript parecem fazer muitas conversões implícitas ( "a" + 1dá a você "a1"), enquanto o Python faz muito menos ( "a" + 1gera uma exceção, mas 1.0 + 1dá a você 2.0*). É difícil colocar esse sentido em termos formais - por que não deveria haver um +que tenha uma string e um int, quando obviamente existem outras funções, como a indexação, que fazem?

* Na verdade, no Python moderno, isso pode ser explicado em termos de subtipo OO, pois isinstance(2, numbers.Real)é verdade. Eu não acho que exista algum sentido em que 2seja uma instância do tipo string em Perl ou JavaScript ... embora em Tcl, na verdade seja, pois tudo é uma instância de string.


Finalmente, há outra definição completamente ortogonal de digitação "forte" vs. "fraca", em que "forte" significa poderoso / flexível / expressivo.

Por exemplo, Haskell permite definir um tipo que é um número, uma sequência, uma lista desse tipo ou um mapa de sequências para esse tipo, que é uma maneira perfeita de representar qualquer coisa que possa ser decodificada no JSON. Não há como definir esse tipo em Java. Mas pelo menos Java possui tipos paramétricos (genéricos), para que você possa escrever uma função que obtenha uma Lista de T e saiba que os elementos são do tipo T; outras linguagens, como o Java inicial, forçaram você a usar uma lista de objetos e fazer o downcast. Mas pelo menos Java permite criar novos tipos com seus próprios métodos; C apenas permite criar estruturas. E o BCPL nem sequer tinha isso. E assim por diante, até a montagem, onde os únicos tipos têm diferentes comprimentos de bits.

Portanto, nesse sentido, o sistema de tipos de Haskell é mais forte que o Java moderno, que é mais forte que o Java anterior, que é mais forte que o C, que é mais forte que o BCPL.

Então, onde o Python se encaixa nesse espectro? Isso é um pouco complicado. Em muitos casos, a digitação com patos permite simular tudo o que você pode fazer em Haskell e até algumas coisas que não pode; Certamente, os erros são detectados no tempo de execução, em vez do tempo de compilação, mas ainda são detectados. No entanto, há casos em que a digitação com patos não é suficiente. Por exemplo, em Haskell, você pode dizer que uma lista vazia de ints é uma lista de ints, para poder decidir que a redução +nessa lista deve retornar 0 *; no Python, uma lista vazia é uma lista vazia; não há informações de tipo para ajudá-lo a decidir o que a redução +deve fazer.

* De fato, Haskell não deixa você fazer isso; se você chamar a função de redução que não assume um valor inicial em uma lista vazia, você recebe um erro. Mas seu sistema de tipos é poderoso o suficiente para que você possa fazer isso funcionar, e o Python não é.

abarnert
fonte
3
Esta resposta é brilhante! Uma pena que ela tenha ficado por tanto tempo no final da lista.
LeoR 28/05
1
Apenas um pequeno comentário ao seu exemplo em C: char sz[]não é um ponteiro para char, é uma matriz de char e, na atribuição, ele se decompõe em ponteiro.
majkel.mk
39

Você está confundindo 'digitado fortemente' com 'digitado dinamicamente' .

Não posso alterar o tipo de 1adição da string '12', mas posso escolher quais tipos armazeno em uma variável e alterá-lo durante o tempo de execução do programa.

O oposto da digitação dinâmica é a estática; a declaração dos tipos de variáveis não muda durante a vida útil de um programa. O oposto de digitação forte é digitação fraca; o tipo de valores pode mudar durante a vida útil de um programa.

Martijn Pieters
fonte
A descrição no link digitou fortemente: "Geralmente, uma linguagem fortemente tipada possui regras de digitação mais rigorosas no momento da compilação, o que implica que é mais provável que erros e exceções ocorram durante a compilação." implica que Python é uma linguagem de tipo fraco ..., o wiki está errado?
Chovendo
1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: isso não está implícito. Python possui regras rígidas de digitação em tempo de compilação, cada objeto criado possui apenas um tipo. E 'geralmente' não implica nada, apenas significa que o Python é uma exceção a isso.
Martijn Pieters
24

De acordo com este artigo do wiki do Python, o Python é do tipo dinâmico e fortemente tipado (fornece uma boa explicação também).

Talvez você esteja pensando em linguagens de tipo estaticamente onde os tipos não podem mudar durante a execução do programa e a verificação de tipos ocorre durante o tempo de compilação para detectar possíveis erros.

Esta pergunta do SO pode ser interessante: Linguagens de tipos dinâmicas versus linguagens de tipos estáticas e este artigo da Wikipedia em Sistemas de tipos fornece mais informações

Levon
fonte
18

TLDR;

A digitação do Python é Dinâmica, para que você possa alterar uma variável de string para uma int

x = 'somestring'
x = 50

A digitação em Python é Forte, portanto você não pode mesclar tipos:

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

Em Javascript de tipo fraco, isso acontece ...

 'foo'+3 = 'foo3'

Em relação à inferência de tipo

Java força você a declarar explicitamente seus tipos de objetos

int x = 50

Kotlin usa inferência para perceber que é umint

x = 50

Mas como os dois idiomas usam tipos estáticos , xnão pode ser alterado de um int. Nenhum idioma permitiria uma mudança dinâmica como

x = 50
x = 'now a string'
Adam Hughes
fonte
Não sei os detalhes do Javascript, mas 'x' + 3pode estar operator+sobrecarregando e fazendo a conversão de tipo nos bastidores?
Chovendo
3
De qualquer forma, sua resposta é realmente mais concisa e fácil de entender do que as anteriores.
Chovendo
8

Já foi respondido algumas vezes, mas Python é uma linguagem fortemente tipada:

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

O seguinte em JavaScript:

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

Essa é a diferença entre digitação fraca e digitação forte. Tipos fracos tentam converter automaticamente de um tipo para outro, dependendo do contexto (por exemplo, Perl). Tipos fortes nunca se convertem implicitamente.

Sua confusão está em um mal-entendido de como o Python vincula valores a nomes (geralmente chamados de variáveis).

No Python, os nomes não têm tipos, então você pode fazer coisas como:

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

E os nomes podem ser vinculados a qualquer coisa:

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

Para leitura adicional:

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

e os ligeiramente relacionados, mas mais avançados:

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

Wayne Werner
fonte
1
Vários anos depois - outro recurso útil e relevante: youtu.be/_AEJHKGk9ns
Wayne Werner
A digitação forte vs fraca não tem nada a ver com o tipo de resultado de expressões como 3 + '4'. JavaScript é tão forte quanto Python para este exemplo.
Qznc 26/09/2015
@qznc como Javasript é tão forte? Não acredito que tenha sugerido que isso tenha alguma coisa a ver com o tipo resultante. Na verdade, declaro explicitamente que tipos fracos tentam converter automaticamente de um tipo para outro .
Wayne Werner
2
@oneloop isso não é necessariamente verdade, é apenas que o comportamento para combinar floats e ints é bem definido e resulta em um float. Você pode fazer "3"*4em python também. O resultado, claro, é "3333". Você não diria que está convertendo qualquer coisa. Claro que isso poderia ser apenas argumentar semântica.
Wayne Werner
1
@oneloop Não é necessariamente verdade que, porque o Python produz a floatpartir da combinação de floate intque está convertendo o tipo implicitamente. Existe uma relação natural entre float e int e, de fato, a hierarquia de tipos explica isso. Suponho que você possa argumentar que o Javascript considera '3'+4e 'e'+4ambas são operações bem definidas da mesma maneira que o Python considera 3.0 + 4bem definidas, mas nesse momento não há realmente tipos fortes ou fracos, apenas (des) definidos operações.
Wayne Werner
6

Uma variável Python armazena uma referência sem tipo para o objeto de destino que representa o valor.

Qualquer operação de atribuição significa atribuir a referência não digitada ao objeto atribuído - ou seja, o objeto é compartilhado através das referências originais e novas (contadas).

O tipo de valor é vinculado ao objeto de destino, não ao valor de referência. A verificação do tipo (forte) é feita quando uma operação com o valor é executada (tempo de execução).

Em outras palavras, as variáveis ​​(tecnicamente) não têm tipo - não faz sentido pensar em termos de um tipo de variável se alguém quiser ser exato. Mas as referências são automaticamente desreferenciadas e, na verdade, pensamos em termos do tipo do objeto de destino.

pepr
fonte
6

O termo "digitação forte" não tem uma definição definida.

Portanto, o uso do termo depende de com quem você está falando.

Não considero nenhum idioma, no qual o tipo de uma variável não seja declarado explicitamente ou digitado estaticamente para ser fortemente digitado.

A digitação forte não exclui apenas a conversão (por exemplo, a conversão "automática" de um número inteiro para uma string). Isso impede a atribuição (ou seja, alterar o tipo de uma variável).

Se o seguinte código for compilado (interpretado), o idioma não será do tipo forte:

Foo = 1 Foo = "1"

Em uma linguagem fortemente tipada, um programador pode "contar" com um tipo.

Por exemplo, se um programador vê a declaração,

UINT64 kZarkCount;

e ele sabe que, 20 linhas depois, o kZarkCount ainda é um UINT64 (desde que ocorra no mesmo bloco) - sem ter que examinar o código intermediário.

user5330045
fonte
1

Acabei de descobrir uma excelente maneira concisa de memorizá-lo:

Expansão digitada dinâmica / estática; valor forte / fracamente digitado.

Chovendo
fonte
0

Eu acho que, neste exemplo simples, você deve explicar as diferenças entre a digitação forte e a dinâmica:

>>> 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
    }
Dmitry Zagorulkin
fonte
Seu código python demonstra digitação dinâmica, enquanto o java demonstra digitação estática. Um exemplo melhor seria $ var = '2' + 1 // resultado é 3
erichlf 25/02
@ivleph eu concordo. também é possível escrever algo como isto: "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....

O exemplo acima criaria um pesadelo de código impossível de manter em um sistema grande por um longo período. Chame como quiser, mas a capacidade de "dinamicamente" alterar um tipo de variável é apenas uma má idéia ...

Ryan Alexander
fonte