Python3'ün “İşlev Ek Açıklamaları” nın iyi kullanımları nelerdir?


159

İşlev Ek Açıklamaları: PEP-3107

Python3'ün işlev ek açıklamalarını gösteren bir kod pasajı ile karşılaştım. Kavram basit ama bunların neden Python3'te uygulandığını veya onlar için iyi kullanımlarını düşünemiyorum. Belki SO beni aydınlatabilir mi?

Nasıl çalışır:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

Bağımsız değişkenten sonra iki nokta üst üste işaretini izleyen her şey bir 'ek açıklamadır' ve ardından gelen bilgiler ->işlevin dönüş değeri için bir ek açıklamadır.

foo.func_annotations bir sözlük döndürür:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

Bunu hazır bulundurmanın önemi nedir?



6
@SilentGhost: ne yazık ki, gerçek kullanım durumları ile bağlantıların çoğu kopuk. İçeriğin depolanabileceği herhangi bir yer var mı yoksa sonsuza dek kayboldu mu?
maksimum

16
olmamalıdır foo.func_annotations olmak foo.__annotations__python3 içinde?
zhangxaochen

2
Ek açıklamaların özel bir önemi yoktur. Python'un yaptığı tek şey onları ek açıklamalar sözlüğüne koymaktır . Diğer tüm eylemler size bağlıdır.
N Randhawa

ne def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):demek
Ali SH

Yanıtlar:


90

Bence bu gerçekten harika.

Akademik bir geçmişe dayanarak, ek açıklamaların Java gibi diller için akıllı statik analizörlerin etkinleştirilmesinde çok değerli olduğunu söyleyebilirim. Örneğin, devlet kısıtlamaları, erişmesine izin verilen iş parçacıkları, mimari sınırlamalar, vb. Gibi anlambilim tanımlayabilir ve daha sonra bunları okuyabilen ve derleyicilerden elde ettiğinizin ötesinde güvenceler sağlamak için bunları işleyebilen birkaç araç vardır. Ön koşulları / son şartları kontrol eden şeyler bile yazabilirsiniz.

Zayıf yazımı nedeniyle Python'da böyle bir şeye özellikle ihtiyaç duyulduğunu hissediyorum, ancak bunu açıkça ve resmi sözdiziminin bir parçası haline getiren hiçbir yapı yoktu.

Ek açıklamaların güvencenin ötesinde başka kullanımları da vardır. Java tabanlı araçlarımı Python'a nasıl uygulayabildiğimi görebiliyorum. Örneğin, yöntemlere özel uyarılar atamanıza izin veren bir araç var ve bunları aradığınızda size belgelerini okumanız gerektiğine dair göstergeler veriyor (örneğin, negatif bir değerle çağrılmaması gereken bir yönteminiz olduğunu hayal edin, ancak adından sezgisel değil). Ek açıklamalarla Python için teknik bir şey yazabilirim. Benzer şekilde, resmi bir sözdizimi varsa, yöntemleri etiketlere dayalı olarak büyük bir sınıfta düzenleyen bir araç yazılabilir.


34
ISTM, yalnızca standart kütüphane ve üçüncü taraf modüllerin hepsi işlev ek açıklamaları kullanıyorsa ve bunları tutarlı bir anlamla kullanıyorsa ve iyi düşünülmüş ek açıklama sistemleri kullanıyorsa gerçekleştirilebilen teorik faydalardır. O güne kadar (asla gelmeyecek), Python'un fonksiyon ek açıklamalarının ana kullanımları diğer cevaplarda açıklanan tek seferlik kullanımlar olacaktır. Şimdilik, akıllı statik analizörleri, derleyici güvencelerini, java tabanlı takım zincirlerini, vb.
Unutabilirsiniz

4
İşlev ek açıklamalarını kullanan her şey olmasa bile, bunları girdilerinde bulunan ve benzer şekilde ek açıklama eklenmiş diğer kodu çağıran kod içinde statik analiz için kullanabilirsiniz. Daha büyük bir proje veya kod tabanı içinde bu, ek açıklama tabanlı statik analiz yapmak için önemli ölçüde yararlı bir kod gövdesi olabilir.
gps

