Eu sugeriria ler PEP 483 e PEP 484 e assistir a esta apresentação de Guido sobre Type Hinting.
Em poucas palavras : dica de tipo é literalmente o que as palavras significam, você sugere o tipo de objeto (s) que está usando .
Devido à natureza dinâmica do Python, é especialmente difícil inferir ou verificar o tipo de um objeto que está sendo usado. Esse fato dificulta que os desenvolvedores entendam exatamente o que está acontecendo no código que eles não escreveram e, mais importante, para as ferramentas de verificação de tipo encontradas em muitos IDEs [PyCharm, PyDev vêm à mente] que são limitados devido ao fato de que eles não têm nenhum indicador de que tipo são os objetos. Como resultado, eles recorrem à tentativa de inferir o tipo com (como mencionado na apresentação) cerca de 50% de taxa de sucesso.
Para tirar dois slides importantes da apresentação Tip Hinting:
Por que digitar dicas?
- Ajuda a digitar verificadores: ao sugerir que tipo de objeto você deseja que o objeto possa detectar facilmente, por exemplo, você está passando um objeto com um tipo que não é esperado.
- Ajuda na documentação: uma terceira pessoa que visualiza seu código saberá o que é esperado onde, portanto, como usá-lo sem obtê-lo
TypeErrors
.
- Ajuda os IDEs a desenvolver ferramentas mais precisas e robustas: Os ambientes de desenvolvimento serão mais adequados para sugerir métodos apropriados quando souberem que tipo de objeto é. Você provavelmente já experimentou isso com algum IDE em algum momento, pressionando
.
e exibindo os métodos / atributos que não estão definidos para um objeto.
Por que usar damas de tipo estático?
- Encontre erros mais cedo : acredito que isso é evidente.
- Quanto maior o seu projeto, mais você precisa : Novamente, faz sentido. Idiomas estáticos oferecem robustez e controle que os idiomas dinâmicos não possuem. Quanto maior e mais complexa sua aplicação se tornar, mais controle e previsibilidade (de um aspecto comportamental) você precisará.
- Equipes grandes já estão executando análises estáticas : acho que isso verifica os dois primeiros pontos.
Como uma nota final para esta pequena introdução : esse é um recurso opcional e, pelo que entendi, foi introduzido para colher alguns dos benefícios da digitação estática.
Geralmente, você não precisa se preocupar com isso e definitivamente não precisa usá-lo (especialmente nos casos em que você usa o Python como uma linguagem de script auxiliar). Deve ser útil no desenvolvimento de grandes projetos, pois oferece robustez, controle e recursos adicionais de depuração .
Digite Hinting with mypy :
Para tornar essa resposta mais completa, acho que uma pequena demonstração seria adequada. Vou usar mypy
a biblioteca que inspirou as dicas de tipo, como são apresentadas no PEP. Isso é escrito principalmente para quem se deparar com essa questão e se perguntar por onde começar.
Antes de fazer isso, permita-me reiterar o seguinte: O PEP 484 não impõe nada; é simplesmente definir uma direção para anotações de funções e propor diretrizes sobre como a verificação de tipo pode / deve ser realizada. Você pode anotar suas funções e sugerir quantas coisas quiser; seus scripts ainda serão executados independentemente da presença de anotações, porque o próprio Python não os utiliza.
De qualquer forma, como observado no PEP, os tipos de dicas geralmente devem assumir três formas:
- Anotações de funções. ( PEP 3107 )
- Arquivos stub para módulos internos / de usuário.
# type: type
Comentários especiais que complementam os dois primeiros formulários. (Consulte: O que são anotações variáveis no Python 3.6? Para obter uma atualização do Python 3.6 para # type: type
comentários)
Além disso, convém usar dicas de tipo em conjunto com o novo typing
módulo introduzido no Py3.5
. Nele, muitos (adicionais) ABCs (Abstract Base Classes) são definidos junto com funções auxiliares e decoradores para uso na verificação estática. Mais ABCs
em collections.abc
estão incluídas, mas em uma Generic
forma de modo a permitir subscrição (por definição um __getitem__()
método).
Para qualquer pessoa interessada em uma explicação mais aprofundada sobre isso, ela mypy documentation
foi escrita muito bem e possui muitos exemplos de código demonstrando / descrevendo a funcionalidade do verificador; definitivamente vale a pena ler.
Anotações de funções e comentários especiais:
Primeiro, é interessante observar alguns dos comportamentos que podemos ter ao usar comentários especiais. Especial# type: type
Comentários podem ser adicionados durante as atribuições de variáveis para indicar o tipo de um objeto, se não for possível inferir diretamente. Tarefas simples geralmente são facilmente deduzidas, mas outras, como listas (com relação ao seu conteúdo), não podem.
Nota: Se quisermos usar qualquer derivada Containers
e precisar especificar o conteúdo desse contêiner, devemos usar os tipos genéricos do typing
módulo. Estes suportam a indexação.
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
Se adicionarmos esses comandos a um arquivo e executá-los com nosso intérprete, tudo funcionará bem e print(a)
imprimirá o conteúdo da lista a
. Os # type
comentários foram descartados, tratados como comentários simples que não têm significado semântico adicional .
Ao executar isso com mypy
, por outro lado, obtemos a seguinte resposta:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Indicando que uma lista de str
objetos não pode conter um int
que, estaticamente falando, seja válido. Isso pode ser corrigido respeitando o tipo de objetos a
e apenas acrescentando-os str
ou alterando o tipo de conteúdo de a
para indicar que qualquer valor é aceitável (Executado intuitivamente com List[Any]
depois de Any
ter sido importado detyping
).
As anotações de função são adicionadas no formulário param_name : type
após cada parâmetro em sua assinatura de função e um tipo de retorno é especificado usando a -> type
notação antes dos dois-pontos finais da função; todas as anotações são armazenadas no __annotations__
atributo para essa função em um prático formulário de dicionário. Usando um exemplo trivial (que não requer tipos extras do typing
módulo):
def annotated(x: int, y: str) -> bool:
return x < y
O annotated.__annotations__
atributo agora tem os seguintes valores:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Se somos um noobie completo, ou estamos familiarizados com os Py2.7
conceitos e, consequentemente, desconhecemos o que está TypeError
escondido na comparação de annotated
, podemos executar outra verificação estática, detectar o erro e nos poupar alguns problemas:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Entre outras coisas, chamar a função com argumentos inválidos também será capturado:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Eles podem ser estendidos para basicamente qualquer caso de uso e os erros detectados vão além das chamadas e operações básicas. Os tipos que você pode verificar são realmente flexíveis e eu apenas dei um pequeno pico de seu potencial. Um olhar notyping
módulo, nos PEPs ou nos mypy
documentos fornecerá uma idéia mais abrangente dos recursos oferecidos.
Arquivos Stub:
Os arquivos stub podem ser usados em dois casos diferentes, não mutuamente exclusivos:
- Você precisa digitar check um módulo para o qual não deseja alterar diretamente as assinaturas da função
- Você deseja escrever módulos e ter verificação de tipo, mas adicionalmente deseja separar anotações do conteúdo.
O que os arquivos stub (com uma extensão de .pyi
) são uma interface anotada do módulo que você está criando / deseja usar. Eles contêm as assinaturas das funções que você deseja verificar com o corpo das funções descartadas. Para entender isso, é fornecido um conjunto de três funções aleatórias em um módulo chamado randfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
Podemos criar um arquivo stub randfunc.pyi
, no qual podemos colocar algumas restrições, se desejarmos fazê-lo. A desvantagem é que alguém que visualiza a fonte sem o stub não recebe realmente essa assistência de anotação ao tentar entender o que deve ser passado para onde.
De qualquer forma, a estrutura de um arquivo stub é bastante simplista: adicione todas as definições de função com corpos vazios ( pass
preenchidos) e forneça as anotações com base em seus requisitos. Aqui, vamos supor que queremos apenas trabalhar com int
tipos para nossos contêineres.
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
A combine
função fornece uma indicação do motivo pelo qual você pode querer usar anotações em um arquivo diferente, algumas vezes confundem o código e reduzem a legibilidade (grande não-não para Python). Obviamente, você pode usar aliases de tipo, mas isso às vezes confunde mais do que ajuda (use-os com sabedoria).
Isso deve familiarizá-lo com os conceitos básicos das Dicas de tipo no Python. Embora o verificador de tipos usado tenha sido,
mypy
você deve gradualmente começar a ver mais pop-ups, alguns internamente em IDEs ( PyCharm ,) e outros como módulos python padrão. Vou tentar adicionar verificadores / pacotes relacionados adicionais na lista a seguir, quando e se os encontrar (ou se sugerido).
Damas que eu conheço :
- Mypy : como descrito aqui.
- PyType : pelo Google, usa uma notação diferente da que eu recolho, provavelmente vale uma olhada.
Pacotes / Projetos relacionados :
Na typeshed
verdade, o projeto é um dos melhores lugares para ver como dicas de tipo podem ser usadas em um projeto próprio. Vamos tomar como exemplo os __init__
dunders da Counter
classe no .pyi
arquivo correspondente :
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
Onde _T = TypeVar('_T')
é usado para definir classes genéricas . Para a Counter
classe, podemos ver que ela não pode aceitar argumentos em seu inicializador, obter um único Mapping
de qualquer tipo para um int
ou aceitar um Iterable
de qualquer tipo.
Aviso : Uma coisa que esqueci de mencionar foi que o typing
módulo foi introduzido provisoriamente . Do PEP 411 :
Um pacote provisório pode ter sua API modificada antes de "graduar" para um estado "estável". Por um lado, esse estado fornece ao pacote os benefícios de fazer parte formal da distribuição do Python. Por outro lado, a equipe principal de desenvolvimento afirma explicitamente que não há promessas com relação à estabilidade da API do pacote, que pode mudar para o próximo lançamento. Embora seja considerado um resultado improvável, esses pacotes podem até ser removidos da biblioteca padrão sem um período de descontinuação, se as preocupações com sua API ou manutenção forem bem fundamentadas.
Então leve as coisas aqui com uma pitada de sal; Duvido que seja removido ou alterado de maneiras significativas, mas nunca se pode saber.
** Outro tópico completo, mas válido no escopo das dicas de tipo PEP 526
:: Sintaxe para anotações de variáveis é um esforço para substituir # type
comentários, introduzindo uma nova sintaxe que permite que os usuários anotem o tipo de variáveis de maneira simples.varname: type
instruções .
Consulte O que são anotações de variáveis no Python 3.6? , como mencionado anteriormente, para uma pequena introdução sobre eles.