* Args ve ** kwargs için ek açıklamalar yazın


158

Bazı arayüzler yazmak için soyut temel sınıflarla Python'un tip ek açıklamalarını deniyorum. Olası *argsve türlerine açıklama eklemenin bir yolu var mı **kwargs?

Örneğin, bir işleve ilişkin mantıklı argümanların ya bir intya da iki ints olduğunu nasıl ifade edebiliriz ? type(args)verir Tuplebenim tahminim olarak türünü açıklama oldu bu yüzden Union[Tuple[int, int], Tuple[int]], ama bu işe yaramaz.

from typing import Union, Tuple

def foo(*args: Union[Tuple[int, int], Tuple[int]]):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

# ok
print(foo((1,)))
print(foo((1, 2)))
# mypy does not like this
print(foo(1))
print(foo(1, 2))

Mypy hata mesajları:

t.py: note: In function "foo":
t.py:6: error: Unsupported operand types for + ("tuple" and "Union[Tuple[int, int], Tuple[int]]")
t.py: note: At top level:
t.py:12: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:14: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 2 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"

Mypy işlev çağrısı için bundan hoşlanmaz çünkü tupleçağrının kendisinde bir tane olmasını bekler . Paketi açtıktan sonra ek olarak anlamadığım bir yazma hatası verir.

İnsan nasıl için mantıklı türlerini açıklama yok *argsve **kwargs?

Yanıtlar:


167

Değişken pozisyonel argümanlar (For *args) ve değişken anahtar kelime argümanlar ( **kw) yalnızca için beklenen değerini belirtmek gerekir bir tür argüman.

Gönderen Keyfi argüman listeleri ve varsayılan argüman değerleri bölümünün içinde Tipi İpuçları PEP:

Rasgele argüman listeleri de açıklama eklenebilir, böylece tanım:

def foo(*args: str, **kwds: int): ...

kabul edilebilirdir ve şu anlama gelir, örneğin aşağıdakilerin tümü geçerli argüman türlerine sahip işlev çağrılarını temsil eder:

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

Dolayısıyla, yönteminizi şu şekilde belirtmek istersiniz:

def foo(*args: int):

Ancak, işleviniz yalnızca bir veya iki tamsayı değerini kabul edebiliyorsa, hiç kullanmamalısınız *args, bir açık konum bağımsız değişkeni ve ikinci bir anahtar kelime bağımsız değişkeni kullanın:

def foo(first: int, second: Optional[int] = None):

Şimdi işleviniz aslında bir veya iki argümanla sınırlıdır ve belirtilirse her ikisi de tamsayı olmalıdır. *args her zaman 0 veya daha fazla anlamına gelir ve tür ipuçlarıyla daha spesifik bir aralıkla sınırlandırılamaz.


1
Sadece merak ediyorum, neden eklemeliyim Optional? Python ile ilgili bir şey mi değişti yoksa fikrini mi değiştirdin? NoneTemerrüt nedeniyle hala kesinlikle gerekli değil mi?
Praxeolitic

10
@Praxeolitic evet, pratikte varsayılan değer olarak Optionalkullandığınızda otomatik, ima edilen ek açıklama Nonebelirli kullanımları zorlaştırdı ve şimdi PEP'ten kaldırılıyor.
Martijn Pieters

5
İlgilenenler için bunu tartışan bir bağlantı . Kesinlikle Optionalgelecekte açıkça gerekli olacak gibi geliyor .
Rick

Bu aslında Callable için desteklenmez: github.com/python/mypy/issues/5876
Shah

1
@ShitalShah: bu konuyla ilgili değil. herhangi bir tür ipucu veya tam durdurma için herhangi bir bahsi Callabledesteklemez . Bu özel konu, belirli argümanları ve diğerlerinin gelişigüzel sayısını kabul eden callables'ları işaretlemektir ve bu nedenle , iki yakalama için çok özel bir tür ipucu kullanır. Ayarladığınız ve / veya daha spesifik bir şeye ayarladığınız durumlarda, a . *args**kwargs *args: Any, **kwargs: Any*args**kwargsProtocol
Martijn Pieters

26

Bunu yapmanın doğru yolu @overload

from typing import overload

@overload
def foo(arg1: int, arg2: int) -> int:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))

@overloadAsıl uygulamaya son eklenmesi gereken ek açıklamalar eklemediğiniz veya yazmamanız gerektiğini unutmayın.

Saplama dosyalarının dışındatyping @overload desteği almak için hem mypy'nin hem de mypy'nin yeni bir sürümüne ihtiyacınız olacak .

Bunu, döndürülen sonucu hangi argüman türlerinin hangi dönüş türüne karşılık geldiğini açıkça gösterecek şekilde değiştirmek için de kullanabilirsiniz. Örneğin:

from typing import Tuple, overload

@overload
def foo(arg1: int, arg2: int) -> Tuple[int, int]:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return j, i
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))

2
Bu cevabı beğendim çünkü daha genel duruma değiniyor. Geriye dönüp baktığımda, örnek olarak (type1)vs (type1, type1)fonksiyon çağrılarını kullanmamalıydım. Belki (type1)vs (type2, type1)daha iyi bir örnek olurdu ve bu cevabı neden sevdiğimi gösteriyor. Bu aynı zamanda farklı dönüş tiplerine de izin verir. Bununla birlikte, sadece bir dönüş tipine sahip olduğunuz ve sizin *argsve *kwargsaynı tipte olduğunuz özel durumda , Martjin'in cevabındaki teknik daha mantıklıdır, bu nedenle her iki cevap da faydalıdır.
Praxeolitic

4
Kullanılması *args(burada 2) argümanlar maksimum sayıdır nerede yanlış hala ancak.
Martijn Pieters

1
@MartijnPieters *argsBurada neden mutlaka yanlış? Beklenen çağrılar (type1)vs ise (type2, type1), bağımsız değişken sayısı değişkendir ve sondaki bağımsız değişken için uygun bir varsayılan değer yoktur. Bir maks'in olması neden önemlidir?
Praxeolitic

1
*argsGerçekten orada içindir sıfır veya daha fazla , kapağını açıp, homojen argümanlar veya yakalama tanıyabileceğiniz 'bakir boyunca bu geçen' için. Gereken bir bağımsız değişken ve bir isteğe bağlı. Bu tamamen farklıdır ve normalde ikinci argümanın atlandığını algılamak için varsayılan bir varsayılan değer vererek işlenir.
Martijn Pieters

3
PEP'ye baktıktan sonra, bu açıkça @overload'un kullanım amacı değildir. Bu cevap, türlerine bireysel olarak açıklama eklemenin ilginç bir yolunu gösterse de *args, sorunun daha iyi bir cevabı, bunun yapılması gereken bir şey olmadığıdır.
Praxeolitic

20

Önceki cevaba kısa bir ek olarak, Python 2 dosyalarında mypy'yi kullanmaya çalışıyorsanız ve ek açıklamalar yerine tür eklemek için yorum kullanmanız gerekiyorsa, sırasıyla argsve kwargsile *ve türlerinin önüne ön ek eklemeniz gerekir **:

def foo(param, *args, **kwargs):
    # type: (bool, *str, **int) -> None
    pass

Bu mypy tarafından aşağıdaki Python 3.5 sürümü ile aynı olarak ele alınır foo:

def foo(param: bool, *args: str, **kwargs: int) -> None:
    pass
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.