Python 3.5'te tip ipuçları nelerdir?


250

Python 3.5'te en çok konuşulan özelliklerden biri tip ipuçlarıdır .

Bir örnek tipi tavsiyeleri açıklanan bu makalede ve bu bir de sorumlu tip ipuçlarını kullanımı söz ederken. Birisi onlar hakkında ne zaman ve ne zaman kullanılmaları gerektiğini açıklayabilir mi?


4
Resmi değişiklik günlüğüne bağlı PEP 484'e bir göz atmalısınız .
Stefan

1
@AvinashRaj: Bültenler hakkında güzel bir tartışma burada
Vaulstein

1
C-API kullanım durumunun bu PEP 484, özellikle Cython ve Numba için tip ipuçları tarafından tamamen göz ardı edilmesi üzücüdür.
denfromufa

Yanıtlar:


344

Okumayı öneririm PEP 483 ve PEP 484 ve izlerken bu Tipi ima üzerinde Guido tarafından sunum.

Özetle : Yazım ipucu kelimelerin anlamıyla, kullandığınız nesnenin / nesnelerin türünü ima edersiniz .

Python'un dinamik doğası nedeniyle , kullanılan bir nesnenin türünü çıkarmak veya kontrol etmek özellikle zordur. Bu gerçek, geliştiricilerin yazmadıkları kodda tam olarak neler olup bittiğini anlamalarını ve en önemlisi, birçok IDE'de bulunan [PyCharm, PyDev akla gelen] tip kontrol araçları için nesnelerin ne tür olduğuna dair herhangi bir gösterge yoktur. Sonuç olarak, (sunumda belirtildiği gibi) yaklaşık% 50 başarı oranı ile türü çıkarmaya çalışmaktadırlar.


Yazım İpuçları sunumundan iki önemli slayt almak için:

Neden Tip İpuçları?

  1. Tür Denetleyicilerine Yardımcı Olur: Nesnenin tür denetleyicisi olmasını istediğiniz türü belirleyerek, örneğin beklenmeyen türde bir nesneyi iletip geçirmediğinizi kolayca algılayabilir.
  2. Belgelere yardımcı olur: Kodunuzu görüntüleyen üçüncü bir kişi, nereden alınacağını, ergo'nun, bunları almadan kullanmanın nasıl olacağını bilecektir TypeErrors.
  3. IDE'lerin daha doğru ve sağlam araçlar geliştirmesine yardımcı olur: Geliştirme Ortamları, nesnenizin ne tür olduğunu bildiğinde uygun yöntemler önermek için daha uygun olacaktır. Bunu bir noktada IDE ile deneyimlemişsinizdir .ve bir nesne için tanımlanmamış yöntemler / nitelikler açılır.

Statik Tip Denetleyicileri neden kullanılır?

  • Daha erken hataları bul : Bu açık bir şekilde, sanırım.
  • Projeniz ne kadar büyük olursa o kadar çok ihtiyacınız olur : Yine mantıklı. Statik diller, dinamik dillerin eksik olduğu bir sağlamlık ve kontrol sunar. Uygulamanız ne kadar büyük ve karmaşık olursa, ihtiyacınız olan (davranışsal açıdan) daha fazla kontrol ve öngörülebilirlik haline gelir.
  • Büyük takımlar zaten statik analiz yapıyor : Sanırım bu ilk iki noktayı doğrulıyor.

Bu küçük giriş için bir kapanış notu olarak : Bu isteğe bağlı bir özelliktir ve anladığım kadarıyla, statik yazmanın bazı avantajlarından yararlanmak için tanıtılmıştır.

Genellikle yok bu konuda endişe gerekir ve kesinlikle (özellikle yardımcı bir betik dili olarak Python kullanmak durumlarda) kullanmak gerekmez. Çok ihtiyaç duyulan sağlamlık, kontrol ve ek hata ayıklama yetenekleri sunduğu için büyük projeler geliştirirken yardımcı olmalıdır .


Mypy ile İpucu Yazma :

Bu cevabı daha eksiksiz hale getirmek için, küçük bir tanıtımın uygun olacağını düşünüyorum. Ben kullanarak olacak mypy, bunlar PEP sunulmuştur olarak Tip İpuçları ilham kütüphanesi. Bu esas olarak bu soruya çarpıp nereden başlayacağını merak eden herkes için yazılmıştır.

