पायथन में फंक्शनिंग


90

पर Codewars.com मैं निम्नलिखित कार्य का सामना करना पड़ा:

एक ऐसा फंक्शन बनाएं जो addउत्तराधिकार में कॉल करने पर एक साथ नंबर जोड़ता है। इसलिए add(1)लौट जाना चाहिए 1, add(1)(2)लौट जाना चाहिए 1+2...

जबकि मैं पायथन की मूल बातों से परिचित हूं, मैंने कभी भी एक ऐसे फ़ंक्शन का सामना नहीं किया है जो इस तरह के उत्तराधिकार में बुलाया जा सकता है, यानी एक ऐसा फ़ंक्शन f(x)जिसे कहा जा सकता है f(x)(y)(z)...। इस प्रकार, मुझे यह भी पता नहीं है कि इस धारणा की व्याख्या कैसे की जाए।

एक गणितज्ञ के रूप में, मुझे संदेह है कि f(x)(y)यह एक ऐसा कार्य है जो प्रत्येक xकार्य के लिए असाइन होता है g_{x}और फिर रिटर्न g_{x}(y)और इसी तरह के लिए f(x)(y)(z)

क्या यह व्याख्या सही होनी चाहिए, पायथन मुझे गतिशील रूप से कार्य करने की अनुमति देगा जो मुझे बहुत दिलचस्प लगता है। मैंने पिछले एक घंटे के लिए वेब खोजा है, लेकिन सही दिशा में लीड नहीं खोज पाया। चूंकि मुझे नहीं पता कि इस प्रोग्रामिंग अवधारणा को कैसे कहा जाता है, हालांकि, यह बहुत आश्चर्यजनक नहीं हो सकता है।

आप इस अवधारणा को कैसे कहते हैं और मैं इसके बारे में और कहां पढ़ सकता हूं?


7
लगता है जैसे आप करीने के कार्यों की तलाश में हैं
OneCricketeer

2
संकेत: एक नेस्टेड फ़ंक्शन गतिशील रूप से बनाया गया है, इसके मूल फ़ंक्शन के स्थानीय लोगों तक पहुंच है, और इसे एक (कॉल करने योग्य) ऑब्जेक्ट के रूप में वापस करने में सक्षम है।
जोनाथन रेइनहार्ट

@JonathonReinhart इस तरह से मैं समस्या के बारे में सोच रहा था। लेकिन मैं वास्तव में यह नहीं देख पाया कि इसे कैसे लागू किया जाए।
स्टीफन मेस्केन

3
एक तरफ के रूप में: पायथन निश्चित रूप से आपको गतिशील रूप से फ़ंक्शन करने की अनुमति देगा। यदि आप रुचि रखते हैं, तो यहां संबंधित अवधारणाओं की एक जोड़ी पर पढ़ने के लिए है: WP: प्रथम श्रेणी के कार्य | आप पायथन में एक उच्च क्रम फ़ंक्शन कैसे बनाते हैं? | functools.partial()| WP: क्लोज़र
लुकास ग्राफ

@LukasGraf मैं इसे देखूंगा। धन्यवाद!
स्टीफन मेस्केन

जवाबों:


100

यह वह जगह है कि क्या मैं नहीं पता है समारोह जितना श्रृंखलन के रूप में यह है प्रतिदेय श्रृंखलन, लेकिन, के बाद से कार्यों हैं callables मैंने किया कोई बुराई नहीं है लगता है। किसी भी तरह से, ऐसा करने के बारे में दो तरीके हो सकते हैं:

उप-वर्गीकरण intऔर परिभाषित करना __call__:

पहला तरीका एक कस्टम intउपवर्ग के साथ होगा __call__जो परिभाषित करता है कि अद्यतन मूल्य के साथ खुद का एक नया उदाहरण देता है:

class CustomInt(int):
    def __call__(self, v):
        return CustomInt(self + v)

addअब एक CustomIntउदाहरण को वापस करने के लिए फ़ंक्शन को परिभाषित किया जा सकता है , जो एक कॉल करने योग्य के रूप में, जो स्वयं का एक अद्यतन मूल्य देता है, को उत्तराधिकार में कहा जा सकता है:

>>> def add(v):
...    return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44)  # and so on..
50

इसके अलावा, एक intउपवर्ग के रूप में , लौटाया गया मूल्य __repr__और एस के __str__व्यवहार को बनाए रखता है intहालांकि अधिक जटिल परिचालनों के लिए, आपको अन्य डंडर्स को उचित रूप से परिभाषित करना चाहिए

जैसा कि @Caridorc ने एक टिप्पणी में कहा है, addयह भी केवल इस प्रकार लिखा जा सकता है:

add = CustomInt 

addइसके बजाय कक्षा का नाम बदलना CustomIntभी इसी तरह काम करता है।


एक बंद को परिभाषित करें, उपज मान के लिए अतिरिक्त कॉल की आवश्यकता होती है:

केवल दूसरा तरीका जो मैं सोच सकता हूं कि इसमें एक नेस्टेड फ़ंक्शन शामिल है जो परिणाम को वापस करने के लिए अतिरिक्त खाली तर्क कॉल की आवश्यकता है। मैं उपयोग नहीं कर रहा हूँ nonlocalऔर पायथन के बीच इसे पोर्टेबल बनाने के लिए फ़ंक्शन ऑब्जेक्ट्स में विशेषताएँ संलग्न करने का विकल्प चुन रहा हूं :

def add(v):
    def _inner_adder(val=None):  
        """ 
        if val is None we return _inner_adder.v 
        else we increment and return ourselves
        """
        if val is None:    
            return _inner_adder.v
        _inner_adder.v += val
        return _inner_adder
    _inner_adder.v = v  # save value
    return _inner_adder 

