* Args और ** kwargs के लिए एनोटेशन टाइप करें


158

मैं कुछ आधार लिखने के लिए अमूर्त आधार कक्षाओं के साथ पायथन के प्रकार एनोटेशन की कोशिश कर रहा हूं। क्या संभव प्रकारों को एनोटेट करने का एक तरीका है *argsऔर **kwargs?

उदाहरण के लिए, कोई कैसे व्यक्त करेगा कि एक फ़ंक्शन के लिए समझदार तर्क intया तो एक या दो intएस हैं? type(args)देता है Tupleतो मेरा अनुमान है के रूप में प्रकार व्याख्या करने के लिए था Union[Tuple[int, int], Tuple[int]], लेकिन यह काम नहीं करता।

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))

मैपी से त्रुटि संदेश:

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 को फंक्शन कॉल के लिए यह पसंद नहीं है क्योंकि यह उम्मीद करता tupleहै कि कॉल में ही ऐसा हो। इसके अलावा unpacking भी एक टाइपिंग त्रुटि देता है जो मुझे समझ में नहीं आता है।

कैसे *argsऔर के लिए समझदार प्रकार एनोटेट करता है **kwargs?

जवाबों:


167

चर स्थितीय तर्कों (के लिए *args) और चर कीवर्ड तर्क ( **kw) आप के लिए ही उम्मीद मूल्य निर्दिष्ट करने की आवश्यकता एक ऐसे तर्क।

प्रकार संकेत पीईपी के मनमाने तर्क सूचियों और डिफ़ॉल्ट तर्क मान अनुभाग से :

मनमाने ढंग से तर्क सूचियाँ टाइप की जा सकती हैं, ताकि परिभाषा:

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

स्वीकार्य है और इसका अर्थ है कि, उदाहरण के लिए, निम्न प्रकार के सभी फ़ंक्शन मान्य प्रकार के तर्कों के साथ कॉल करते हैं:

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

तो आप इस तरह अपनी विधि निर्दिष्ट करना चाहते हैं:

def foo(*args: int):

हालाँकि, यदि आपका फ़ंक्शन केवल एक या दो पूर्णांक मानों को स्वीकार कर सकता है, तो आपको बिल्कुल भी उपयोग नहीं करना चाहिए *args, एक स्पष्ट स्थिति तर्क और दूसरे कीवर्ड तर्क का उपयोग करें:

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

अब आपका कार्य वास्तव में एक या दो तर्कों तक सीमित है, और निर्दिष्ट होने पर दोनों पूर्णांक होने चाहिए। *args हमेशा 0 या अधिक का मतलब है, और टाइप संकेत द्वारा एक अधिक विशिष्ट सीमा तक सीमित नहीं किया जा सकता है।


1
बस जिज्ञासु, क्यों जोड़ें Optional? क्या पायथन के बारे में कुछ बदल गया या आपने अपना दिमाग बदल दिया? क्या Noneडिफ़ॉल्ट के कारण यह अभी भी सख्ती से आवश्यक नहीं है ?
प्रिक्सोलिटिक

10
@Praxeolitic हाँ, Optionalजब आप Noneएक डिफ़ॉल्ट मान के रूप में उपयोग करते हैं , तो स्वचालित रूप से निहित एनोटेशन का उपयोग कुछ कठिन परिश्रम से किया जाता है और जिसे अब PEP से हटा दिया जा रहा है।
मार्टिन पीटर्स

5
यहाँ इस पर चर्चा करने वालों के लिए एक लिंक दिया गया है । यह निश्चित रूप से ध्वनि करता है जैसे Optionalकि भविष्य में स्पष्ट होना आवश्यक है।
रिक

यह वास्तव में Callable के लिए समर्थित नहीं है: github.com/python/mypy/issues/5876
शीतल शाह

1
@ शीतलशाह: यह वास्तव में उस मुद्दे के बारे में नहीं है। Callableका समर्थन नहीं करता किसी भी के लिए एक प्रकार का संकेत का उल्लेख *argsया **kwargs पूर्ण विराम । यह विशिष्ट मुद्दा उन कॉलबल्स को चिह्नित करने के बारे में है जो विशिष्ट तर्कों को स्वीकार करते हैं और दूसरों की एक मनमानी संख्या को स्वीकार करते हैं , और इसलिए *args: Any, **kwargs: Anyदो कैच-ऑल के लिए एक बहुत ही विशिष्ट प्रकार के संकेत का उपयोग करते हैं । उन मामलों के लिए जहां आप सेट करते हैं *argsऔर / या **kwargsकुछ और विशिष्ट के लिए आप एक का उपयोग कर सकते हैं Protocol
मार्टिन पीटर्स

