আপনি কি ক্লোজারগুলি ব্যাখ্যা করতে পারেন (যেমন তারা পাইথনের সাথে সম্পর্কিত)?


85

ক্লোজার সম্পর্কে আমি অনেক কিছু পড়ছি এবং আমি মনে করি আমি সেগুলি বুঝতে পেরেছি, তবে নিজের এবং অন্যদের জন্য ছবিটি ক্লাউড না করে, আমি আশা করছি যে কেউ সমাধিগুলি যথাসম্ভব এবং পরিষ্কারভাবে ব্যাখ্যা করতে পারে। আমি একটি সহজ ব্যাখ্যা খুঁজছি যা আমাকে বুঝতে সাহায্য করতে পারে যে আমি কোথায় এবং কেন সেগুলি ব্যবহার করতে চাই।

উত্তর:


97

বন্ধের উপর বন্ধ

অবজেক্টগুলি হ'ল সংযুক্ত পদ্ধতিযুক্ত ডেটা, ক্লোজারগুলি সংযুক্ত ডেটা সহ ফাংশন।

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 ছিল না পুরো উপর, read-write বন্ধ (অর্থাত আপনি ভেরিয়েবল ওপরে মুখ বন্ধ পড়তে পারি, কিন্তু তাদের মান বদলাতে নয়)
জেমস পোর্টার

6
@ জেমসপোর্টার: দ্রষ্টব্য: আপনি nonlocalপাইথন 2-তে কোনও পরিবর্তনীয় বস্তুর উদাহরণ দিয়ে উদাহরণস্বরূপ কীওয়ার্ড অনুকরণ করতে পারেন , L = [0] \n def counter(): L[0] += 1; return L[0]উদাহরণস্বরূপ, আপনি এই ক্ষেত্রে নাম পরিবর্তন করতে পারবেন না (এটি অন্য কোনও বস্তুর সাথে আবদ্ধ করুন) তবে আপনি নিজেই পরিবর্তনযোগ্য বস্তুটি পরিবর্তন করতে পারবেন যে নামটি বোঝায় প্রতি. পাইথনটিতে পূর্ণসংখ্যাগুলি পরিবর্তনযোগ্য না হওয়ায় তালিকাটি প্রয়োজনীয়।
jfs

4
@ জেফএসবেস্তিয়ান: ঠিক আছে। এটি সর্বদা নোংরা, নোংরা হ্যাকের মতো মনে হয় :)
জেমস পোর্টার

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

মনে রাখবেন যে 12 এবং 4 যথাক্রমে চ এবং জি এর ভিতরে "অদৃশ্য" হয়ে গেছে, এই বৈশিষ্ট্যটি চ এবং জি যথাযথ বন্ধ করে দেয়।


করার দরকার নেই constant = x; আপনি কেবল return y + xনেস্টেড ফাংশনে (বা নামের সাথে যুক্তিটি গ্রহণ করতে পারেন constant) করতে পারেন, এবং এটি ঠিক কাজ করবে; যুক্তিগুলি অ-তর্ক স্থানীয়দের চেয়ে আলাদাভাবে বন্ধের দ্বারা ধরা পড়ে।
শ্যাডোর্যাঞ্জার

15

আমি এই মোটামুটি, সংক্ষিপ্ত সংজ্ঞা পছন্দ :

এমন একটি ফাংশন যা পরিবেশের বিষয়ে উল্লেখ করতে পারে যা আর সক্রিয় নয়।

আমি যুক্ত করব

একজন অবসান আপনি যদি একটি ফাংশন মধ্যে ভেরিয়েবল সহিত আবদ্ধ করতে পারবেন তাদের প্যারামিটার হিসেবে পাস ছাড়া

পরামিতিগুলি গ্রহণ করে এমন সজ্জিতগুলি ক্লোজারগুলির জন্য একটি সাধারণ ব্যবহার। বন্ধকরণগুলি এই ধরণের "ফাংশন কারখানার" জন্য একটি সাধারণ বাস্তবায়ন প্রক্রিয়া। যখন কৌশলটি রান-টাইমে ডেটা দ্বারা সংশোধন করা হয় তখন আমি প্রায়শই কৌশল প্যাটার্নে ক্লোজারগুলি ব্যবহার করতে পছন্দ করি ।

এমন একটি ভাষায় যা বেনামে ব্লক সংজ্ঞা দেয় - উদাহরণস্বরূপ, রুবি, সি # - ক্লোজারগুলি উপন্যাসের নতুন নিয়ন্ত্রণ কাঠামো বাস্তবায়নের জন্য (কী পরিমাণ) ব্যবহার করা যেতে পারে। পাইথন বন্ধের সীমাবদ্ধতার মধ্যে বেনামে ব্লকের অভাব রয়েছে ।


15

সত্যি কথা বলতে, আমি ক্লোজারগুলি পুরোপুরি ভালভাবে বুঝতে পারি ব্যতীত আমি কখনই পরিষ্কার করি না যে জিনিসটি "ক্লোজার" এবং এটি সম্পর্কে এতটা "বন্ধ" কী what আমি আপনাকে পদটি পছন্দ করার পিছনে কোনও যুক্তি সন্ধান করার পরামর্শ দেওয়া উচিত।

যাইহোক, এখানে আমার ব্যাখ্যা:

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

bar = foo()
bar()   # print 5

এখানে একটি মূল ধারণাটি হ'ল foo থেকে ফাংশন অবজেক্ট স্থানীয় বর্ণ 'x' তে একটি হুক ধরে রেখেছে যদিও 'x' সুযোগের বাইরে চলে গেছে এবং তার অবনমিত হওয়া উচিত। এই হুকটি ভারে নিজেই হয়, কেবল তখন ভারের যে মান ছিল তা নয়, তাই যখন বারটি বলা হয় তখন এটি 3 টি নয়, 5 টি প্রিন্ট করে।

