क्या आप क्लोजर समझा सकते हैं (जैसा कि वे पायथन से संबंधित हैं)?


83

मैं क्लोज़र के बारे में बहुत कुछ पढ़ रहा हूं और मुझे लगता है कि मैं उन्हें समझ रहा हूं, लेकिन अपने और दूसरों के लिए तस्वीर को बिना बादल दिए, मैं उम्मीद कर रहा हूं कि कोई व्यक्ति स्पष्ट रूप से और स्पष्ट रूप से क्लोजर के रूप में समझा सकता है। मैं एक सरल स्पष्टीकरण की तलाश में हूं जो मुझे यह समझने में मदद कर सकता है कि मैं उनका उपयोग कहां और क्यों करना चाहता हूं।

जवाबों:


95

बंद होने पर बंद कर दिया

ऑब्जेक्ट्स संलग्न तरीकों के साथ डेटा हैं, क्लोजर संलग्न डेटा के साथ फ़ंक्शन हैं।

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

6
ध्यान दें कि nonlocalअजगर 3 में जोड़ा गया था, अजगर 2.x में पूर्ण-पर, पढ़ने-लिखने के बंद नहीं थे (अर्थात आप चर पर बंद पढ़ सकते हैं, लेकिन उनके मूल्यों को नहीं बदल सकते हैं)
जेम्स पोर्टर

6
@JamesPorter: ध्यान दें: आप nonlocalएक परिवर्तनशील वस्तु का उपयोग करके पायथन 2 में कीवर्ड का अनुकरण कर सकते हैं। उदाहरण के लिए, L = [0] \n def counter(): L[0] += 1; return L[0]आप इस मामले में नाम नहीं बदल सकते हैं (इसे किसी अन्य ऑब्जेक्ट पर बाँध सकते हैं) लेकिन आप परिवर्तन योग्य ऑब्जेक्ट को स्वयं बदल सकते हैं जो नाम संदर्भित करता है सेवा। सूची की आवश्यकता है क्योंकि पूर्णांक पायथन में अपरिवर्तनीय हैं।
jfs

1
@JFSebastian: सही है। हालांकि यह हमेशा एक गंदे, गंदे हैक की तरह लगता है :)
जेम्स पोर्टर

46

यह सरल है: एक फ़ंक्शन जो चर दायरे से संदर्भ लेता है, संभवतः प्रवाह-नियंत्रण के बाद उस दायरे को छोड़ दिया है। वह अंतिम बिट बहुत उपयोगी है:

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

ध्यान दें कि क्रमशः f और g के अंदर 12 और 4 "गायब" हो गए हैं, यह सुविधा है जो f और g को उचित बंद बनाती है।


करने की कोई जरूरत नहीं है constant = x; आप बस return y + xनेस्टेड फ़ंक्शन में कर सकते हैं (या नाम के साथ तर्क प्राप्त करते हैं constant), और यह ठीक काम करेगा; तर्कों को गैर-तर्क स्थानीय लोगों की तुलना में अलग से बंद कर दिया जाता है।
शैडो रेंजर

15

मुझे यह कठिन, संक्षिप्त परिभाषा पसंद है :

एक फ़ंक्शन जो उन वातावरणों को संदर्भित कर सकता है जो अब सक्रिय नहीं हैं।

मैं जोड़ूंगा

एक क्लोजर आपको चर को मापदंडों के रूप में पारित किए बिना एक फ़ंक्शन में बांधने की अनुमति देता है ।

डेकोरेटर्स जो मापदंडों को स्वीकार करते हैं वे क्लोजर के लिए एक सामान्य उपयोग हैं। क्लोजर "फ़ंक्शन फैक्टरी" के उस प्रकार के लिए एक सामान्य कार्यान्वयन तंत्र है। मैं अक्सर रणनीति पैटर्न में क्लोजर का उपयोग करना चुनता हूं, जब रणनीति को रन-टाइम पर डेटा द्वारा संशोधित किया जाता है।

एक ऐसी भाषा में जो अनाम ब्लॉक की परिभाषा की अनुमति देता है - जैसे, रूबी, सी # - क्लोजर का उपयोग उपन्यास नियंत्रण नियंत्रण संरचनाओं को लागू करने के लिए (कितनी मात्रा में) किया जा सकता है। अनाम ब्लॉक की कमी पायथन में बंद होने की सीमाओं के बीच है


15

सच कहूं तो, मैं समझता हूं कि मैं पूरी तरह से अच्छी तरह से बंद कर देता हूं, इसके अलावा मैं कभी भी स्पष्ट नहीं हूं कि वास्तव में वह चीज क्या है जो "बंद" है और इसके बारे में इतना "बंद" क्या है। मैं आपको शब्द की पसंद के पीछे किसी भी तर्क की तलाश करने की सलाह देता हूं।

वैसे भी, यहाँ मेरी व्याख्या है:

def foo():
   x = 3
   def bar():
      print x
   x = 5
   return bar