26

ऐसा करने का उचित तरीका उपयोग कर रहा है @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))

ध्यान दें कि आप @overloadवास्तविक क्रियान्वयन के लिए एनोटेशन को नहीं जोड़ते हैं या टाइप नहीं करते हैं , जो अंतिम होना चाहिए।

आपको स्टब फ़ाइलों के बाहरtyping @overload के लिए समर्थन प्राप्त करने के लिए दोनों और mypy के नए संस्करण की आवश्यकता होगी ।

आप लौटे हुए परिणाम को अलग करने के लिए भी इसका उपयोग कर सकते हैं जो स्पष्ट करता है कि किस प्रकार के तर्क किस प्रकार के रिटर्न से मेल खाते हैं। उदाहरण के लिए:

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
मुझे यह उत्तर पसंद है क्योंकि यह अधिक सामान्य मामले को संबोधित करता है। पीछे देखते हुए, मुझे अपने उदाहरण के रूप में (type1)बनाम (type1, type1)कॉल का उपयोग नहीं करना चाहिए था । हो सकता है कि (type1)बनाम (type2, type1)इसका एक बेहतर उदाहरण हो और दिखाता हो कि मुझे यह जवाब क्यों पसंद है। यह अलग-अलग रिटर्न प्रकारों को भी अनुमति देता है। हालांकि, विशेष मामले में जहां आपके पास केवल एक रिटर्न प्रकार और आपका है *argsऔर *kwargsसभी समान प्रकार हैं, मार्टजिन के उत्तर में तकनीक अधिक समझ में आती है इसलिए दोनों उत्तर उपयोगी होते हैं।
प्रिक्सोलिटिक

4
उपयोग करना *argsजहाँ तर्क की अधिकतम संख्या है (2 यहाँ) अभी भी गलत है
मार्टिन पीटर्स

1
@MartijnPieters यहाँ क्यों *argsगलत है? यदि अपेक्षित कॉल (type1)बनाम थे (type2, type1), तो तर्कों की संख्या परिवर्तनशील है और अनुगामी तर्क के लिए उपयुक्त डिफ़ॉल्ट नहीं है। यह महत्वपूर्ण क्यों है कि एक अधिकतम है?
प्रिक्सोलिटिक

1
*argsवास्तव में शून्य या अधिक , अनकैप्ड, समरूप तर्कों के लिए या 'अछूते इन कैच-ऑल के साथ गुजरने' के लिए है। आपके पास एक आवश्यक तर्क है और एक वैकल्पिक है। यह पूरी तरह से अलग है और आम तौर पर छोड़े गए पता लगाने के लिए दूसरा तर्क प्रहरी डिफ़ॉल्ट मान देकर नियंत्रित किया जाता है।
मार्टिन पीटर्स

3
PEP को देखने के बाद, यह स्पष्ट रूप से @overload का उपयोग नहीं है। हालांकि यह उत्तर व्यक्तिगत रूप से प्रकारों को एनोटेट करने के लिए एक दिलचस्प तरीका दिखाता है *args, लेकिन सवाल का एक और बेहतर जवाब यह है कि यह ऐसा कुछ नहीं है जिसे बिल्कुल किया जाना चाहिए।
प्रिक्सोलिटिक

20

पिछले उत्तर के लिए एक छोटे से अतिरिक्त के रूप में, यदि आप पायथन 2 फाइलों पर mypy का उपयोग करने की कोशिश कर रहे हैं और टिप्पणी के बजाय एनोटेशन के प्रकार जोड़ने के लिए टिप्पणियों का उपयोग करने की आवश्यकता है, तो आपको क्रमशः argsऔर उसके kwargsसाथ *और इसके प्रकार टाइप करने की आवश्यकता है **:

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

यह नीचे के रूप में ही होने के रूप में mypy द्वारा इलाज किया जाता है, पायथन 3.5 संस्करण foo:

def foo(param: bool, *args: str, **kwargs: int) -> None:
    pass
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.