1
AFAICT, tüm bunları ek açıklamalardan önce gelen dekoratörler ile yapabilirsiniz; bu yüzden hala fayda görmüyorum. Bu soruyu biraz farklı bir şekilde ele
aldım

9
2015 yılına gelindiğinde, python.org/dev/peps/pep-0484 ve mypy-lang.org tüm muhaliflerin yanlış olduğunu kanıtlamaya başlıyor.
Mauricio Scheffer

1
Ayrıca Python'un Swift üzerindeki etkisini daha da ortaya koyuyor.
uchuugaka

92

İşlev ek açıklamaları bunlardan yaptığınız şeydir.

Belgeler için kullanılabilirler:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

Ön koşul kontrolü için kullanılabilirler:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

Ayrıca, tür denetlemesini uygulamanın bir yolu için http://www.python.org/dev/peps/pep-0362/ adresine bakın .


18
Bu, dokümantasyon için bir doktoradan veya fonksiyondaki açık tip kontrolünden daha iyi mi? Bu, dili sebepsiz yere zorlaştırıyor gibi görünüyor.
endolith

10
@endolith İşlev açıklamaları olmadan kesinlikle yapabiliriz. Ek açıklamalara erişmek için yalnızca standart bir yol sağlarlar. Bu, onları yardım () ve araç ipuçlarına erişilebilir kılar ve içgözlem için kullanılabilir hale getirir.
Raymond Hettinger

4
Aksine sen türlerini yaratabilecek sayılar etrafında geçen daha Massve Velocityonun yerine.
rightfold

1
bunu tam olarak def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second') -> float:göstermek için dönüş türünü de göstermek zorundayım . Bu benim en sevdiğim cevap.
Tommy

Kodunuzu kullanarak returnek açıklamayı doğrulamanın bir yolu var mı? Görünüşe göre görünmüyorlocals
user189728

46

Bu çok geç bir cevaptır, ancak AFAICT, fonksiyon açıklamalarının en iyi kullanımı PEP- 0484 ve MyPy'dir .

Mypy, Python için isteğe bağlı bir statik tip denetleyicisidir. Python 3.5 beta 1'de (PEP 484) tanıtılan tip ek açıklamaları için yaklaşan standardı kullanarak Python programlarınıza yazı ipuçları ekleyebilir ve bunları statik olarak kontrol etmek için mypy'yi kullanabilirsiniz.

Şöyle kullanılır:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b


Ayrıca bkz. Pytype - diğer statik analizör PEP- 0484 düşünülerek üretilmiştir.
gps

Maalesef bu tür zorunlu değildir. list(fib('a'))Örnek işlevinizle yazdığımda , Python 3.7 argümanı memnuniyetle kabul eder ve bir dize ile int'i karşılaştırmanın bir yolu olmadığından şikayet eder.
Denis de Bernardy

@DenisdeBernardy PEP-484'te açıklandığı gibi Python yalnızca tip ek açıklamaları sağlar. Türleri zorlamak için mypy kullanmanız gerekir.
Dustin Wyatt

23

Sadece benim cevap iyi bir kullanım özgün bir örneğini eklemek burada multimethods için basit bir mekanizma yapılabilir dekoratörler ile birleştiğinde,.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

ve bir kullanım örneği:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

Bu, Guido'nun orijinal postunun gösterdiği gibi, dekoratöre tipler eklenerek yapılabilir , ancak parametrelerin ve tiplerin yanlış eşleşmesi olasılığını önlediğinden parametrelerin kendilerine açıklama eklemek daha iyidir.

Not : In Python siz açıklamaları erişebilir function.__annotations__ziyade function.func_annotationsolarak func_*stil Python 3 çıkarıldı.


2
İlginç bir uygulama, korkarım function = self.typemap.get(types)alt sınıflar söz konusu olduğunda çalışmayacak. Bu durumda, muhtemelen typemapkullanarak döngüye girmeniz gerekir isinnstance. Acaba @overloadbunu doğru ele alır mı
Tobias Kienzler

Bence işlevin bir dönüş türü varsa bu bozuk
zenna

1
Bu __annotations__ , dictbağımsız değişkenlerin sırasını sağlamayan bir öğedir, bu nedenle bu snippet bazen başarısız olur. Ben değişen öneriyoruz types = tuple(...)için spec = inspect.getfullargspec(function)daha sonra types = tuple([spec.annotations[x] for x in spec.args]).
xoolive

