A menos que eu esteja enganado, a criação de uma função no Python funciona assim:
def my_func(param1, param2):
# stuff
No entanto, você não fornece os tipos desses parâmetros. Além disso, se bem me lembro, o Python é uma linguagem fortemente tipada; portanto, parece que o Python não deve permitir que você transmita um parâmetro de um tipo diferente do esperado pelo criador da função. No entanto, como o Python sabe que o usuário da função está passando nos tipos adequados? O programa simplesmente morrerá se for do tipo errado, assumindo que a função realmente use o parâmetro? Você precisa especificar o tipo?
python
function
parameters
Leif Andersen
fonte
fonte
Respostas:
O Python é fortemente digitado porque cada objeto tem um tipo, todo objeto conhece seu tipo, é impossível usar acidentalmente ou deliberadamente um objeto de um tipo "como se" fosse um objeto de um tipo diferente e todas as operações elementares no objeto fossem delegado ao seu tipo.
Isso não tem nada a ver com nomes . Um nome no Python não "tem um tipo": se e quando um nome é definido, o nome se refere a um objeto e o objeto tem um tipo (mas isso não força de fato um tipo no nome : a nome é um nome).
Um nome em Python pode perfeitamente se referir a objetos diferentes em momentos diferentes (como na maioria das linguagens de programação, embora não todas) - e não há restrição no nome, de modo que, se ele já se referiu a um objeto do tipo X, é, em seguida, para sempre obrigado a referir-se apenas a outros objetos de X. restrições de tipo sobre nomes não fazem parte do conceito de "tipagem forte", embora alguns entusiastas de estática digitação (onde os nomes que se constrangidos, e em um estático, AKA de compilação tempo, moda também) fazem mau uso do termo dessa maneira.
fonte
try
/except
) ocorrerá quando e se for tentada uma operação que o objeto não suporta. No Python 3.5, agora você pode opcionalmente "especificar tipos" de argumentos, mas nenhum erro ocorre, per se, se a especificação for violada; a notação de digitação serve apenas para ajudar a separar ferramentas que executam análises, etc., não altera o comportamento do próprio Python.As outras respostas fizeram um bom trabalho ao explicar a digitação de patos e a resposta simples de tzot :
No entanto , uma coisa interessante mudou desde 2010 (quando a pergunta foi feita pela primeira vez), a saber, a implementação do PEP 3107 (implementada no Python 3). Agora você pode realmente especificar o tipo de um parâmetro e o tipo do tipo de retorno de uma função como esta:
Podemos ver aqui que são
pick
necessários 2 parâmetros, uma listal
e um número inteiroindex
. Também deve retornar um número inteiro.Então, aqui está implícito que
l
há uma lista de números inteiros que podemos ver sem muito esforço, mas para funções mais complexas pode ser um pouco confuso quanto ao que a lista deve conter. Também queremos que o valor padrãoindex
seja 0. Para resolver isso, você pode optar por escreverpick
assim:Observe que agora colocamos uma string como o tipo de
l
, que é sintaticamente permitido, mas não é bom para analisar programaticamente (que voltaremos mais tarde).É importante observar que o Python não aumentará
TypeError
se você passar um floatindex
, a razão para isso é um dos principais pontos na filosofia de design do Python: "Todos nós somos adultos concordantes aqui" , o que significa que você deve esteja ciente do que você pode passar para uma função e do que não pode. Se você realmente deseja escrever código que lança TypeErrors, pode usar aisinstance
função para verificar se o argumento passado é do tipo apropriado ou uma subclasse dele como esta:Mais sobre por que você raramente deve fazer isso e o que deve fazer é discutido na próxima seção e nos comentários.
O PEP 3107 não apenas melhora a legibilidade do código, mas também possui vários casos de uso adequados sobre os quais você pode ler aqui .
A anotação de tipo recebeu muito mais atenção no Python 3.5 com a introdução do PEP 484, que introduz um módulo padrão para dicas de tipo.
Essas dicas de tipo vieram do verificador de tipos mypy ( GitHub ), que agora é compatível com PEP 484 .
O módulo de digitação vem com uma coleção bastante abrangente de dicas de tipo, incluindo:
List
,Tuple
,Set
,Map
- paralist
,tuple
,set
emap
respectivamente.Iterable
- útil para geradores.Any
- quando poderia ser qualquer coisa.Union
- quando poderia ser qualquer coisa dentro de um conjunto especificado de tipos, em oposição aAny
.Optional
- quando pode ser Nenhum. Taquigrafia paraUnion[T, None]
.TypeVar
- usado com genéricos.Callable
- usado principalmente para funções, mas pode ser usado para outros callables.Essas são as dicas de tipo mais comuns. Uma lista completa pode ser encontrada na documentação do módulo de digitação .
Aqui está o exemplo antigo usando os métodos de anotação introduzidos no módulo de digitação:
Um recurso poderoso é o
Callable
que permite digitar métodos de anotação que assumem uma função como argumento. Por exemplo:O exemplo acima pode se tornar mais preciso com o uso de em
TypeVar
vez deAny
, mas isso foi deixado como um exercício para o leitor, pois acredito que já preenchi minha resposta com muitas informações sobre os maravilhosos novos recursos habilitados pela dica de tipo.Anteriormente, quando um código Python documentado com, por exemplo, Sphinx, algumas das funcionalidades acima podiam ser obtidas escrevendo-se documentos formatados da seguinte forma:
Como você pode ver, isso requer várias linhas extras (o número exato depende de quão explícito você deseja ser e de como formata sua string de documento). Mas agora deve ficar claro para você como o PEP 3107 fornece uma alternativa que é, de várias maneiras (superior?) Superior. Isso é especialmente verdadeiro em combinação com o PEP 484 , que, como vimos, fornece um módulo padrão que define uma sintaxe para essas dicas / anotações de tipo que podem ser usadas de tal maneira que sejam inequívocas e precisas, mas flexíveis. combinação poderosa.
Na minha opinião pessoal, esse é um dos maiores recursos do Python de todos os tempos. Mal posso esperar para as pessoas começarem a aproveitar o poder disso. Desculpe pela resposta longa, mas é isso que acontece quando fico animado.
Um exemplo de código Python que usa fortemente dicas de tipo pode ser encontrado aqui .
fonte
TypeError
, qual é o sentido de usarpick(l: list, index: int) -> int
como definição de uma linha? Ou entendi errado, não sei.__annotations__
atributo do objeto de função).def f(a) -> Tuple[int, int]:
Você não especifica um tipo. O método só falhará (em tempo de execução) se tentar acessar atributos que não estão definidos nos parâmetros passados.
Portanto, esta função simples:
... não falhará, não importa em que dois argumentos sejam passados.
No entanto, esta função:
... falhará em tempo de execução se
param1
eparam2
não tiverem atributos que possam ser nomeadosquack
.fonte
Muitos idiomas têm variáveis, que são de um tipo específico e têm um valor. Python não possui variáveis; possui objetos e você usa nomes para se referir a esses objetos.
Em outros idiomas, quando você diz:
então uma variável (normalmente inteira) altera seu conteúdo para o valor 1.
Em Python,
significa "use o nome a para se referir ao objeto 1 ". Você pode fazer o seguinte em uma sessão interativa do Python:
A função
type
é chamada com o objeto1
; como todo objeto conhece seu tipo, é fáciltype
descobrir o tipo e devolvê-lo.Da mesma forma, sempre que você define uma função
a função recebe dois objetos e os nomeia
param1
eparam2
, independentemente de seus tipos. Se você deseja garantir que os objetos recebidos sejam de um tipo específico, codifique sua função como se eles fossem do (s) tipo (s) necessário (s) e capture as exceções que são lançadas se não forem. As exceções lançadas são geralmenteTypeError
(você usou uma operação inválida) eAttributeError
(você tentou acessar um membro inexistente (os métodos também são membros)).fonte
O Python não é fortemente digitado no sentido de verificação estática ou do tipo em tempo de compilação.
A maioria dos códigos Python se enquadra no chamado "Duck Typing" - por exemplo, você procura por um método
read
em um objeto - não se importa se o objeto é um arquivo em disco ou soquete, você só quer ler N bytes dele.fonte
Como Alex Martelli explica ,
Leia o restante da postagem para obter informações úteis.
fonte
Python não se importa com o que você passa para suas funções. Quando você chama
my_func(a,b)
, as variáveis param1 e param2 retêm os valores de a e b. O Python não sabe que você está chamando a função com os tipos adequados e espera que o programador cuide disso. Se sua função for chamada com diferentes tipos de parâmetros, você pode quebrar o código acessando-os com blocos try / except e avaliar os parâmetros da maneira que desejar.fonte
Você nunca especifica o tipo; Python tem o conceito de digitação de pato ; basicamente, o código que processa os parâmetros fará certas suposições sobre eles - talvez chamando certos métodos que se espera que um parâmetro implemente. Se o parâmetro for do tipo errado, uma exceção será lançada.
Em geral, depende do seu código garantir que você esteja passando objetos do tipo adequado - não há compilador para impor isso com antecedência.
fonte
Há uma exceção notória da digitação de pato que vale a pena mencionar nesta página.
Quando a
str
função chama o__str__
método de classe, ele sutilmente verifica seu tipo:Como se Guido nos sugerisse qual exceção um programa deveria criar se encontrar um tipo inesperado.
fonte
No Python, tudo tem um tipo. Uma função Python fará o que for solicitado se o tipo de argumento a suportar.
Exemplo:
foo
adicionará tudo o que pode ser__add__
ed;) sem se preocupar muito com seu tipo. Portanto, para evitar falhas, você deve fornecer apenas as coisas que suportam a adição.fonte
Eu não vi isso mencionado em outras respostas, então vou adicionar isso ao pote.
Como outros já disseram, o Python não aplica o tipo nos parâmetros de função ou método. Supõe-se que você saiba o que está fazendo e que, se realmente precisar saber o tipo de algo que foi repassado, irá verificar e decidir o que fazer por si mesmo.
Uma das principais ferramentas para fazer isso é a função isinstance ().
Por exemplo, se eu escrever um método que espera obter dados binários de texto brutos, em vez das seqüências codificadas utf-8 normais, poderia verificar o tipo de parâmetros no caminho e adaptar-me ao que encontro ou gerar uma exceção para recusar.
O Python também fornece todos os tipos de ferramentas para cavar objetos. Se você é corajoso, pode até usar o importlib para criar seus próprios objetos de classes arbitrárias em tempo real. Eu fiz isso para recriar objetos de dados JSON. Tal coisa seria um pesadelo em uma linguagem estática como C ++.
fonte
Para usar efetivamente o módulo de digitação (novo no Python 3.5), inclua all (
*
).E você estará pronto para usar:
No entanto, ainda é possível usar nomes de tipo como
int
,list
,dict
, ...fonte
Eu implementei um wrapper se alguém quiser especificar tipos de variáveis.
Use-o como:
EDITAR
O código acima não funcionará se nenhum dos argumentos (ou retorno) for declarado. A edição a seguir pode ajudar, por outro lado, funciona apenas para kwargs e não verifica argumentos.
fonte