पायथन: फंक्शनलसेलेशियल क्यों आवश्यक है?


193

आंशिक आवेदन अच्छा है। क्या कार्यक्षमता functools.partialप्रदान करता है कि आप lambdas के माध्यम से प्राप्त नहीं कर सकते हैं?

>>> sum = lambda x, y : x + y
>>> sum(1, 2)
3
>>> incr = lambda y : sum(1, y)
>>> incr(2)
3
>>> def sum2(x, y):
    return x + y

>>> incr2 = functools.partial(sum2, 1)
>>> incr2(4)
5

है functoolsकिसी भी तरह अधिक कुशल, या पढ़ने योग्य?

जवाबों:


266

क्या कार्यक्षमता functools.partialप्रदान करता है कि आप lambdas के माध्यम से प्राप्त नहीं कर सकते हैं?

अतिरिक्त कार्यक्षमता के मामले में बहुत अधिक नहीं है (लेकिन, बाद में देखें) - और, पठनीयता देखने वाले की नजर में है।
अधिकांश लोग जो कार्यात्मक प्रोग्रामिंग भाषाओं से परिचित हैं (जो विशेष रूप से लिस्प / स्कीम के परिवार वाले हैं) lambdaठीक-ठाक पसंद करते हैं - मैं कहता हूं "सबसे", निश्चित रूप से सभी नहीं , क्योंकि गुइडो और मैं आश्वस्त रूप से उन "परिचित" (आदि) में से हैं। ) अभी तक lambdaपायथन में एक आंखों की बीमारी के रूप में सोचें ...
वह इसे पाइथन में स्वीकार करने के लिए पश्चाताप कर रहा था, जबकि इसे पायथन 3 से निकालने की योजना बनाई गई थी, "पायथन ग्लिट्स" में से एक के रूप में।
मैंने उसमें उनका पूरा समर्थन किया। (मैं lambda योजना में प्यार करता हूं ... जबकि अजगर में इसकी सीमाएं हैं , और अजीब तरह से यह सिर्फ doesn ' बाकी भाषा के साथ, मेरी त्वचा को क्रॉल करें)।

हालांकि ऐसा नहीं है, हालांकि, lambdaप्रेमियों की भीड़ के लिए - जिन्होंने पायथन के इतिहास में देखे गए विद्रोह के लिए सबसे करीबी चीजों में से एक का मंचन किया, जब तक कि गुइडो पीछे नहीं हटे और छोड़ने का फैसला lambdaकिया।
कई functoolsकार्यों को निरंतर बनाने के लिए, पहचान बनाने के लिए। आदि) नहीं हुआ (स्पष्ट रूप से अधिक lambdaकार्यक्षमता को दोहराए जाने से बचने के लिए ), हालांकि partialनिश्चित रूप से बना रहा (यह कोई कुल दोहराव नहीं है, न ही यह एक नजर है)।

याद रखें कि lambdaशरीर एक अभिव्यक्ति तक ही सीमित है , इसलिए इसे सीमाएँ मिली हैं। उदाहरण के लिए...:

>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>> 

functools.partialलौटाया गया फ़ंक्शन आत्मनिरीक्षण के लिए उपयोगी विशेषताओं के साथ सजाया गया है - वह फ़ंक्शन जो इसे लपेट रहा है, और किस स्थिति और नामित तर्क इसे ठीक करता है। इसके अलावा, नामित तर्कों को सही तरीके से ओवरराइड किया जा सकता है ("फिक्सिंग" बल्कि, एक अर्थ में, दोषों की सेटिंग है):

>>> f('23', base=10)
23

इसलिए, जैसा कि आप देखते हैं, यह निश्चित रूप से उतना सरल नहीं है lambda s: int(s, base=2)! -)

हां, आप अपने लैम्ब्डा का विरोध कर सकते हैं ताकि आपको कुछ इस तरह दिया जा सके - जैसे, कीवर्ड-ओवरराइडिंग के लिए,

>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))

लेकिन मुझे सबसे ज्यादा उम्मीद है कि सबसे उत्साही lambdaव्यक्ति भी इस डरावनी partialकॉल की तुलना में अधिक पठनीय नहीं मानते हैं! -)। "विशेषता सेटिंग" भाग और भी कठिन है, क्योंकि "शरीर की एक एकल अभिव्यक्ति" पायथन की सीमा है lambda(प्लस तथ्य यह है कि असाइनमेंट कभी भी पायथन अभिव्यक्ति का हिस्सा नहीं हो सकता है) ... आप एक अभिव्यक्ति के भीतर "फ़ेकिंग असाइनमेंट" समाप्त करते हैं स्ट्रेचिंग लिस्ट को अच्छी तरह से समझ कर उसकी डिज़ाइन सीमा से ...:

>>> f = [f for f in (lambda f: int(s, base=2),)
           if setattr(f, 'keywords', {'base': 2}) is None][0]

अब नामित-तर्कों को एक साथ अभिव्यक्ति में तीन विशेषताओं की स्थापना के साथ-साथ, और मुझे बताएं कि यह कितना पठनीय है ...!


2
हाँ, मैं कहूँगा functools.partialकि आपके द्वारा बताई गई अतिरिक्त कार्यक्षमता इसे लंबोदर से बेहतर बनाती है। शायद यह एक और पोस्ट का विषय है, लेकिन यह एक डिज़ाइन स्तर पर क्या है जो आपको इतना परेशान करता है lambda?
निक हेइनर

11
@Rosarch, जैसा कि मैंने कहा: पहले, यह सीमाएँ (पायथन अभिव्यक्ति और बयानों को तीव्र रूप से अलग करती हैं - ऐसा बहुत कुछ है जो आप नहीं कर सकते हैं, या समझदारी से नहीं कर सकते हैं , एक ही अभिव्यक्ति के भीतर, और यही एक लंबोदर का शरीर है ); दूसरा, इसकी बिल्कुल अजीब सिंटेक्स चीनी। अगर मैं समय पर वापस जा सकता था और पायथन के भीतर एक चीज को बदल सकता था, तो यह बेतुका, अर्थहीन, आंखें defऔर lambdaकीवर्ड होगा: उन दोनों को बनाओ function(एक नाम पसंद जावास्क्रिप्ट वास्तव में सही हो गया), और मेरी आपत्तियों में से कम से कम 1/3 गायब हो जाएंगे -)। जैसा कि मैंने कहा, मुझे लिस्प में लैम्ब्डा से कोई आपत्ति नहीं है ...! -)
एलेक्स मार्टेली

1
@ अलेक्स मार्टेली, गुइडो ने मेमने के लिए इस तरह की सीमा क्यों निर्धारित की: "शरीर की एकल अभिव्यक्ति"? एक समारोह के शरीर में सी # लैम्ब्डा शरीर कुछ भी मान्य हो सकता है। गुइडो सिर्फ अजगर अजगर के लिए सीमा क्यों नहीं हटाते?
पीटर लॉन्ग

3
@PeterLong उम्मीद है कि गुइडो आपके सवाल का जवाब दे सकते हैं। इसका सार यह है कि यह बहुत जटिल होगा, और आप किसी defभी तरह का उपयोग कर सकते हैं । हमारे हितैषी नेता बोले हैं!
new123456

5
@AlexMartelli DropBox का गुइडो पर एक दिलचस्प प्रभाव पड़ा है - twitter.com/gvanrossum/status/391769557758521345
David

83

खैर, यहाँ एक उदाहरण है जो एक अंतर दिखाता है:

In [132]: sum = lambda x, y: x + y

In [133]: n = 5

In [134]: incr = lambda y: sum(n, y)

In [135]: incr2 = partial(sum, n)

In [136]: print incr(3), incr2(3)
8 8

In [137]: n = 9

In [138]: print incr(3), incr2(3)
12 8

इवान मूर की ये पोस्ट "लंबोदा की सीमाओं" पर विस्तार करती है और अजगर में बंद हो जाती है:


1
अच्छा उदाहरण। मेरे लिए, यह लंबोदर के साथ वास्तव में अधिक "बग" लगता है, लेकिन मैं समझता हूं कि अन्य लोग असहमत हो सकते हैं। (ऐसा ही कुछ लूप के भीतर परिभाषित क्लोजर के साथ भी होता है, जैसा कि कई प्रोग्रामिंग भाषाओं में लागू किया गया है।)
श्रीवत्सआर