এও স্পষ্ট যে পাইথন ২.x সীমিত বন্ধ রয়েছে: আমি 'বার' এর ভিতরে 'এক্স' সংশোধন করার উপায় নেই কারণ 'x = বিএল' লেখায় একটি স্থানীয় 'এক্স' বারে প্রকাশিত হবে, ফু'র 'এক্স' নির্ধারণ করা হবে না । এটি পাইথনের অ্যাসাইনমেন্ট = ঘোষণার একটি পার্শ্ব-প্রতিক্রিয়া। এটি পেতে, পাইথন 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 = 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

ক্লোজারগুলির জন্য এখানে সাধারণ ব্যবহারের কেস রয়েছে - জিইউআই উপাদানগুলির জন্য কলব্যাক (এটি বোতাম শ্রেণীর উপশ্রেণীর বিকল্প হবে)। উদাহরণস্বরূপ, আপনি একটি ফাংশন তৈরি করতে পারেন যা একটি বোতাম টিপে জবাব দেওয়া হবে এবং ক্লিক প্রক্রিয়াজাতকরণের জন্য প্রয়োজনীয় প্যারেন্ট স্কোপে প্রাসঙ্গিক ভেরিয়েবলগুলির উপর "বন্ধ" করুন। এইভাবে আপনি একই সূচনা কার্য থেকে বেশ জটিল ইন্টারফেসগুলি ওয়্যার আপ করতে পারেন, বন্ধের মধ্যে সমস্ত নির্ভরতা তৈরি করে।


2

পাইথনে, একটি ক্লোজার হল এমন কোনও ফাংশনের উদাহরণ যা এর সাথে স্থায়ীভাবে আবদ্ধ ভেরিয়েবলগুলি রয়েছে।

আসলে, ডেটা মডেল এটি ফাংশনগুলির __closure__বৈশিষ্ট্যটির বর্ণনাতে এটি ব্যাখ্যা করে :

ফাংশনটির বিনামূল্যে ভেরিয়েবলের জন্য বাইন্ডিংগুলি ধারণ করে এমন কোনও বা কক্ষের একটি টিপল নেই । শুধুমাত্র পাঠযোগ্য

এটি প্রদর্শন করতে:

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

closure_instance = enclosure('foo')

স্পষ্টতই, আমরা জানি যে আমাদের এখন ভেরিয়েবলের নাম থেকে একটি ফাংশন নির্দেশিত closure_instance। স্পষ্টতই, আমরা যদি এটি একটি বস্তুর সাথে কল করি bar, এটি স্ট্রিংটি মুদ্রণ করা উচিত 'foo'এবং স্ট্রিংয়ের উপস্থাপনা যা কিছু হোক bar

বস্তুত, STRING 'foo বিন্যাস' হয় ফাংশন উদাহরণস্বরূপ আবদ্ধ, এবং আমরা সরাসরি তা এখানে পড়তে পারেন অ্যাক্সেস করার মাধ্যমে, cell_contentsপ্রথম (এবং একমাত্র) এর tuple মধ্যে কক্ষের অ্যাট্রিবিউট __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

এখানে, মোড়ক ফাংশন ফানক অবজেক্টে অ্যাক্সেস করতে সক্ষম হয়েছে কারণ মোড়ক "লেক্সিকাল ক্লোজার", এটি এর মূল বৈশিষ্ট্যগুলিতে অ্যাক্সেস করতে পারে। এজন্য, এটি ফানক অবজেক্টে অ্যাক্সেস করতে সক্ষম।


0

আমি আমার উদাহরণ এবং বন্ধ সম্পর্কে একটি ব্যাখ্যা ভাগ করতে চাই। আমি একটি অজগর উদাহরণ তৈরি করেছি এবং স্ট্যাকের রাজ্যগুলি প্রদর্শনের জন্য দুটি চিত্র figures

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

আমি কখনও বন্ধ হওয়ার সবচেয়ে ভাল ব্যাখ্যাটি ছিল মেকানিজমটি ব্যাখ্যা করা। এটি এরকম কিছু হয়েছিল:

আপনার প্রোগ্রাম স্ট্যাকটিকে একটি হ্রাসপ্রাপ্ত গাছ হিসাবে কল করুন যেখানে প্রতিটি নোডের একটি করে শিশু থাকে এবং একক পাতার নোডটি আপনার বর্তমানে সম্পাদন পদ্ধতির প্রসঙ্গ।

এখন প্রতিটি নোডে কেবল একটি শিশু থাকতে পারে এমন প্রতিবন্ধকতাটি শিথিল করুন।

আপনি যদি এটি করেন তবে আপনার কাছে এমন একটি কনস্ট্রাক্ট ('ফলন') থাকতে পারে যা স্থানীয় প্রসঙ্গটি অস্বীকার না করেই কোনও পদ্ধতি থেকে ফিরে আসতে পারে (যেমন আপনি ফিরে আসার পরে এটি স্ট্যাকটি পপ করে না)। পরবর্তী সময় প্রক্রিয়াটি শুরু করা হলে, অনুরোধটি পুরানো স্ট্যাক (গাছ) ফ্রেমটি ধরে এবং যেখানে ছেড়ে যায় সেখানে কার্যকর করা চালিয়ে যায়।


এটি ক্লোজারগুলির ব্যাখ্যা নয়।
জুলাই

আপনি ধারাবাহিকতা বর্ণনা করছেন, বন্ধ নয়।
ম্যাথু ওলানিক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.