Çok haklısın, @xoolive. Düzeltmenizi eklemek için cevabı neden düzenlemiyorsunuz?
Muhammad Alkarouri

@xoolive: Fark ettim. Bazen editörler düzenlemeleri yönetirken ağır bir el kullanırlar. Sorunuzu düzeltmenizi içerecek şekilde düzenledim. Aslında, bu konuda bir tartışma yaptım , ama düzeltmeyi reddetmenin bir yolu yok. Bu arada yardım için teşekkürler.
Muhammad Alkarouri

22

Uri zaten doğru bir cevap verdi, bu yüzden daha az ciddi bir cevap var: Böylece doktorluklarınızı kısaltabilirsiniz.


2
Sevdim. +1. Ancak, sonunda, docstrings yazmak hala benim kod okunabilir yapmak için bir numaralı yoludur, ancak, herhangi bir statik veya dinamik kontrol uygulamak olsaydı, bu olması güzel. Belki de bir faydası olabilir.
Warren P

8
Ek açıklamaları bir Args: bölümü veya @param satırları veya benzerlerini docstrings'inizde (kullanmayı seçerseniz seçin) kullanmanız önerilmez. Belge ek açıklamaları güzel bir örnek oluştururken, diğer daha güçlü kullanımların önüne geçebileceği için ek açıklamaların potansiyel gücünü karartır. Ayrıca, docstrings ve assert ifadelerinde olduğu gibi bellek tüketimini (python -OO) azaltmak için çalışma zamanında ek açıklamaları atlayamazsınız.
gps

2
@gps: Dediğim gibi, daha az ciddi bir cevaptı.
JAB

2
Tüm ciddiyetle, DuckTyping'e bağlı kalırken beklediğiniz türleri belgelemek için çok daha iyi bir yol.
Marc

1
@gps Docstrings'in bellek tüketiminin, vakaların% 99.999'unda endişelenecek bir şey olduğundan emin değilim.
Tommy

13

Ek açıklamaları ilk gördüğümde, "harika! Sonunda bir tür kontrol seçebilirim!" Diye düşündüm. Tabii ki, ek açıklamaların gerçekten uygulanmadığını fark etmemiştim.

Bu yüzden onları uygulamak için basit bir fonksiyon dekoratörü yazmaya karar verdim :

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

Bunu Ensure kütüphanesine ekledim .


Nihayet Python sonunda tür denetleme olduğuna inanarak çıktıktan sonra aynı hayal kırıklığı var. Sonunda ev tipi kontrol uygulaması ile devam etmek zorunda kalacak.
Hibou57

3

Bu sorulduğundan beri uzun bir süre geçmesine rağmen, soruda verilen örnek pasaj (orada da belirtildiği gibi) PEP 3107'den ve bu PEP örneğinin sonunda, soruyu PEP'ler noktasından cevaplayabilecek kullanım örnekleri de verilmiştir. görünüm ;)

Aşağıdakiler PEP3107'den alınmıştır.

Kullanım Örnekleri

Ek açıklamaların tartışılması sırasında birtakım kullanım örnekleri gündeme gelmiştir. Bunlardan bazıları burada, ne tür bilgileri ilettiklerine göre gruplandırılmış olarak sunulmaktadır. Ek açıklamalardan yararlanabilecek mevcut ürün ve paket örnekleri de dahildir.

  • Yazma bilgisi sağlama
    • Tip kontrolü ([3], [4])
    • IDE'lerin bir işlevin ne tür beklediğini ve döndürdüğünü göstermesine izin verin ([17])
    • Fonksiyon aşırı yüklenmesi / genel fonksiyonlar ([22])
    • Yabancı dil köprüleri ([18], [19])
    • Adaptasyon ([21], [20])
    • Mantık işlevlerini tahmin etme
    • Veritabanı sorgu eşlemesi
    • RPC parametre sıralama ([23])
  • Diğer bilgiler
    • Parametreler ve dönüş değerleri belgeleri ([24])

Belirli noktalar (ve bunların referansları) hakkında daha fazla bilgi için PEP'e bakınız.