Bunu yapmadan önce aşağıdakileri tekrarlamama izin verin: PEP 484 hiçbir şeyi zorlamıyor; sadece fonksiyon açıklamaları için bir yön belirlemek ve tip kontrolünün nasıl yapılacağı / yapılması gerektiğine dair kılavuzlar önermektedir . İşlevlerinize açıklama ekleyebilir ve istediğiniz kadar ipucu verebilirsiniz; komut dosyalarınız ek açıklamaların varlığından bağımsız olarak yine de çalışır çünkü Python'un kendisi bunları kullanmaz.

Her neyse, KEP'de belirtildiği gibi, ipucu türleri genellikle üç şekilde olmalıdır:

  • İşlev açıklamaları. ( PEP 3107 )
  • Yerleşik / kullanıcı modülleri için saplama dosyaları.
  • # type: typeİlk iki formu tamamlayan özel yorumlar. (Yorumlar: Python 3.6 güncellemesi için Python 3.6'daki değişken ek açıklamalar nelerdir?# type: type )

Ayrıca, typingtanıtılan yeni modülle birlikte tür ipuçlarını da kullanmak isteyeceksiniz Py3.5. İçinde, statik kontrolde kullanılmak üzere yardımcı fonksiyonlar ve dekoratörler ile birlikte birçok (ek) ABC (Soyut Temel Sınıflar) tanımlanır. En ABCsde collections.abcdahildir ancak içinde Generic, formu, (a tanımlayarak abonelik izin vermek için __getitem__()bir yöntem).

Bunların daha ayrıntılı bir açıklamasıyla ilgilenen herkes için mypy documentation, çok güzel yazılmıştır ve denetleyicilerinin işlevselliğini gösteren / açıklayan birçok kod örneğine sahiptir; kesinlikle bir okumaya değer.

İşlev açıklamaları ve özel yorumlar:

İlk olarak, özel yorumlar kullanırken elde edebileceğimiz bazı davranışları gözlemlemek ilginçtir. # type: typeDeğişken atamaları sırasında, doğrudan çıkartılamazsa bir nesnenin türünü belirtmek için özel yorumlar eklenebilir. Basit ödevler genellikle kolayca çıkarılabilir, ancak listeler gibi diğer içerikler (içerikleriyle ilgili olarak) yapamaz.

Not: Herhangi bir türevini kullanmak istiyorsak Containersve o kap için içeriği belirtmemiz gerekiyorsa , modülden genel türleri kullanmalıyız typing. Bunlar endekslemeyi destekler.

# 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']

Bu komutları bir dosyaya ekler ve yorumcumuzla yürütürsek, her şey yolunda gider ve print(a)sadece listenin içeriğini yazdırır a. # typeYorum, atılmış ek semantik anlama sahiptir düz yorum olarak işlenirler .

Bununla birlikte mypy, bunu yaparak aşağıdaki yanıtı alırız:

(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"

strNesne listesinin int, statik olarak söylemek gerekirse, ses içermediğini belirtir. Bu, herhangi bir değerin kabul edilebilir olduğunu belirtmek için nesnelerin türüne ave yalnızca eklenen strnesnelere bağlı kalarak veya içeriğinin türünü değiştirerek düzeltilebilir a(Sezgisel olarak ile içe aktarıldıktan List[Any]sonra gerçekleştirilebilir ).Anytyping

İşlev ek açıklamaları, işlev imzanızdaki param_name : typeher parametreden sonra forma eklenir -> typeve bitiş işlevi iki nokta üst üste işaretinden önceki notasyon kullanılarak bir dönüş türü belirtilir ; tüm ek açıklamalar __annotations__bu işlevin özniteliğinde kullanışlı bir sözlük biçiminde saklanır . Önemsiz bir örnek kullanarak ( typingmodülden fazladan tip gerektirmeyen ):

def annotated(x: int, y: str) -> bool:
    return x < y

annotated.__annotations__Nitelik şimdi aşağıdaki değerlere sahiptir:

{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}

Tam bir noobie'yiz veya Py2.7kavramlara aşinaysak ve sonuç TypeErrorolarak karşılaştırmada gizlenenlerden habersizsek, annotatedbaşka bir statik kontrol gerçekleştirebilir, hatayı yakalayabilir ve bize bazı sorunlar kaydedebiliriz:

(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")

Diğer şeylerin yanı sıra, işlevi geçersiz argümanlarla çağırmak da yakalanır:

annotated(20, 20)

# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"

Bunlar temelde herhangi bir kullanım durumuna genişletilebilir ve yakalanan hatalar temel çağrı ve işlemlerden daha fazla uzanır. Kontrol edebileceğiniz tipler gerçekten esnektir ve sadece potansiyelinin küçük bir zirvesini verdim. typingModüle, PEP'lere veya mypybelgelere bir bakış size sunulan yetenekler hakkında daha kapsamlı bir fikir verecektir.

Saplama Dosyaları:

Saplama dosyaları, birbirini dışlayan iki farklı durumda kullanılabilir:

  • İşlev imzalarını doğrudan değiştirmek istemediğiniz bir modülü kontrol etmelisiniz.
  • Modüller yazmak istiyorsunuz ve yazım denetimi yapmak istiyorsunuz ancak ek açıklamaları içerikten ayırmak istiyorsunuz.

Saplama dosyalarının (uzantısıyla birlikte .pyi) ne yaptığınız / kullanmak istediğiniz modülün açıklamalı arayüzüdür. Atılan işlevlerin gövdesi ile yazmak istediğiniz işlevlerin imzalarını içerir. Bunu hissetmek için, adlandırılmış bir modülde üç rastgele işlev kümesi verildi 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)

İstersek randfunc.pyibazı kısıtlamalar koyabileceğimiz bir saplama dosyası oluşturabiliriz . Dezavantajı, saplama olmadan kaynağı görüntüleyen birinin, neyin nerede geçmesi gerektiğini anlamaya çalışırken gerçekten bu ek yardım almamasıdır.

Her neyse, bir saplama dosyasının yapısı oldukça basittir: Tüm işlev tanımlarını boş gövdelerle ( passdolu) ekleyin ve ek açıklamaları gereksinimlerinize göre sağlayın. Burada, yalnızca intKonteynerlerimiz için türlerle çalışmak istediğimizi varsayalım .

# 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

combineFonksiyonu farklı bir dosyada ek açıklamaları kullanmak isteyebilirsiniz neden bir gösterge verir, bazı zamanlar kodunu kadar karmakarışık ve (Python için hayır-hayır büyük) okunabilirliği azaltır. Tabii ki tür takma adları kullanabilirsiniz, ancak bu bazen yardımcı olduğundan daha fazla karıştırır (bu yüzden bunları akıllıca kullanın).


Bu sizi Python'daki Tip İpuçları temel kavramlarına alıştırmalıdır. Kullanılan tür denetleyicisi olmasına rağmen, mypyyavaş yavaş daha fazla pop-up görmeye başlamalısınız, bazıları dahili olarak IDE'lerde ( PyCharm ,) ve diğerleri standart python modülleri olarak. Bunları ne zaman ve bulursam (veya önerilirse) aşağıdaki listeye ek dama / ilgili paketler ekleyeceğim.

Bildiğim dama :

  • Mypy : burada açıklandığı gibi.
  • PyType : Google tarafından, topladığımdan farklı bir gösterim kullanır, muhtemelen bir göz atmaya değer.

İlgili Paketler / Projeler :

  • typeshed: Standart kütüphane için çeşitli saplama dosyaları barındıran resmi Python repo.

typeshedProje aslında tip ipucu kendinize ait bir projede nasıl kullanılabileceğine görmek için bakabilirsiniz iyi yerlerden biridir. Örnek olarak edelim almak bir Dunders sınıfında karşılık gelen dosyanın:__init__Counter.pyi

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: ...

Nerede _T = TypeVar('_T')genel sınıfları tanımlamak için kullanılır . CounterSınıf için, başlatıcısında hiçbir argüman alamayacağını Mapping, herhangi bir türden bir tanesini elde edemeyeceğini int veyaIterable herhangi bir türden birini alabileceğini görebiliriz .


Uyarı : Bahsetmeyi unuttuğum bir şey, typingmodülün geçici olarak tanıtıldığıydı . Gönderen PEP 411 :

Geçici bir paket, "kararlı" duruma geçmeden önce API'sını değiştirebilir. Bir yandan, bu durum pakete Python dağıtımının resmi bir parçası olmanın faydalarını sağlar. Öte yandan, çekirdek geliştirme ekibi, paketin API'sinin kararlılığıyla ilgili olarak bir sonraki sürümde değişebilecek vaatlerde bulunmadığını açıkça belirtmektedir. Beklenmedik bir sonuç olarak görülse de, API veya bakımlarıyla ilgili endişelerin iyi kurulmuş olması durumunda, bu paketler kullanımdan kaldırılma süresi olmadan standart kütüphaneden çıkarılabilir.

Burada bir tutam tuzla bir şeyler alın; Önemli yollarla kaldırılacağından veya değiştirileceğinden şüpheliyim ama kimse asla bilemez.


** Tip ipuçları kapsamında tamamen ama geçerli olan başka bir konu PEP 526:: Değişken Ek Açıklamalar için Sözdizimi# type , kullanıcıların basit varname: typeifadelerde değişkenlerin türüne açıklama eklemesine izin veren yeni sözdizimi sunarak yorumları değiştirme çabasıdır .

Bkz . Python 3.6'da değişken ek açıklamalar nelerdir? , daha önce de belirtildiği gibi, bunlarla ilgili küçük bir giriş için.


3
"Python'un son derece dinamik doğası nedeniyle, kullanılan bir nesnenin türünü çıkarmak veya kontrol etmek özellikle zordur." Statik kontrole atıfta bulunuyorsunuz, değil mi?
bsam

53

Jim'in ayrıntılı cevabına ek olarak:

typingModülü kontrol edin - bu modül PEP 484 tarafından belirtilen tip ipuçlarını destekler .

Örneğin, aşağıdaki işlev tür değerlerini alır ve döndürür strve aşağıdaki gibi açıklanır:

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

typingModül ayrıca destekler:

  1. Aliasing yazın .
  2. Geri arama işlevleri için ipucu yazın .
  3. Jenerikler - Soyut temel sınıflar, kap öğeleri için beklenen türleri belirtmek üzere aboneliği desteklemek üzere genişletilmiştir.
  4. Kullanıcı tanımlı genel türler - Kullanıcı tanımlı bir sınıf genel bir sınıf olarak tanımlanabilir.
  5. Herhangi bir tür - Her tür, Herhangi birinin alt türüdür.

26

Yeni piyasaya sürülen PyCharm 5 tip ipucunu destekler. Bu konudaki blog yazılarında (PyCharm 5'teki Python 3.5 tip ipucuna bakın ), hangi tip ipuçlarının ne olduğuna dair harika bir açıklama sunarlar ve kodunuzda nasıl kullanılacağına dair birkaç örnek ve çizim ile birlikte değildirler .

Ayrıca, Python 2.7 desteklenir olarak açıklandığı Bu yoruma :

PyCharm, Python 2.7, Python 3.2-3.4 için PyPI'den yazma modülünü destekler. Python 3.0'da işlev ek açıklamaları eklendiğinden, 2.7 için * .pyi saplama dosyalarına tür ipuçları koymak zorundasınız .


0

Tür ipucu, onlarca yıldır milletlerin isimlendirme kurallarını Macarca kadar basit bir şekilde yemin ettiği dinamik bir dile eklenmiştir (ilk harfi b = boolian, c = karakter, d = sözlük, i = tamsayı, l = liste, n = sayısal , s = string, t = tuple), çok hantal değildi, ama şimdi karar verdik, oh bekleyin ... nesneleri (süslü IDE'ler) tanımak için dili (type ()) kullanmak çok fazla sorun karmaşık bir şey yapmak için yardıma ihtiyaç duyar ve dinamik olarak atanan nesne değerleri onları her nasılsa tamamen işe yaramaz hale getirirken, basit bir adlandırma kuralı, herhangi bir geliştirici için hepsini bir bakışta çözebilirdi.


Açıkçası, bu bir cevaptan çok bir rant gibi geliyor.
Dimitris Fasarakis Hilliard

-1

Tip ipuçları sürdürülebilirlik içindir ve Python tarafından yorumlanmaz. Aşağıdaki kodda, satır def add(self, ic:int)bir sonraki return...satıra kadar hataya neden olmaz :

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'
 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.