यह लगातार स्वयं ( _inner_adder) वापस लौटता है , यदि valआपूर्ति की जाती है , तो इसे बढ़ाता है ( _inner_adder += val) और यदि नहीं, तो मान को वापस लौटाता है। जैसा कि मैंने उल्लेख किया है, ()वृद्धिशील मूल्य वापस करने के लिए इसे एक अतिरिक्त कॉल की आवश्यकता है :

>>> add(1)(2)()
3
>>> add(1)(2)(3)()  # and so on..
6

6
इंटरैक्टिव कोड में add = CostumIntभी काम करना चाहिए और सरल होना चाहिए।
कारिडोरक

4
अंतर्निहित उप-वर्ग के साथ समस्या यह है कि क्योंकि कॉल करने योग्य नहीं है, के (2*add(1)(2))(3)साथ विफल रहता है। मूल रूप से सादे में परिवर्तित किया जाता है जब किसी भी संदर्भ में इस्तेमाल किया जाता है, जब कॉलिंग को छोड़कर। एक और अधिक मजबूत समाधान के लिए आपको मूल रूप से संस्करणों सहित सभी तरीकों को फिर से लागू करना होगा ...TypeErrorintCustomIntint__*____r*__
बकुरी

@Caridorc या इसे परिभाषित नहीं करते हैं CustomInt, लेकिन addइसे परिभाषित करते समय।
22

27

आप मुझसे नफरत कर सकते हैं, लेकिन यहाँ एक लाइनर है :)

add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v)

संपादित करें: ठीक है, यह कैसे काम करता है? कोड @Jim के उत्तर के समान है, लेकिन सब कुछ एक पंक्ति में होता है।

  1. typeनए प्रकार के निर्माण के लिए इस्तेमाल किया जा सकता है type(name, bases, dict) -> a new type:। के लिए nameहम खाली स्ट्रिंग प्रदान करते हैं जैसा कि नाम से वास्तव में इस मामले में जरूरत नहीं है। के लिए bases(टपल) हम एक प्रदान करते हैं (int,), जो विरासत में के समान है intdictवर्ग गुण हैं, जहां हम लंबोदर को संलग्न करते हैं __call__
  2. self.__class__(self + v) के समान है return CustomInt(self + v)
  3. नए प्रकार का निर्माण और बाहरी मेमने के भीतर वापस आ गया है।

17
या उससे भी कम:class add(int):__call__ = lambda self, v: add(self+v)
बकुरी

3
एक वर्ग के अंदर कोड को सामान्य कोड की तरह निष्पादित किया जाता है ताकि आप असाइनमेंट द्वारा विशेष विधियों को परिभाषित कर सकें। फर्क सिर्फ इतना है कि वर्ग गुंजाइश थोड़ी है ... अजीब।
बकुरी

16

यदि आप किसी फ़ंक्शन को कई बार कॉल करने के लिए परिभाषित करना चाहते हैं, तो पहले आपको कॉल करने योग्य ऑब्जेक्ट को हर बार (उदाहरण के लिए फ़ंक्शन) को वापस करने की आवश्यकता होती है अन्यथा आपको कॉल करने योग्य होने के लिए __call__, एक विशेषता को परिभाषित करके अपनी खुद की ऑब्जेक्ट बनाना होगा।

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

यहाँ एक नमूना फ़ंक्शन है जो कॉरटुइन का उपयोग करता है, जो स्वयं की नवीनतम स्थिति को संरक्षित करता है। ध्यान दें कि इसे कई बार नहीं कहा जा सकता है क्योंकि रिटर्न वैल्यू वह है integerजो कॉल करने योग्य नहीं है, लेकिन आप इसे अपनी अपेक्षित वस्तु ;-) में बदलने के बारे में सोच सकते हैं।

def add():
    current = yield
    while True:
        value = yield current
        current = value + current


it = add()
next(it)
print(it.send(10))
print(it.send(2))
print(it.send(4))

10
12
16

6

यह करने के लिए पायथोनिक तरीका गतिशील तर्कों का उपयोग करना होगा:

def add(*args):
    return sum(args)

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


1
मैंने आपका ' पीएस ' नोट हटा दिया , निचेचर। हम सभी इस बात से वाकिफ हैं कि पायथन कितना सुंदर है। मुझे नहीं लगता कि यह उत्तर के शरीर में है।
दिमित्री फासरकिस हिलियार

6
मुझे लगता है कि आप बस कर सकते add = sumथे अगर उस रास्ते पर जाने वाले थे
codykochmann


3

यदि आप ()उस परिणाम को प्राप्त करने के लिए एक अतिरिक्त स्वीकार करने के लिए तैयार हैं जो आप उपयोग कर सकते हैं functools.partial:

from functools import partial

def add(*args, result=0):
    return partial(add, result=sum(args)+result) if args else result

उदाहरण के लिए:

>>> add(1)
functools.partial(<function add at 0x7ffbcf3ff430>, result=1)
>>> add(1)(2)
functools.partial(<function add at 0x7ffbcf3ff430>, result=3)
>>> add(1)(2)()
3

यह एक साथ कई नंबरों को निर्दिष्ट करने की अनुमति देता है:

>>> add(1, 2, 3)(4, 5)(6)()
21

यदि आप इसे एकल संख्या तक सीमित करना चाहते हैं, तो आप निम्नलिखित कार्य कर सकते हैं:

def add(x=None, *, result=0):
    return partial(add, result=x+result) if x is not None else result

यदि आप add(x)(y)(z)आसानी से परिणाम वापस करना चाहते हैं और आगे कॉल करने योग्य हैं तो उप-क्लासिंग intजाने का रास्ता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.