डेटा साइंस में Python का सामान्य सिंटैक्स (एडवांस)

हाल ही में, मैं Data Science from Scratch (PDF लिंक) नामक किताब पढ़ रहा था। डेटा साइंस में कदम रखने वालों के लिए यह एक बेहतरीन और आसानी से समझ आने वाली किताब है। इसके एक अध्याय में Python के मूल सिंटैक्स और डेटा साइंस में इस्तेमाल होने वाले कुछ एडवांस सिंटैक्स के बारे में बताया गया है। मुझे लगा कि इसकी जानकारी बहुत अच्छी और सीधी-सादी है, इसलिए मैंने इसे यहाँ अपनी याददाश्त ताज़ा रखने के लिए अनुवाद करके रखा है। डेटा साइंस में Python का सामान्य सिंटैक्स (बेसिक) डेटा साइंस में Python का सामान्य सिंटैक्स (एडवांस)

यह अध्याय Python के उन एडवांस सिंटैक्स और फ़ंक्शंस पर केंद्रित है जो डेटा प्रोसेसिंग में बहुत उपयोगी हैं (Python 2.7 पर आधारित)।

सॉर्टिंग (Sorting)

यदि आप Python की किसी लिस्ट को सॉर्ट करना चाहते हैं, तो आप लिस्ट के sort मेथड का उपयोग कर सकते हैं। यदि आप मूल लिस्ट को नहीं बदलना चाहते, तो आप एक नई सॉर्ट की गई लिस्ट प्राप्त करने के लिए sorted फ़ंक्शन का उपयोग कर सकते हैं:

x = [4,1,2,3]
y = sorted(x)       # y = [1,2,3,4], x अपरिवर्तित रहता है
x.sort()            # अब x = [1,2,3,4] है
# बाय डिफ़ॉल्ट, `sort` या `sorted` लिस्ट को छोटे से बड़े क्रम में सॉर्ट करते हैं।

यदि आप इसे बड़े से छोटे क्रम में सॉर्ट करना चाहते हैं, तो आप reverse = True पैरामीटर सेट कर सकते हैं।

आप कस्टम सॉर्टिंग फ़ंक्शन भी बना सकते हैं, जिससे लिस्ट को किसी खास कीवर्ड के अनुसार सॉर्ट किया जा सके:

# एब्सोल्यूट वैल्यू के अनुसार बड़े से छोटे क्रम में सॉर्ट करें
x = sorted([-4,1,-2,3], key=abs, reverse=True) # is [-4,3,-2,1]
# शब्दों के आने की संख्या के अनुसार बड़े से छोटे क्रम में सॉर्ट करें
wc = sorted(word_counts.items(),
key=lambda (word, count): count,
reverse=True)

लिस्ट कॉम्प्रिहेंशन्स (List Comprehensions)

अक्सर हमें ऐसी स्थिति का सामना करना पड़ता है जहाँ हम किसी लिस्ट के कुछ खास एलिमेंट्स को निकालकर एक नई लिस्ट बनाना चाहते हैं, या उनमें से कुछ एलिमेंट्स के मान बदलना चाहते हैं, या दोनों करना चाहते हैं। Python में इसका सामान्य तरीका लिस्ट कॉम्प्रिहेंशन्स (List Comprehensions) का उपयोग करना है:

even_numbers = [x for x in range(5) if x % 2 == 0]  # [0, 2, 4]
squares = [x * x for x in range(5)]                 # [0, 1, 4, 9, 16]
even_squares = [x * x for x in even_numbers]        # [0, 4, 16]

इसी तरह, आप लिस्ट को डिक्शनरी या सेट में बदल सकते हैं:

square_dict = { x : x * x for x in range(5) }       # { 0:0, 1:1, 2:4, 3:9, 4:16 }
square_set = { x * x for x in [1, -1] }             # { 1 }