Downvoters, downvote'a neden olan en azından kısa bir yorum bırakırsa gerçekten takdir ediyorum. Bu gerçekten (en azından bana) gelişmeye çok yardımcı olacaktır.
klaas

2

Python 3.X (yalnızca), bağımsız değişkenlerin ve dönüş değerlerinin uzantılarda kullanılmak üzere nesne değerleriyle açıklanmasına izin vermek için işlev tanımını genelleştirir .

META verilerini açıklamak, fonksiyon değerleri hakkında daha açık olmak.

Ek açıklamalar :valuebağımsız değişken adından sonra ve varsayılandan önce ->valueve bağımsız değişken listesinden sonra olduğu gibi kodlanır .

Bunlar __annotations__işlevin bir niteliğinde toplanır , ancak başka türlü Python tarafından özel olarak ele alınmazlar:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Kaynak: Python Cep Referansı, Beşinci Baskı

MİSAL:

typeannotationsModül tür denetimi ve Python kodunun tür kesmesi için bir araç kümesi sağlar. Ayrıca, işlevlere ve nesnelere açıklama eklemek için kullanışlı bir dizi tür sağlar.

Bu araçlar temel olarak linter, kod tamamlama kütüphanesi ve IDE gibi statik analizörler tarafından kullanılmak üzere tasarlanmıştır. Ayrıca, çalışma zamanı denetimleri yapmak için dekoratörler de sağlanır. Çalışma zamanı türü denetimi Python'da her zaman iyi bir fikir değildir, ancak bazı durumlarda çok yararlı olabilir.

https://github.com/ceronman/typeannotations

Yazma Daha İyi Kod Yazmaya Nasıl Yardımcı Olur

Yazma, kodunuzu üretime göndermeden ve bazı açık hatalardan kaçınmadan önce tür hatalarını yakalamak için statik kod analizi yapmanıza yardımcı olabilir. Mypy gibi, yazılım yaşam döngünüzün bir parçası olarak araç kutunuza ekleyebileceğiniz araçlar vardır. mypy, kod tabanınıza kısmen veya tamamen çalışarak doğru türleri denetleyebilir. mypy ayrıca, değer bir işlevden döndürüldüğünde Hiçbiri türünü denetleme gibi hataları algılamanıza yardımcı olur. Yazmak, kodunuzu daha temiz hale getirmenize yardımcı olur. Bir doktora türünü belirttiğiniz yorumları kullanarak kodunuzu belgelemek yerine, herhangi bir performans maliyeti olmadan türleri kullanabilirsiniz.

Temiz Python: Python'da Zarif Kodlama ISBN: ISBN-13 (pbk): 978-1-4842-4877-5

PEP 526 - Değişken Ek Açıklamalar için Sözdizimi

https://www.python.org/dev/peps/pep-0526/

https://www.attrs.org/en/stable/types.html


@BlackJack, "uzantılarda kullanım için" belli değildi?
Demz

Anlaşılır, ancak IMHO sorusuna cevap vermiyor. Bu, "Programlarda kullanım için" ile "Sınıfların iyi kullanımları nelerdir?" Sorusunu yanıtlamak gibidir. Açık, doğru, ancak soran taraf, iyi betonun kullandığı hal konusunda gerçekten daha akıllı değildir . Sizinki, daha genel olamayacak bir cevaptır, aslında sorudakiyle aynı olan bir örnek .
BlackJack

1

Burada açıklanan tüm kullanımlara rağmen, ek açıklamaların uygulanabilir ve büyük olasılıkla zorunlu kullanımı tür ipuçları içindir .

Bu şu anda hiçbir şekilde uygulanmamaktadır, ancak PEP 484'ten yola çıkarak, Python'un gelecekteki sürümleri yalnızca ek açıklamaların değeri olarak türlere izin verecektir.

Alıntı Ek açıklamaların mevcut kullanımları ne olacak? :

