TL; DR : jika Anda menggunakan Python 4.0 itu hanya berfungsi. Mulai hari ini (2019) dalam 3,7+ Anda harus mengaktifkan fitur ini menggunakan pernyataan masa depan (from __future__ import annotations
) - untuk Python 3.6 atau di bawahnya menggunakan string.
Saya kira Anda mendapatkan pengecualian ini:
NameError: name 'Position' is not defined
Hal ini karena Position
harus ditentukan sebelum Anda dapat menggunakannya dalam anotasi kecuali Anda menggunakan Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 memperkenalkan PEP 563: evaluasi anotasi yang ditunda . Modul yang menggunakan pernyataan di masa mendatang from __future__ import annotations
akan menyimpan anotasi sebagai string secara otomatis:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Ini dijadwalkan menjadi default di Python 4.0. Karena Python masih merupakan bahasa yang diketik secara dinamis sehingga tidak ada pemeriksaan tipe yang dilakukan pada saat runtime, mengetik anotasi seharusnya tidak memiliki dampak kinerja, bukan? Salah! Sebelum python 3.7, modul pengetikan digunakan untuk menjadi salah satu modul python paling lambat di inti jadi jika import typing
Anda akan melihat hingga 7 kali peningkatan kinerja ketika Anda meningkatkan ke 3,7.
Python <3.7: gunakan string
Menurut PEP 484 , Anda harus menggunakan string daripada kelas itu sendiri:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Jika Anda menggunakan kerangka Django ini mungkin akrab karena model Django juga menggunakan string untuk referensi ke depan (definisi kunci asing di mana model asing adalah self
atau belum dideklarasikan). Ini harus bekerja dengan Pycharm dan alat-alat lainnya.
Sumber
Bagian-bagian PEP 484 dan PEP 563 yang relevan , untuk membantu Anda melakukan perjalanan:
Referensi penerusan
Ketika petunjuk jenis berisi nama-nama yang belum didefinisikan, definisi tersebut dapat dinyatakan sebagai string literal, untuk diselesaikan nanti.
Situasi di mana hal ini terjadi umumnya adalah definisi kelas kontainer, di mana kelas yang didefinisikan terjadi dalam tanda tangan beberapa metode. Sebagai contoh, kode berikut (awal implementasi pohon biner sederhana) tidak berfungsi:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Untuk mengatasi ini, kami menulis:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Literal string harus berisi ekspresi Python yang valid (yaitu, kompilasi (lit, '', 'eval') harus menjadi objek kode yang valid) dan harus mengevaluasi tanpa kesalahan setelah modul telah dimuat penuh. Namespace lokal dan global yang dievaluasi harus ruang nama yang sama di mana argumen default untuk fungsi yang sama akan dievaluasi.
dan PEP 563:
Dalam Python 4.0, anotasi fungsi dan variabel tidak akan lagi dievaluasi pada waktu definisi. Sebagai gantinya, bentuk string akan disimpan dalam __annotations__
kamus masing-masing . Pemeriksa tipe statis tidak akan melihat perbedaan dalam perilaku, sedangkan alat yang menggunakan anotasi saat runtime harus melakukan evaluasi yang ditunda.
...
Fungsi yang dijelaskan di atas dapat diaktifkan mulai dari Python 3.7 menggunakan impor khusus berikut:
from __future__ import annotations
Hal-hal yang mungkin tergoda untuk Anda lakukan
A. Definisikan boneka Position
Sebelum definisi kelas, tempatkan definisi dummy:
class Position(object):
pass
class Position(object):
...
Ini akan menghilangkan NameError
dan bahkan mungkin terlihat OK:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Tetapi apakah itu?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Kera-tambalan untuk menambahkan anotasi:
Anda mungkin ingin mencoba beberapa sihir pemrograman meta Python dan menulis dekorator ke monyet-patch definisi kelas untuk menambahkan anotasi:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Dekorator harus bertanggung jawab atas hal yang setara ini:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Setidaknya sepertinya benar:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Mungkin terlalu banyak kesulitan.
Kesimpulan
Jika Anda menggunakan 3,6 atau di bawah menggunakan string literal yang berisi nama kelas, gunakan 3,7 from __future__ import annotations
dan itu hanya akan berfungsi.
typing
karena semua jenis yang Anda gunakan harus dalam lingkup ketika string dievaluasi.''
sekeliling kelas, bukan parameter tipefrom __future__ import annotations
- ini harus diimpor sebelum semua impor lainnya.Menentukan jenis sebagai string baik-baik saja, tetapi selalu memberi saya sedikit bahwa kita pada dasarnya menghindari parser. Jadi sebaiknya Anda tidak salah mengeja salah satu dari string literal ini:
Sedikit variasi adalah dengan menggunakan typevar terikat, setidaknya Anda harus menulis string hanya sekali ketika mendeklarasikan typevar:
sumber
typing.Self
menentukan ini secara eksplisit.typing.Self
ada. Mengembalikan string kode keras gagal mengembalikan jenis yang benar ketika memanfaatkan polimorfisme. Dalam kasus saya, saya ingin menerapkan metode deserialize class. Saya memutuskan untuk mengembalikan dikt (kwargs) dan meneleponsome_class(**some_class.deserialize(raw_data))
.Position
, dan bukan kelasnya, jadi contoh di atas secara teknis salah. Implementasinya harus digantiPosition(
dengan sesuatu sepertiself.__class__(
.other
, tetapi kemungkinan besar sebenarnya tergantungself
. Jadi, Anda harus meletakkan anotasiself
untuk menggambarkan perilaku yang benar (dan mungkinother
seharusnya hanyaPosition
untuk menunjukkan bahwa itu tidak terkait dengan tipe pengembalian). Ini juga dapat digunakan untuk kasus-kasus ketika Anda hanya bekerja denganself
. misalnyadef __aenter__(self: T) -> T:
Nama 'Posisi' tidak tersedia pada saat tubuh kelas itu sendiri diuraikan. Saya tidak tahu bagaimana Anda menggunakan deklarasi tipe, tetapi Python PEP 484 - yang merupakan mode yang paling banyak digunakan jika menggunakan petunjuk pengetikan ini mengatakan bahwa Anda dapat dengan mudah meletakkan nama sebagai string pada saat ini:
Periksa https://www.python.org/dev/peps/pep-0484/#forward-references - alat yang sesuai dengan itu akan tahu untuk membuka bungkusan nama kelas dari sana dan memanfaatkannya. (Selalu penting untuk memiliki perlu diingat bahwa bahasa Python sendiri tidak melakukan anotasi ini - mereka biasanya dimaksudkan untuk analisis kode-statis, atau orang dapat memiliki pustaka / kerangka kerja untuk memeriksa jenis dalam run-time - tetapi Anda harus secara eksplisit mengaturnya).
perbarui Juga, pada Python 3.8, periksa pep-563 - pada Python 3.8 dimungkinkan untuk menulis
from __future__ import annotations
untuk menunda evaluasi anotasi - kelas referensi maju harus bekerja secara langsung.sumber
Ketika petunjuk tipe berbasis string dapat diterima,
__qualname__
item tersebut juga dapat digunakan. Itu memegang nama kelas, dan tersedia di tubuh definisi kelas.Dengan melakukan ini, mengubah nama kelas tidak berarti memodifikasi petunjuk tipe. Tapi saya pribadi tidak akan mengharapkan editor kode pintar untuk menangani formulir ini dengan baik.
sumber