O que são dicas de tipo no Python 3.5?


250

Um dos recursos mais comentados no Python 3.5 são as dicas de tipo .

Um exemplo de pistas tipo é mencionado no presente artigo e uma esta enquanto também mencionar a usar dicas tipo responsável. Alguém pode explicar mais sobre eles e quando devem ser usados ​​e quando não?


4
Você deve dar uma olhada no PEP 484, que está vinculado a partir do changelog oficial .
Stefan

1
@AvinashRaj: Uma boa discussão sobre sobre os lançamentos está acontecendo aqui
Vaulstein

1
É uma pena que o caso de uso da C-API seja completamente ignorado por este PEP 484, particularmente dicas de tipo para Cython e Numba.
Denfromufa

Respostas:


343

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?

  1. 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.
  2. 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.
  3. 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 mypya 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:

Além disso, convém usar dicas de tipo em conjunto com o novo typingmó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 ABCsem collections.abcestão incluídas, mas em uma Genericforma 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 documentationfoi 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: typeComentá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 Containerse precisar especificar o conteúdo desse contêiner, devemos usar os tipos genéricos do typingmó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 # typecomentá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 strobjetos não pode conter um intque, estaticamente falando, seja válido. Isso pode ser corrigido respeitando o tipo de objetos ae apenas acrescentando-os strou alterando o tipo de conteúdo de apara indicar que qualquer valor é aceitável (Executado intuitivamente com List[Any]depois de Anyter sido importado detyping ).

As anotações de função são adicionadas no formulário param_name : typeapós cada parâmetro em sua assinatura de função e um tipo de retorno é especificado usando a -> typenotaçã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 typingmó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.7conceitos e, consequentemente, desconhecemos o que está TypeErrorescondido 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 mypydocumentos 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 ( passpreenchidos) e forneça as anotações com base em seus requisitos. Aqui, vamos supor que queremos apenas trabalhar com inttipos 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 combinefunçã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, mypyvocê 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 typeshedverdade, 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 Counterclasse no .pyiarquivo 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 Counterclasse, podemos ver que ela não pode aceitar argumentos em seu inicializador, obter um único Mappingde qualquer tipo para um int ou aceitar um Iterablede qualquer tipo.


Aviso : Uma coisa que esqueci de mencionar foi que o typingmó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 # typecomentá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.


3
"Devido à natureza altamente dinâmica do Python, inferir ou verificar o tipo de um objeto que está sendo usado é especialmente difícil". Você está se referindo à verificação estática, certo?
bsam

53

Adicionando à resposta elaborada de Jim:

Verifique o typingmódulo - este módulo suporta dicas de tipo, conforme especificado pelo PEP 484 .

Por exemplo, a função abaixo pega e retorna valores do tipo stre é anotada da seguinte maneira:

def greeting(name: str) -> str:
    return 'Hello ' + name

O typingmódulo também suporta:

  1. Digite alias .
  2. Digite dica para funções de retorno de chamada .
  3. Genéricos - as classes base abstratas foram estendidas para oferecer suporte à assinatura e indicar os tipos esperados para elementos de contêiner.
  4. Tipos genéricos definidos pelo usuário - Uma classe definida pelo usuário pode ser definida como uma classe genérica.
  5. Qualquer tipo - Todo tipo é um subtipo de Qualquer.

26

O recém-lançado PyCharm 5 suporta dicas de tipo. Em sua postagem no blog (consulte a dica do tipo Python 3.5 no PyCharm 5 ), eles oferecem uma ótima explicação sobre o que são dicas de tipo e não são, além de vários exemplos e ilustrações de como usá-las em seu código.

Além disso, ele é suportado no Python 2.7, conforme explicado neste comentário :

O PyCharm suporta o módulo de digitação do PyPI para Python 2.7, Python 3.2-3.4. Para o 2.7, você precisa colocar dicas de tipo nos arquivos stub * .pyi, pois as anotações de funções foram adicionadas no Python 3.0 .


0

Dica de tipo é uma adição recente a um idioma dinâmico em que há décadas as pessoas juram convenções de nomenclatura tão simples quanto húngaro (rótulo do objeto com a primeira letra b = booliano, c = caractere, d = dicionário, i = número inteiro, l = lista, n = numérico , s = string, t = tupla) não eram necessários, muito pesados, mas agora decidimos que, oh espere ... é muito complicado usar a linguagem (type ()) para reconhecer objetos e nossos sofisticados IDEs precisa de ajuda para fazer qualquer coisa tão complicada, e que os valores dos objetos atribuídos dinamicamente os tornem completamente inúteis, enquanto uma simples convenção de nomenclatura poderia ter resolvido tudo isso, para qualquer desenvolvedor, com um simples olhar.


Para ser franco, isso parece mais um discurso retórico do que uma resposta.
Dimitris Fasarakis Hilliard

-1

As dicas de tipo são para manutenção e não são interpretadas pelo Python. No código abaixo, a linha def add(self, ic:int)não resulta em erro até a próxima return...linha:

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic
    
c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.