Tip ipuçlarının sonunda ek açıklamalar için tek kullanım olacağını umuyoruz, ancak bu, Python 3.5 ile yazma modülünün ilk kullanıma sunulmasından sonra ek tartışma ve kullanımdan kaldırma süresi gerektirecektir. Mevcut PEP, Python 3.6 serbest bırakılana kadar geçici bir duruma sahip olacaktır (bkz. PEP 411). Akla gelebilecek en hızlı şema, 3.6'da tip ipucu olmayan ek açıklamaların sessizce kaldırılmasını, 3.7'de tam kullanımdan kaldırılmasını ve Python 3.8'de ek açıklamaların izin verilen tek kullanımı olarak tip ipuçlarını beyan edecektir.

3.6'da henüz herhangi bir sessiz itiraz görmeme rağmen, bunun yerine 3.7'ye çok iyi bir şekilde çarpılabilir.

Bu nedenle, başka iyi kullanım durumları olsa da, bu kısıtlamanın olduğu bir gelecekte her şeyi değiştirmek istemiyorsanız, bunları yalnızca tür ipucu için tutmak en iyisidir.


1

Gecikmeli bir cevap olarak, paketlerimden birkaçı (marrow.script, WebCore vb.), Daktiloyu bildirmek (varsa, web'den gelen değerleri dönüştürmek, hangi argümanların boole anahtarları olduğunu tespit etmek) için ek açıklamalar kullanır. ek argüman işaretlemesi yapmak için.

İliği Komut Dosyası rastgele işlevler ve sınıflar için eksiksiz bir komut satırı arabirimi oluşturur ve eski çalışma zamanlarını desteklemek için bir dekoratörle ek açıklamalar yoluyla dokümantasyon, döküm ve geri çağrıdan türetilen varsayılan değerleri tanımlamaya izin verir. Ek açıklamaları kullanan tüm kütüphanelerim formları destekliyor:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

Docstrings veya typecasting işlevleri için "çıplak" destek, açıklama eki olan diğer kütüphanelerle daha kolay karıştırmaya olanak tanır. (Komut satırı komut dosyası olarak da ifşa edilen yazı tiplerini kullanan bir web denetleyicim var.)

Eklemek için düzenlendi: Ayrıca doğrulama için geliştirme zamanı iddialarını kullanarak TypeGuard paketini kullanmaya başladım . Faydası: "optimizasyonlar" etkinken ( -O/ PYTHONOPTIMIZEenv var) çalıştırıldığında, pahalı olabilecek kontroller (örn. Özyinelemeli) atlanır, uygulamanızın geliştirilmesinde düzgün bir şekilde test edildiğinden, kontroller üretimde gereksiz olmalıdır.


-2

Ek açıklamalar, kodu kolayca modüle etmek için kullanılabilir. Bakımını yaptığım bir program için bir modül sadece şöyle bir yöntem tanımlayabilir:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

ve kullanıcıdan "saymak için gerekli" ve "int" olması gereken "param1" adında bir şey isteyebiliriz. Sonunda, en sorunsuz deneyimi elde etmek için kullanıcı tarafından verilen dizgiyi istenen türe dönüştürebiliriz.

Buna yardımcı olan ve gerekli değerleri otomatik olarak alabilen ve bunları istenen herhangi bir türe dönüştürebilen (ek açıklama bir dönüşüm yöntemidir) açık kaynak sınıfı için işlev meta veri nesnesimize bakın . IDE'ler bile otomatik tamamlamaları doğru gösterir ve türlerin ek açıklamalara uygun olduğunu varsayar - mükemmel uyum.


-2

Cython'un faydaları listesine bakarsanız, büyük bir tanesi derleyiciye bir Python nesnesinin hangi tipte olduğunu söyleme yeteneğidir.

Cython'un (veya Python kodunuzun bir kısmını derleyen benzer araçların) sihirlerini yapmak için ek açıklama sözdizimini kullanacağı bir gelecek öngörebilirim.


RPython Annotator uygun Pythonic hisseder bir yaklaşımın bir örneği olmaktadır; uygulamanızın bir grafiğini oluşturduktan sonra, her değişkenin türünü çözebilir ve (RPython için) tek tip güvenliği zorlayabilir. OTOH, dinamik zengin değerlere izin vermek için "boks" veya başka çözümler / çözüm yolları gerektirir. Tamamen geçerli multiplyolduğunda 'na' * 8 + ' batman!', kimin işlevimi yalnızca tamsayılara karşı çalışmaya zorlayacağım ? ;)
amcgregor
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.