यदि आपको लिस्ट के एलिमेंट्स का उपयोग करने की आवश्यकता नहीं है, तो आप अंडरस्कोर (_) को एक वेरिएबल के रूप में उपयोग कर सकते हैं:

zeroes = [0 for _ in even_numbers] # लिस्ट `even_numbers` के समान लंबाई

लिस्ट कॉम्प्रिहेंशन्स मल्टीपल for लूप्स को सपोर्ट करते हैं:

pairs = [(x, y)
    for x in range(10)
    for y in range(10)]    # कुल 100 जोड़े: (0,0) (0,1) ... (9,8), (9,9)

बाद वाले for लूप पहले वाले for लूप के परिणामों का उपयोग कर सकते हैं:

increasing_pairs = [(x, y)                      # केवल x < y वाले जोड़े शामिल हैं
                    for x in range(10)          # range(lo, hi) बराबर है
                    for y in range(x + 1, 10)]  # [lo, lo + 1, ..., hi - 1]

भविष्य में हम लिस्ट कॉम्प्रिहेंशन्स का अक्सर उपयोग करेंगे।

जनरेटर और इटरेटर (Generators and Iterators)

लिस्ट के साथ एक समस्या यह है कि वे आसानी से बहुत बड़ी हो सकती हैं, उदाहरण के लिए range(1000000) एक मिलियन एलिमेंट्स वाली लिस्ट बनाएगा। यदि आप एक समय में केवल एक डेटा प्रोसेस करते हैं, तो इसमें बहुत समय लग सकता है (या मेमोरी खत्म हो सकती है)। और असल में, आपको शायद सिर्फ़ पहले कुछ डेटा की ही ज़रूरत हो, तो बाकी की गणनाएँ अनावश्यक हो जाती हैं।

जनरेटर आपको केवल उन्हीं डेटा पर इटरेट करने देते हैं जिनकी आपको ज़रूरत होती है। आप एक फ़ंक्शन और yield एक्सप्रेशन का उपयोग करके एक जनरेटर बना सकते हैं:

def lazy_range(n):
    """range का एक 'आलसी' संस्करण"""
    i = 0
    while i < n:
        yield i
        i += 1

अनुवादक का नोट: जनरेटर भी एक प्रकार के विशेष इटरेटर होते हैं। yield जनरेटर में आइट्रेशन को लागू करने की कुंजी है। यह जनरेटर के निष्पादन को रोकने और फिर से शुरू करने का एक बिंदु होता है। आप yield एक्सप्रेशन को एक मान असाइन कर सकते हैं, और यह yield एक्सप्रेशन के मान को भी लौटा सकता है। कोई भी फ़ंक्शन जिसमें yield स्टेटमेंट होता है, उसे जनरेटर कहा जाता है। जनरेटर से बाहर निकलते समय, यह अपनी वर्तमान निष्पादन स्थिति को सहेज लेता है, और अगली बार निष्पादित होने पर, यह अगले आइट्रेशन मान को प्राप्त करने के लिए उस स्थिति को बहाल करता है। लिस्ट आइट्रेशन में बहुत अधिक एड्रेस स्पेस लगता है, जबकि जनरेटर का उपयोग करने से लगभग एक ही एड्रेस स्पेस का उपयोग होता है, जिससे मेमोरी की बचत होती है।

नीचे दिया गया लूप yield से एक-एक करके मान का उपभोग करेगा जब तक कि सभी मान समाप्त न हो जाएँ:

for i in lazy_range(10):
    do_something_with(i)

(वास्तव में, Python में _lazy_range_ जैसा ही एक फ़ंक्शन है, जिसे xrange कहा जाता है, और Python 3 में इसे range कहा जाता है।) इसका मतलब है कि आप एक अनंत अनुक्रम बना सकते हैं:

def natural_numbers():
    """1, 2, 3, ... लौटाता है"""
    n = 1
    while True:
        yield n
        n += 1