bar = foo()
bar()   # print 5

यहाँ एक महत्वपूर्ण विचार यह है कि foo से लौटाया गया फंक्शन ऑब्जेक्ट स्थानीय संस्करण 'x' के लिए एक हुक को बनाए रखता है, भले ही 'x' कार्यक्षेत्र से बाहर हो गया हो और डिफेक्ट होना चाहिए। यह हुक स्वयं ही var के लिए होता है, न कि उस समय जो वर्जन होता है, इसलिए जब बार कहा जाता है, तो यह 5 नहीं, 3 प्रिंट करता है।

यह भी स्पष्ट करें कि पायथन 2.x में सीमित क्लोजर है: 'बार' के अंदर 'x' को संशोधित करने का कोई तरीका नहीं है क्योंकि 'x = bla' लिखने से बार में एक स्थानीय 'x' घोषित होगा, foo के 'x' को असाइन न करें । यह पायथन के असाइनमेंट = घोषणा का एक साइड-इफेक्ट है। इसके चारों ओर जाने के लिए, Python 3.0 नॉनक्लॉक कीवर्ड का परिचय देता है:

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

7

मैंने कभी भी लेन-देन के बारे में एक ही संदर्भ में इस्तेमाल करने के बारे में नहीं सुना है, जो यह बताता है कि एक क्लोजर क्या है और यहां वास्तव में कोई लेनदेन शब्दार्थ नहीं है।

इसे क्लोजर कहा जाता है क्योंकि यह बाहर के वैरिएबल (स्थिर) पर "बंद हो जाता है" - यानी, यह केवल एक फ़ंक्शन नहीं है, बल्कि पर्यावरण का एक एनक्लोजर है जहां फ़ंक्शन बनाया गया था।

निम्नलिखित उदाहरण में, x बदलने के बाद क्लोजर g को कॉल करने से g के भीतर x का मान भी बदल जाएगा, क्योंकि g, x के ऊपर बंद हो जाता है:

x = 0

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


closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

इसके अलावा, जैसा कि यह खड़ा है, g()गणना करता है x * 2लेकिन कुछ भी वापस नहीं करता है। वह होना चाहिए return x * 2। +1 फिर भी "क्लोजर" शब्द के स्पष्टीकरण के लिए।
ब्रूनो ले फ्लोच

3

यहां क्लोजर के लिए एक सामान्य उपयोग का मामला है - GUI तत्वों के लिए कॉलबैक (यह बटन वर्ग को उप-वर्ग करने के लिए एक विकल्प होगा)। उदाहरण के लिए, आप एक बटन प्रेस के जवाब में एक फ़ंक्शन का निर्माण कर सकते हैं, और पैरेंट स्कोप में संबंधित वैरिएबल पर "क्लोज़" कर सकते हैं जो क्लिक को संसाधित करने के लिए आवश्यक हैं। इस तरह आप एक ही इनिशियलाइज़ेशन फंक्शन से काफी जटिल इंटरफेस को बंद कर सकते हैं, जिससे सभी निर्भरताएँ बंद हो जाती हैं।


2

पाइथन में, एक क्लोजर एक फ़ंक्शन का एक उदाहरण है जिसमें वैरिएबल हैं जो इसे अपरिवर्तनीय रूप से बाध्य करते हैं।

वास्तव में, डेटा मॉडल अपने कार्यों की __closure__विशेषता के विवरण में यह बताता है :

कोई भी या कोशिकाओं का एक समूह जिसमें फ़ंक्शन के मुक्त चर के लिए बाइंडिंग होती है। सिफ़ पढ़िये

इसे प्रदर्शित करने के लिए:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

स्पष्ट रूप से, हम जानते हैं कि अब हमारे पास चर नाम से इंगित एक फ़ंक्शन है closure_instance। मूल रूप से, अगर हम इसे किसी ऑब्जेक्ट के साथ कहते हैं, तो barइसे स्ट्रिंग को प्रिंट करना चाहिए, 'foo'और जो भी स्ट्रिंग का प्रतिनिधित्व barहै।

वास्तव में, स्ट्रिंग 'foo' है समारोह के कहने के लिए बाध्य है, और हम सीधे उसे यहां पढ़ सकें तक पहुंच कर, cell_contentsपहली (और केवल) की टपल में सेल की विशेषता __closure__विशेषता:

>>> closure_instance.__closure__[0].cell_contents
'foo'

एक तरफ के रूप में, सेल ऑब्जेक्ट्स को सी एपीआई प्रलेखन में वर्णित किया गया है:

"सेल" ऑब्जेक्ट का उपयोग कई स्कोप द्वारा संदर्भित चर को लागू करने के लिए किया जाता है

और हम अपने बंद करने के उपयोग को प्रदर्शित कर सकते हैं, यह देखते हुए कि 'foo'फ़ंक्शन में अटक गया है और नहीं बदलता है:

>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux

और कुछ भी इसे बदल नहीं सकता है:

>>> closure_instance.__closure__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute

आंशिक कार्य

दिए गए उदाहरण एक आंशिक फ़ंक्शन के रूप में क्लोजर का उपयोग करता है, लेकिन अगर यह हमारा एकमात्र लक्ष्य है, तो उसी लक्ष्य को पूरा किया जा सकता है functools.partial

>>> from __future__ import print_function # use this if you're in Python 2.
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux

अधिक जटिल क्लोजर भी हैं जो आंशिक फ़ंक्शन उदाहरण के लिए फिट नहीं होंगे, और मैं उन्हें समय की अनुमति देता हूं।


2
# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

क्लोजर से मिलने के लिए मानदंड हैं:

  1. हमारे पास नेस्टेड फंक्शन होना चाहिए।
  2. नेस्टेड फ़ंक्शन को एनक्लोजिंग फ़ंक्शन में परिभाषित मूल्य का उल्लेख करना चाहिए।
  3. एनक्लोजिंग फ़ंक्शन नेस्टेड फ़ंक्शन को वापस करना चाहिए।

# Example 2
def make_multiplier_of(n): # Outer function
    def multiplier(x): # Inner nested function
        return x * n
    return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
print(times5(3)) # 15
print(times3(2)) #  6

1

यहाँ पायथन 3 क्लोजर का एक उदाहरण है

def closure(x):
    def counter():
        nonlocal x
        x += 1
        return x
    return counter;

counter1 = closure(100);
counter2 = closure(200);

print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 1 " + str(counter1()))
print("i from closure 2 " + str(counter2()))

# result

i from closure 1 101
i from closure 1 102
i from closure 2 201
i from closure 1 103
i from closure 1 104
i from closure 1 105
i from closure 2 202

0

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

def makefunction (x)
  def multiply (a,b)
    puts a*b
  end
  return lambda {|n| multiply(n,x)} # => returning a closure
end

func = makefunction(2) # => we capture the closure
func.call(6)    # => Result equal "12"  

यह तब भी काम करता है जब दोनों, "गुणा" विधि और "x" चर, अब मौजूद नहीं हैं। सभी क्योंकि बंद करने की क्षमता याद है।


0

हम सभी ने अजगर में डेकोरेटर का इस्तेमाल किया है । वे यह दिखाने के लिए अच्छे उदाहरण हैं कि अजगर में क्लोजर फ़ंक्शन क्या हैं।

class Test():
    def decorator(func):
        def wrapper(*args):
            b = args[1] + 5
            return func(b)
        return wrapper

@decorator
def foo(val):
    print val + 2

obj = Test()
obj.foo(5)

यहाँ अंतिम मूल्य 12 है

यहां, आवरण फ़ंक्शन फंक ऑब्जेक्ट तक पहुंचने में सक्षम है क्योंकि आवरण "लेक्सिकल क्लोजर" है, यह अभिभावक विशेषताओं तक पहुंच सकता है। इसीलिए, यह func object को एक्‍सेस करने में सक्षम है।


0

मैं अपने उदाहरण और क्लोजर के बारे में स्पष्टीकरण साझा करना चाहूंगा। मैंने एक अजगर का उदाहरण दिया, और स्टैक राज्यों को प्रदर्शित करने के लिए दो आंकड़े।

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n’ * margin_top, a * n, 
            ' ‘ * padding, msg, ' ‘ * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

इस कोड का आउटपुट निम्नानुसार होगा:

*****      hello      #####

      good bye!    ♥♥♥

यहां स्टैक्स दिखाने के लिए दो आंकड़े हैं और फ़ंक्शन ऑब्जेक्ट से जुड़ा हुआ क्लोजर है।

जब फ़ंक्शन निर्माता से वापस आ जाता है

जब समारोह बाद में कहा जाता है

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


-2

सबसे अच्छा स्पष्टीकरण जो मैंने कभी देखा था, वह तंत्र की व्याख्या करना था। यह कुछ इस प्रकार रहा:

एक पतित वृक्ष के रूप में अपने प्रोग्राम स्टैक की कल्पना करें जहां प्रत्येक नोड में केवल एक बच्चा है और एकल पत्ती नोड आपके वर्तमान निष्पादन प्रक्रिया का संदर्भ है।

अब बाधा को शांत करें कि प्रत्येक नोड में केवल एक बच्चा हो सकता है।

यदि आप ऐसा करते हैं, तो आपके पास एक निर्माण ('उपज') हो सकता है जो स्थानीय संदर्भ को त्यागने के बिना एक प्रक्रिया से वापस आ सकता है (यानी जब आप वापस लौटते हैं तो यह स्टैक को पॉप नहीं करता है)। अगली बार जब इस प्रक्रिया को लागू किया जाता है, तो आह्वान पुराने स्टैक (पेड़) के फ्रेम को उठाता है और निष्पादित करना जारी रखता है जहां इसे छोड़ा गया था।


यह क्लोजर का स्पष्टीकरण नहीं है।
जूल्स

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