28
इस "जल्दी बनाम देर से बाध्यकारी दुविधा" का समाधान स्पष्ट रूप से प्रारंभिक बंधन का उपयोग करना है, जब आप चाहते हैं कि, द्वारा lambda y, n=n: ...। लेट बाइंडिंग ( केवल किसी फ़ंक्शन के शरीर में दिखने वाले नामों में, उसके defया समतुल्य में नहीं lambda) कुछ भी नहीं है, लेकिन एक बग है, जैसा कि मैंने अतीत में लंबे एसओ के उत्तर में लंबाई में दिखाया है: आप जल्दी से स्पष्ट रूप से बाँधते हैं जब आप चाहते हैं, देर से बाध्यकारी डिफ़ॉल्ट का उपयोग करते हैं कि आप क्या चाहते है, और है कि वास्तव में सही डिजाइन विकल्प अजगर के डिजाइन के बाकी के संदर्भ दिया।
एलेक्स मार्टेली

1
@ एलेक्स मार्टेली: हाँ, क्षमा करें। मैं बस देर से बाध्यकारी ठीक से उपयोग करने में विफल हो जाता हूं, शायद इसलिए क्योंकि मुझे लगता है कि जब कार्यों को परिभाषित करते हुए कि मैं वास्तव में अच्छे के लिए कुछ परिभाषित कर रहा हूं, और अप्रत्याशित आश्चर्य केवल मुझे सिरदर्द का कारण बनता है। (अधिक जब मैं पायथन की तुलना में जावास्क्रिप्ट में कार्यात्मक चीजें करने की कोशिश करता हूं, हालांकि।) मैं समझता हूं कि बहुत से लोग देर से बाध्यकारी के साथ सहज हैं , और यह पायथन के बाकी डिजाइन के अनुरूप है। मैं फिर भी आपके अन्य लंबे एसओ उत्तर पढ़ना चाहूंगा, हालांकि - लिंक? :-)
श्रीवत्सआर

3
एलेक्स सही है, यह बग नहीं है। लेकिन यह एक "गेटचा" है जो कई लंबो उत्साही लोगों को फंसाता है। : एक haskel / कार्यात्मक प्रकार से तर्क के "बग" पक्ष के लिए, एंड्रेज बॉयर की पोस्ट देखने के math.andrej.com/2009/04/09/pythons-lambda-is-broken
आर्स

@ars: आह हाँ, यह लेडी बाउर के पोस्ट के लिंक के लिए धन्यवाद। हाँ, देर से बाध्यकारी के प्रभाव निश्चित रूप से कुछ हैं जो हम गणित-प्रकार (बदतर, एक हास्केल पृष्ठभूमि के साथ) को सकल अप्रत्याशित और चौंकाने वाला पाते हैं। :-) मुझे यकीन है कि मैं जहाँ तक प्रो Bauer के रूप में जाने के लिए और यह एक डिजाइन त्रुटि फोन चाहते हैं नहीं कर रहा हूँ, लेकिन यह है पूरी तरह से सोच का एक ही रास्ता है और एक अन्य के बीच स्विच करने के लिए मानव प्रोग्रामर के लिए मुश्किल। (या शायद यह सिर्फ मेरे अपर्याप्त पायथन अनुभव है।)
श्रीवत्सआर

26

अजगर के नवीनतम संस्करण (> = 2.7) में, आप कर सकते हैं pickleएक partialनहीं, बल्कि एक lambda:

>>> pickle.dumps(partial(int))
'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.'
>>> pickle.dumps(lambda x: int(x))
Traceback (most recent call last):
  File "<ipython-input-11-e32d5a050739>", line 1, in <module>
    pickle.dumps(lambda x: int(x))
  File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.7/pickle.py", line 748, in save_global
    (obj, module, name))
PicklingError: Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>

1
दुर्भाग्य से आंशिक कार्य के लिए अचार करने में विफल multiprocessing.Pool.map()stackoverflow.com/a/3637905/195139
wting

3
@ यह पोस्ट 2010 से है। partialपाइथन 2.7 में लाइक किया जा सकता है।
फ्रेड फू

22

क्या फंक्शनलबल्स किसी तरह अधिक कुशल हैं ..?

इसके आंशिक उत्तर के रूप में मैंने प्रदर्शन का परीक्षण करने का निर्णय लिया। यहाँ मेरा उदाहरण है:

from functools import partial
import time, math

def make_lambda():
    x = 1.3
    return lambda: math.sin(x)

def make_partial():
    x = 1.3
    return partial(math.sin, x)

Iter = 10**7

start = time.clock()
for i in range(0, Iter):
    l = make_lambda()