हालांकि, ऐसे स्टेटमेंट का उपयोग करने की सलाह नहीं दी जाती है जिनमें लूप से बाहर निकलने का कोई तर्क न हो।

TIP

जनरेटर का उपयोग करके आइट्रेट करने का एक नुकसान यह है कि आप एलिमेंट्स को शुरू से अंत तक केवल एक बार ही आइट्रेट कर सकते हैं। यदि आप कई बार आइट्रेट करना चाहते हैं, तो आपको हर बार एक नया जनरेटर बनाना होगा या लिस्ट का उपयोग करना होगा।

जनरेटर बनाने का दूसरा तरीका: कोष्ठकों के भीतर एक्सप्रेशन का उपयोग करके:

lazy_evens_below_20 = (i for i in lazy_range(20) if i % 2 == 0)

हम जानते हैं कि डिक्शनरी का items() मेथड डिक्शनरी के सभी की-वैल्यू पेयर्स की एक लिस्ट लौटाता है, लेकिन ज़्यादातर मामलों में, हम iteritems() जनरेटर मेथड का उपयोग आइट्रेशन के लिए करते हैं, जो हर बार केवल एक की-वैल्यू पेयर उत्पन्न करता है और लौटाता है।

रैंडमनेस (Randomness)

डेटा साइंस सीखते समय, हमें अक्सर रैंडम नंबर बनाने की ज़रूरत पड़ेगी, इसलिए random मॉड्यूल को इम्पोर्ट करके इसका उपयोग किया जा सकता है:

import random
four_uniform_randoms = [random.random() for _ in range(4)]
# [0.8444218515250481,        # random.random() रैंडम नंबर उत्पन्न करता है
# 0.7579544029403025,         # रैंडम नंबरों को 0 और 1 के बीच मानों के लिए सामान्यीकृत किया जाता है
# 0.420571580830845,          # यह फ़ंक्शन रैंडम नंबर बनाने के लिए सबसे अधिक उपयोग किया जाने वाला फ़ंक्शन है
# 0.25891675029296335]

यदि आप ऐसे परिणाम चाहते हैं जिन्हें दोहराया जा सके, तो आप random मॉड्यूल को random.seed द्वारा निर्धारित आंतरिक स्थिति के आधार पर स्यूडो-रैंडम (यानी, नियतात्मक) नंबर उत्पन्न करने के लिए कह सकते हैं:

random.seed(10)           # सीड को 10 पर सेट करें
print random.random()     # 0.57140259469
random.seed(10)           # सीड को 10 पर फिर से सेट करें
print random.random()     # 0.57140259469 फिर से

कभी-कभी हम random.randrange फ़ंक्शन का उपयोग एक निश्चित सीमा के भीतर एक रैंडम नंबर बनाने के लिए भी करते हैं:

random.randrange(10)      # `range(10) = [0, 1, ..., 9]` में से एक रैंडम नंबर चुनें
random.randrange(3, 6)    # `range(3, 6) = [3, 4, 5]` में से एक रैंडम नंबर चुनें

कुछ अन्य तरीके भी हैं जो कभी-कभी बहुत काम आते हैं, जैसे random.shuffle एक लिस्ट के एलिमेंट्स के क्रम को बदल देता है और एक नई रैंडम रूप से व्यवस्थित लिस्ट बनाता है:

up_to_ten = range(10)
random.shuffle(up_to_ten)
print up_to_ten
# [2, 5, 1, 9, 7, 3, 8, 6, 4, 0] (आपको अलग परिणाम मिलना चाहिए)

यदि आप किसी लिस्ट में से एक रैंडम एलिमेंट चुनना चाहते हैं, तो आप random.choice मेथड का उपयोग कर सकते हैं:

my_best_friend = random.choice(["Alice", "Bob", "Charlie"]) # मुझे "Bob" मिला

यदि आप एक रैंडम अनुक्रम बनाना चाहते हैं और मूल लिस्ट को भी नहीं बदलना चाहते हैं, तो आप random.sample मेथड का उपयोग कर सकते हैं:

lottery_numbers = range(60)
winning_numbers = random.sample(lottery_numbers, 6) # [16, 36, 10, 6, 25, 9]

आप एक से अधिक रैंडम सैंपल्स चुनने के लिए कई बार कॉल करके ऐसा कर सकते हैं (दोहराव की अनुमति है):

four_with_replacement = [random.choice(range(10))
                         for _ in range(4)]
# [9, 4, 4, 2]

रेगुलर एक्सप्रेशंस (Regular Expressions)

रेगुलर एक्सप्रेशंस का उपयोग टेक्स्ट सर्च के लिए किया जाता है; वे थोड़े जटिल होते हैं लेकिन बहुत उपयोगी हैं, और इसलिए रेगुलर एक्सप्रेशंस पर कई किताबें लिखी गई हैं। जब हम उनका सामना करेंगे तब हम उनकी विस्तृत व्याख्या करेंगे। नीचे Python में रेगुलर एक्सप्रेशंस का उपयोग करने के कुछ उदाहरण दिए गए हैं:

import re
print all([                                 # नीचे दिए गए सभी कथन `True` लौटाते हैं, क्योंकि
    not re.match("a", "cat"),               # * 'cat' 'a' से शुरू नहीं होता है
    re.search("a", "cat"),                  # * 'cat' में 'a' अक्षर शामिल है
    not re.search("c", "dog"),              # * 'dog' में 'c' अक्षर शामिल नहीं है
    3 == len(re.split("[ab]", "carbs")),    # * 'a' या 'b' के आधार पर शब्द को तीन भागों में तोड़ना ['c','r','s']
    "R-D-" == re.sub("[0-9]", "-", "R2D2")  # * संख्याओं को डैश से बदलना
    ])                                      # आउटपुट True

ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (Object-Oriented Programming)

कई अन्य भाषाओं की तरह, Python आपको क्लास परिभाषित करने की अनुमति देता है जो डेटा को एन्कैप्सुलेट करते हैं और उन पर काम करने वाले फ़ंक्शन भी। हम कभी-कभी अपने कोड को अधिक स्पष्ट और संक्षिप्त बनाने के लिए इनका उपयोग करते हैं। इन्हें समझाने का सबसे आसान तरीका शायद एक अच्छी तरह से टिप्पणी किए गए उदाहरण के साथ होगा। मान लीजिए कि Python में कोई बिल्ट-इन सेट नहीं है, तो हम अपनी खुद की Set क्लास बनाना चाहेंगे। तो इस क्लास में कौन-कौन सी कार्यक्षमताएँ होनी चाहिए? उदाहरण के लिए, एक Set दिए जाने पर, हमें उसमें आइटम जोड़ने, उससे आइटम हटाने और यह जाँचने में सक्षम होना चाहिए कि क्या उसमें कोई खास मान शामिल है। इसलिए, हम इन सभी कार्यक्षमताओं को क्लास के मेंबर फ़ंक्शंस के रूप में बनाएंगे। इस तरह, हम Set ऑब्जेक्ट के बाद डॉट का उपयोग करके इन मेंबर फ़ंक्शंस तक पहुँच सकते हैं:

