TL; DR : se você estiver usando o Python 4.0, ele simplesmente funciona. A partir de hoje (2019) em 3.7+, você deve ativar esse recurso usando uma instrução futura ( from __future__ import annotations
) - para Python 3.6 ou abaixo, use uma string.
Eu acho que você recebeu essa exceção:
NameError: name 'Position' is not defined
Isso ocorre porque Position
deve ser definido antes que você possa usá-lo em uma anotação, a menos que esteja usando o Python 4.
Python 3.7+: from __future__ import annotations
O Python 3.7 introduz o PEP 563: avaliação adiada de anotações . Um módulo que usa a instrução futura from __future__ import annotations
armazenará anotações como seqüências de caracteres automaticamente:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Isso está programado para se tornar o padrão no Python 4.0. Como o Python ainda é uma linguagem de tipo dinâmico, portanto, nenhuma verificação de tipo é feita em tempo de execução, as anotações de digitação não devem ter impacto no desempenho, certo? Errado! Antes de python 3.7 do módulo de digitação costumava ser um dos mais lentos módulos python no núcleo assim se você import typing
você vai ver até 7 vezes aumento no desempenho quando você atualizar para 3.7.
Python <3.7: use uma string
De acordo com o PEP 484 , você deve usar uma string em vez da própria classe:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Se você usa a estrutura do Django, isso pode ser familiar, pois os modelos do Django também usam strings para referências futuras (definições de chave estrangeira em que o modelo estrangeiro é self
ou ainda não foi declarado). Isso deve funcionar com Pycharm e outras ferramentas.
Fontes
As partes relevantes do PEP 484 e PEP 563 , para poupar a viagem:
Encaminhar referências
Quando uma dica de tipo contém nomes que ainda não foram definidos, essa definição pode ser expressa como uma string literal, a ser resolvida posteriormente.
Uma situação em que isso ocorre geralmente é a definição de uma classe de contêiner, em que a classe que está sendo definida ocorre na assinatura de alguns dos métodos. Por exemplo, o código a seguir (o início de uma implementação simples de árvore binária) não funciona:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Para resolver isso, escrevemos:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
O literal da string deve conter uma expressão Python válida (ou seja, compile (lit, '', 'eval') deve ser um objeto de código válido) e deve ser avaliada sem erros quando o módulo estiver totalmente carregado. O espaço para nome local e global em que é avaliado deve ser o mesmo espaço para nome no qual os argumentos padrão para a mesma função seriam avaliados.
e PEP 563:
No Python 4.0, as anotações de função e variável não serão mais avaliadas no momento da definição. Em vez disso, um formulário de string será preservado no respectivo __annotations__
dicionário. Os verificadores de tipo estático não verão diferença no comportamento, enquanto as ferramentas que usam anotações em tempo de execução terão que realizar uma avaliação adiada.
...
A funcionalidade descrita acima pode ser ativada a partir do Python 3.7 usando a seguinte importação especial:
from __future__ import annotations
Coisas que você pode se sentir tentado a fazer
A. Definir um manequim Position
Antes da definição da classe, coloque uma definição fictícia:
class Position(object):
pass
class Position(object):
...
Isso vai se livrar do NameError
e pode até parecer OK:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Mas é isso?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Patch de macaco para adicionar as anotações:
Você pode tentar um pouco da mágica de programação de Python e escrever um decorador para corrigir a definição de classe com o objetivo de adicionar anotações:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
O decorador deve ser responsável pelo equivalente a isso:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Pelo menos parece certo:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Provavelmente demais.
Conclusão
Se você estiver usando o 3.6 ou abaixo, use uma string literal contendo o nome da classe, no 3.7 use from __future__ import annotations
e ele simplesmente funcionará.