stop = time.clock()
print('lambda creation time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    l()
stop = time.clock()
print('lambda execution time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    p = make_partial()
stop = time.clock()
print('partial creation time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    p()
stop = time.clock()
print('partial execution time {}'.format(stop - start))

पायथन 3.3 पर यह देता है:

lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114

जिसका मतलब है कि आंशिक रूप से निर्माण के लिए थोड़ा और समय चाहिए लेकिन निष्पादन के लिए काफी कम समय। यह प्रारंभिक और देर से बाध्यकारी के प्रभाव को अच्छी तरह से प्रभावित कर सकता है जो आरएस से जवाब में चर्चा की जाती है ।


3
अधिक महत्वपूर्ण रूप से, partialशुद्ध पायथन के बजाय C में लिखा गया है, जिसका अर्थ है कि यह केवल एक फ़ंक्शन बनाने वाले फ़ंक्शन की तुलना में अधिक कुशल कॉल करने योग्य उत्पादन कर सकता है।
जुआन

12

एलेक्स द्वारा बताई गई अतिरिक्त कार्यक्षमता के अलावा, फंक्शनलकूल का एक और फायदा गति है। आंशिक के साथ आप एक और स्टैक फ्रेम के निर्माण (और विनाशकारी) से बच सकते हैं।

न तो आंशिक और न ही लैम्ब्डा द्वारा उत्पन्न फ़ंक्शन में डिफ़ॉल्ट रूप से डॉकस्ट्रिंग्स हैं (हालांकि आप किसी भी ऑब्जेक्ट के लिए डॉक्टर स्ट्रिंग सेट कर सकते हैं __doc__)।

आप इस ब्लॉग में अधिक जानकारी पा सकते हैं: पायथन में आंशिक समारोह अनुप्रयोग


यदि आपने गति लाभ का परीक्षण किया है, तो लैंबडा पर आंशिक रूप से किस गति में सुधार की उम्मीद की जा सकती है?
ट्रिलियन

1
जब आप कहते हैं कि डॉकस्ट्रिंग विरासत में मिला है, तो आप किस पायथन संस्करण का उल्लेख करते हैं? पायथन 2.7.15 और पायथन 3.7.2 में उन्हें विरासत में नहीं मिला है। यह एक अच्छी बात है, क्योंकि मूल रूप से मूल रूप से डॉकस्ट्रिंग आंशिक रूप से लागू तर्कों के साथ कार्य के लिए सही नहीं है।
जनवरी

Python 2.7 ( docs.python.org/2/library/functools.html#partial-objects ) के लिए: " नाम और डॉक्टर विशेषताएँ स्वचालित रूप से नहीं बनाई गई हैं"। उसी के लिए 3. [5-7]।
यारोस्लाव निकितेंको 10

आपके लिंक में एक गलती है: log_info = आंशिक (log_template, स्तर = "जानकारी") - यह संभव नहीं है क्योंकि स्तर उदाहरण में एक कीवर्ड तर्क नहीं है। अजगर 2 और 3 दोनों कहते हैं: "TypeError: log_template () को तर्क 'स्तर' के लिए कई मान मिले"।
यारोस्लाव निकितेंको

वास्तव में, मैंने हाथ से एक आंशिक (एफ) बनाया और यह डॉक्टर को 'आंशिक (दुर्गंध, * आर्ग, ** कीवर्ड) के रूप में देता है - दिए गए तर्कों और कीवर्ड के आंशिक अनुप्रयोग \ n के साथ नया फ़ंक्शन। \ n' (दोनों) अजगर 2 और 3 के लिए)।
यारोस्लाव निकितेंको

1

मैं तीसरे उदाहरण में इरादे को तेज समझता हूं।

जब मैं लंबोदर को पार्स करता हूं, तो मैं मानक पुस्तकालय द्वारा सीधे पेशकश की तुलना में अधिक जटिलता / विषमता की उम्मीद कर रहा हूं।

इसके अलावा, आप देखेंगे कि तीसरा उदाहरण केवल वही है जो पूर्ण हस्ताक्षर पर निर्भर नहीं करता है sum2; इस प्रकार यह थोड़ा और अधिक युग्मित बना रहा है।


1
हम्म, मैं वास्तव में विपरीत अनुनय की बात कर रहा हूं, मुझे functools.partialकॉल को पार्स करने में बहुत अधिक समय लगा , जबकि लंबोदर स्वयं स्पष्ट हैं।
डेविड जेड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.