# परंपरा के अनुसार, हम क्लास के नामों को _PascalCase_ में देते हैं।
class Set:
    # ये मेंबर फ़ंक्शंस हैं
    # हर मेंबर फ़ंक्शन में पहला पैरामीटर "self" होता है (एक और परंपरा)
    # "self" उस विशिष्ट Set ऑब्जेक्ट से मेल खाता है जिसका उपयोग किया जा रहा है

    def __init__(self, values=None):
        """यह क्रिएशन फ़ंक्शन है
        जब भी आप एक नया Set बनाते हैं तो यह फ़ंक्शन कॉल होता है
        इसे इस तरह कॉल किया जा सकता है
        s1 = Set() # खाली सेट
        s2 = Set([1,2,2,3]) # निर्दिष्ट मानों के साथ सेट को इनिशियलाइज़ करें"""
        self.dict = {} # Set के प्रत्येक इंस्टेंस की अपनी dict एट्रिब्यूट होती है
        # हम इस एट्रिब्यूट का उपयोग हर मेंबर को ट्रैक करने के लिए करते हैं
        if values is not None:
            for value in values:
            self.add(value)

    def __repr__(self):
        """यह Set ऑब्जेक्ट का स्ट्रिंग रिप्रेजेंटेशन है
        आप Python कमांड प्रॉम्प्ट में स्ट्रिंग टाइप करके या str() मेथड का उपयोग करके ऑब्जेक्ट को पास करके इसे देख सकते हैं"""
        return "Set: " + str(self.dict.keys())

    # हम self.dict में एक की बनकर और की के मान को True सेट करके मेंबरशिप दर्शाते हैं
    def add(self, value):
        self.dict[value] = True

    # यदि पैरामीटर डिक्शनरी में एक की है, तो संबंधित मान Set में है
    def contains(self, value):
        return value in self.dict

    def remove(self, value):
        del self.dict[value]

फिर हम Set का उपयोग इस प्रकार कर सकते हैं:

s = Set([1,2,3])
s.add(4)
print s.contains(4)     # True
s.remove(3)
print s.contains(3)     # False

फ़ंक्शनल टूल्स (Functional Tools)

आंशिक फ़ंक्शन (partial)

फ़ंक्शन पास करते समय, कभी-कभी हम किसी फ़ंक्शन की आंशिक कार्यक्षमता का उपयोग करके एक नया फ़ंक्शन बनाना चाहते हैं। एक साधारण उदाहरण के लिए, मान लीजिए हमारे पास दो वेरिएबल वाला एक फ़ंक्शन है:

def exp(base, power):
    return base ** power

हम इसका उपयोग एक ऐसा फ़ंक्शन बनाने के लिए करना चाहते हैं जो एक वेरिएबल इनपुट लेता है और 2 के आधार पर घातीय फ़ंक्शन exp(2, power) का परिणाम आउटपुट करता है।

निश्चित रूप से, हम def का उपयोग करके एक नया फ़ंक्शन परिभाषित कर सकते हैं, हालाँकि यह बहुत समझदारी भरा नहीं लगता:

def two_to_the(power):
  return exp(2, power)

अधिक स्मार्ट तरीका functools.partial मेथड का उपयोग करना है:

from functools import partial
two_to_the = partial(exp, 2)      # इस फ़ंक्शन में अब केवल एक वेरिएबल है
print two_to_the(3)               # 8

यदि नाम निर्दिष्ट किया गया है, तो partial मेथड का उपयोग अन्य पैरामीटर्स को भरने के लिए भी किया जा सकता है:

square_of = partial(exp, power=2)
print square_of(3)                # 9

यदि आप फ़ंक्शन के बीच में पैरामीटर्स के साथ छेड़छाड़ करने की कोशिश करते हैं, तो प्रोग्राम जल्दी ही गड़बड़ हो जाएगा, इसलिए कृपया इस व्यवहार से बचें।

मैप (map)

हम कभी-कभी map, reduce, और filter जैसे फ़ंक्शंस का उपयोग लिस्ट कॉम्प्रिहेंशन्स के विकल्प के रूप में भी करते हैं:

def double(x):
    return 2 * x

xs = [1, 2, 3, 4]
twice_xs = [double(x) for x in xs]      # [2, 4, 6, 8]
twice_xs = map(double, xs)              # वही
list_doubler = partial(map, double)     # यह फ़ंक्शन लिस्ट को दोगुना करता है
twice_xs = list_doubler(xs)             # यह भी [2, 4, 6, 8] है

map मेथड का उपयोग मल्टी-पैरामीटर फ़ंक्शंस को मल्टी-लिस्ट में मैप करने के लिए भी किया जा सकता है:

def multiply(x, y): return x * y

products = map(multiply, [1, 2], [4, 5])  # [1 * 4, 2 * 5] = [4, 10]

फ़िल्टर (filter)

इसी तरह, filter लिस्ट कॉम्प्रिहेंशन्स में if की कार्यक्षमता को लागू करता है:

def is_even(x):
    """यदि x सम है तो True लौटाता है, यदि x विषम है तो False लौटाता है"""
    return x % 2 == 0

x_evens = [x for x in xs if is_even(x)]   # [2, 4]
x_evens = filter(is_even, xs)             # वही
list_evener = partial(filter, is_even)    # यह फ़ंक्शन फ़िल्टरिंग का कार्य करता है
x_evens = list_evener(xs)                 # यह भी [2, 4] है

रिड्यूस (reduce)

reduce मेथड लिस्ट के पहले और दूसरे एलिमेंट को लगातार मर्ज करता है, फिर परिणाम को तीसरे एलिमेंट के साथ मर्ज करता है, और यह प्रक्रिया तब तक दोहराता है जब तक कि एक अनूठा परिणाम न मिल जाए:

x_product = reduce(multiply, xs)          # = 1 * 2 * 3 * 4 = 24
list_product = partial(reduce, multiply)  # यह फ़ंक्शन एक लिस्ट को रिड्यूस करने का कार्य करता है
x_product = list_product(xs)              # यह भी 24 है

इनुमरेट (enumerate)

कभी-कभी ऐसा होता है कि लिस्ट में इटरेट करते समय एलिमेंट और उसके इंडेक्स दोनों का उपयोग करना पड़ता है:

# अधिक Pythonic नहीं (कम संक्षिप्त और सुरुचिपूर्ण)
for i in range(len(documents)):
    document = documents[i]
    do_something(i, document)

# यह भी अधिक Pythonic नहीं (कम संक्षिप्त और सुरुचिपूर्ण)
i = 0
for document in documents:
    do_something(i, document)
    i += 1

सबसे संक्षिप्त तरीका enumerate मेथड का उपयोग करके tuples (index, element) उत्पन्न करना है:

for i, document in enumerate(documents):
    do_something(i, document)

इसी तरह, यदि आप केवल इंडेक्स का उपयोग करना चाहते हैं:

for i in range(len(documents)): do_something(i)   # संक्षिप्त नहीं
for i, _ in enumerate(documents): do_something(i) # संक्षिप्त

हम बाद में इस मेथड का अक्सर उपयोग करेंगे।

ज़िप और आर्गुमेंट अनपैकिंग (zip and Argument Unpacking)

ज़िप (zip)

हम अक्सर दो या अधिक लिस्ट को ज़िप करते हैं। ज़िपिंग मूल रूप से कई लिस्ट को संबंधित टुपल्स की एक लिस्ट में बदलना है:

list1 = ['a', 'b', 'c']
list2 = [1, 2, 3]
zip(list1, list2)       # परिणाम [('a', 1), ('b', 2), ('c', 3)] है

आर्गुमेंट अनपैकिंग (Argument Unpacking)

यदि कई लिस्ट की लंबाई अलग-अलग है, तो ज़िपिंग प्रक्रिया सबसे छोटी लिस्ट के अंत में रुक जाएगी। आप लिस्ट को अनज़िप करने के लिए एक अजीब “अनज़िप” ट्रिक का भी उपयोग कर सकते हैं:

pairs = [('a', 1), ('b', 2), ('c', 3)]
letters, numbers = zip(*pairs)

यहाँ स्टार (*) पैरामीटर अनपैकिंग के लिए उपयोग किया जाता है, यह pairs के एलिमेंट्स को zip के अलग-अलग पैरामीटर के रूप में उपयोग करता है। नीचे दिया गया कॉल समान प्रभाव डालता है:

zip(('a', 1), ('b', 2), ('c', 3))  # [('a','b','c'), ('1','2','3')] लौटाता है

पैरामीटर अनपैकिंग का उपयोग अन्य फ़ंक्शंस के साथ भी किया जा सकता है:

def add(a, b): return a + b

add(1, 2)           # 3 लौटाता है
add([1, 2])         # त्रुटि
add(*[1, 2])        # 3 लौटाता है

यह बहुत व्यावहारिक नहीं है, लेकिन कोड को संक्षिप्त बनाने के लिए यह एक अच्छी ट्रिक है।

###不定 लंबाई वाले पैरामीटर पास करना (args और kwargs)

मान लीजिए हम एक हाई-ऑर्डर फ़ंक्शन बनाना चाहते हैं जो एक पुराना फ़ंक्शन लेता है और एक नया फ़ंक्शन लौटाता है, जहाँ नया फ़ंक्शन पुराने फ़ंक्शन का दोगुना होता है:

def doubler(f):
    def g(x):
      return 2 * f(x)
    return g

उदाहरण चलाएँ:

def f1(x):
    return x + 1

g = doubler(f1)
print g(3)        # 8 (== ( 3 + 1) * 2)
print g(-1)       # 0 (== (-1 + 1) * 2)

हालांकि, यदि एक से अधिक पैरामीटर पास किए जाते हैं, तो यह तरीका ठीक से काम नहीं करता है:

def f2(x, y):
    return x + y

g = doubler(f2)
print g(1, 2) # त्रुटि TypeError: g() takes exactly 1 argument (2 given)

इसलिए हमें एक ऐसा फ़ंक्शन निर्दिष्ट करने की आवश्यकता है जो किसी भी संख्या में पैरामीटर स्वीकार कर सके, और फिर पैरामीटर अनपैकिंग का उपयोग करके कई पैरामीटर पास कर सके, जो थोड़ा जादुई लग सकता है:

def magic(*args, **kwargs):
    print "unnamed args:", args
    print "keyword args:", kwargs
magic(1, 2, key="word", key2="word2")
# आउटपुट परिणाम:
# unnamed args: (1, 2)
# keyword args: {'key2': 'word2', 'key': 'word'}

जब हम इस तरह एक फ़ंक्शन को परिभाषित करते हैं, तो args (arguments का संक्षिप्त रूप) अनाम पैरामीटर वाला एक टुपल होता है, और kwargs (keyword arguments का संक्षिप्त रूप) नामित पैरामीटर वाला एक डिक्शनरी होता है।

उनका उपयोग तब भी किया जा सकता है जब पास किए गए पैरामीटर लिस्ट (या टुपल) या ऐरे हों:

def other_way_magic(x, y, z):
    return x + y + z

x_y_list = [1, 2]
z_dict = { "z" : 3 }
print other_way_magic(*x_y_list, **z_dict)    # 6

आप इसका उपयोग विभिन्न अजीब तरीकों से कर सकते हैं, लेकिन हम इसका उपयोग केवल हाई-ऑर्डर फ़ंक्शंस में अनिश्चित संख्या में पैरामीटर पास करने की समस्या को हल करने के लिए करेंगे:

def doubler_correct(f):
    """यह f चाहे कुछ भी हो, ठीक से काम करता है"""
    def g(*args, **kwargs):
        """चाहे कितने भी पैरामीटर हों, यह फ़ंक्शन f को सही ढंग से पैरामीटर पास कर सकता है"""
        return 2 * f(*args, **kwargs)
    return g

g = doubler_correct(f2)
print g(1, 2) # 6

डेटा साइंस की दुनिया में आपका स्वागत है!

डिंग! बधाई हो, आपने एक नई दुनिया के द्वार खोल दिए हैं! अब आप खुशी से इसका आनंद ले सकते हैं~

संबंधित लेख:

डेटा साइंस में Python का सामान्य सिंटैक्स (बेसिक)