নেস্টেড ফাংশনগুলিতে স্থানীয় ভেরিয়েবল


105

ঠিক আছে, এটি সহ্য করুন, আমি জানি এটি মারাত্মকভাবে সংশ্লেষিত দেখাবে, তবে কী হচ্ছে তা বুঝতে দয়া করে আমাকে সহায়তা করুন।

from functools import partial

class Cage(object):
    def __init__(self, animal):
        self.animal = animal

def gotimes(do_the_petting):
    do_the_petting()

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        cage = Cage(animal)

        def pet_function():
            print "Mary pets the " + cage.animal + "."

        yield (animal, partial(gotimes, pet_function))

funs = list(get_petters())

for name, f in funs:
    print name + ":", 
    f()

দেয়:

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

সুতরাং মূলত, কেন আমি তিনটি পৃথক প্রাণী পাচ্ছি না? cageনেস্টেড ফাংশনটির স্থানীয় স্কোপে 'প্যাকেজড' না ? যদি তা না হয় তবে নেস্টেড ফাংশনটিতে কল কিভাবে স্থানীয় ভেরিয়েবলগুলি সন্ধান করে?

আমি জানি যে এই ধরণের সমস্যায় পড়ার অর্থ সাধারণত একজন 'ভুল করে চলেছে' তবে কী হয় তা আমি বুঝতে চাই।


1
চেষ্টা করুন for animal in ['cat', 'dog', 'cow']... আমি নিশ্চিত যে কেউ
জন ক্লিমেটস

উত্তর:


114

নেস্টেড ফাংশনটি যখন কার্যকর করা হয় তখন প্যারেন্ট স্কোপ থেকে ভেরিয়েবল সন্ধান করে, যখন সংজ্ঞায়িত হয় না।

ফাংশন বডিটি সংকলিত হয়, এবং 'ফ্রি' ভেরিয়েবলগুলি (অ্যাসাইনমেন্ট দ্বারা নিজেই ফাংশনে সংজ্ঞায়িত হয় না) যাচাই করা হয়, তারপরে প্রতিটি ঘরের রেফারেন্সের জন্য কোড ব্যবহার করে কোডটি ফাংশনে ক্লোজার সেল হিসাবে আবদ্ধ হয়। pet_functionএইভাবে হয়েছে এক বিনামূল্যে পরিবর্তনশীল ( cage) যা তারপর একটি অবসান সেল মাধ্যমে রেফারেন্সড হয়, সূচক 0 অবসান নিজেই স্থানীয় পরিবর্তনশীল স্থানটিকে cageমধ্যে get_pettersফাংশন।

যখন আপনি আসলে ফাংশন কল, যে অবসান তারপর মান তাকান ব্যবহার করা হয় cageপার্শ্ববর্তী সুযোগ বার আপনি যখন ফাংশন কল এ । এখানে সমস্যা আছে। আপনি যখন আপনার ফাংশনগুলিকে কল করবেন ততক্ষণে get_pettersফাংশনটি ইতিমধ্যে এর ফলাফলগুলির গণনা সম্পন্ন হয়েছে। cageযে সম্পাদনের সময় কিছু সময়ে স্থানীয় পরিবর্তনশীল প্রতিটি কার্যভার প্রদান করা হয়েছিল 'cow', 'dog'এবং 'cat'স্ট্রিং, কিন্তু ফাংশন শেষে cageগত মান রয়েছে 'cat'। সুতরাং, আপনি যখন প্রতিটি গতিশীলভাবে ফিরে আসা ফাংশন কল করেন, আপনি মানটি 'cat'মুদ্রিত পাবেন।

কাজের চারপাশ বন্ধ হওয়ার উপর নির্ভর না করা। পরিবর্তে আপনি একটি আংশিক ফাংশন ব্যবহার করতে পারেন , একটি নতুন ফাংশন স্কোপ তৈরি করতে পারেন বা কোনও কীওয়ার্ড প্যারামিটারের জন্য ভেরিয়েবলটিকে ডিফল্ট মান হিসাবে বাঁধতে পারেন ।

  • আংশিক ফাংশন উদাহরণ, ব্যবহার করে functools.partial():

    from functools import partial
    
    def pet_function(cage=None):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
    
  • একটি নতুন সুযোগের উদাহরণ তৈরি করা:

    def scoped_cage(cage=None):
        def pet_function():
            print "Mary pets the " + cage.animal + "."
        return pet_function
    
    yield (animal, partial(gotimes, scoped_cage(cage)))
    
  • মূলশব্দ প্যারামিটারের জন্য ভেরিয়েবলটিকে ডিফল্ট মান হিসাবে আবদ্ধ করা:

    def pet_function(cage=cage):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, pet_function))
    

লুপটিতে scoped_cageফাংশনটি সংজ্ঞায়িত করার দরকার নেই , সংকলন কেবল একবার হয়, লুপের প্রতিটি পুনরাবৃত্তির উপর নয়।


1
কাজের জন্য একটি স্ক্রিপ্টে আমি আজ 3 ঘন্টা এই প্রাচীরের উপরে মাথা বেঁধেছি। আপনার শেষ পয়েন্টটি অত্যন্ত গুরুত্বপূর্ণ এবং আমি কেন এই সমস্যার মুখোমুখি হলাম তার মূল কারণ। আমার পুরো কোড জুড়ে ক্লোজারগুলির সাথে আমার কলব্যাক রয়েছে, তবে লুপে একই কৌশলটি চেষ্টা করে যা আমাকে পেল।
ডায়েস্পেরেন্টো

12

আমার বোধগম্যতা হল খাঁচার জন্য প্যারেন্ট ফাংশন নেমস্পেসে সন্ধান করা হয় যখন ফলনযোগ্য পোষা_ফানশানটি আসলে বলা হয়, আগে নয়।

সুতরাং যখন আপনি করবেন

funs = list(get_petters())

আপনি 3 টি ফাংশন তৈরি করেন যা সর্বশেষে তৈরি করা খাঁচাটি খুঁজে পাবে।

আপনি যদি আপনার শেষ লুপটি প্রতিস্থাপন করেন:

for name, f in get_petters():
    print name + ":", 
    f()

আপনি আসলে পাবেন:

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

6

এটি নিম্নলিখিত থেকে উদ্ভূত হয়

for i in range(2): 
    pass

print(i)  # prints 1

পুনরাবৃত্তি করার পরে এর মানটি iচূড়ান্ত মান হিসাবে অলসভাবে সংরক্ষণ করা হয়।

জেনারেটর হিসাবে ফাংশনটি কাজ করবে (অর্থাত্ প্রতিটি মান মুদ্রণ করে), তবে কোনও তালিকায় রূপান্তর করার সময় এটি জেনারেটরের উপরে চলে যায় , অতএব সমস্ত বিড়ালকে cage( cage.animal) বিড়ালগুলিতে কল করে ।


0

আসুন প্রশ্নটি সহজ করুন। নির্ধারণ:

def get_petters():
    for animal in ['cow', 'dog', 'cat']:
        def pet_function():
            return "Mary pets the " + animal + "."

        yield (animal, pet_function)

তারপরে, ঠিক যেমন প্রশ্নের মতো আমরা পেয়েছি:

>>> for name, f in list(get_petters()):
...     print(name + ":", f())

cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.

তবে আমরা যদি list()প্রথমটি তৈরি করা এড়িয়ে চলি :

>>> for name, f in get_petters():
...     print(name + ":", f())

cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.

কি হচ্ছে? কেন এই সূক্ষ্ম পার্থক্য সম্পূর্ণভাবে আমাদের ফলাফল পরিবর্তন করে?


আমরা যদি দেখি list(get_petters()), পরিবর্তিত মেমরি ঠিকানাগুলি থেকে এটি পরিষ্কার যে আমরা প্রকৃতপক্ষে তিনটি পৃথক ফাংশন পাই:

>>> list(get_petters())

[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
 ('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
 ('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]

যাইহোক, cellএই ফাংশনগুলি যে সীমাবদ্ধ তা একবার দেখুন:

>>> for _, f in list(get_petters()):
...     print(f(), f.__closure__)

Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)

>>> for _, f in get_petters():
...     print(f(), f.__closure__)

Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)

উভয় লুপের জন্য, cellপুনরাবৃত্তিগুলির মধ্যে অবজেক্টটি একই থাকে। তবে, যেমনটি প্রত্যাশা করা হয়েছে, সুনির্দিষ্টভাবে strউল্লেখ করা হয়েছে এটি দ্বিতীয় লুপে পরিবর্তিত হয়। cellবস্তু বোঝায় animal, যা যখন তৈরি করা হয় get_petters()বলা হয়। যাইহোক, জেনারেটর ফাংশন চলমান হিসাবে এটি animalকোন strবস্তুকে বোঝায় তা পরিবর্তন করে ।

প্রথম লুপে, প্রতিটি পুনরাবৃত্তির সময়, আমরা সমস্ত fগুলি তৈরি করি , তবে জেনারেটর get_petters()সম্পূর্ণ নিঃশেষ হয়ে যাওয়ার পরে এবং listফাংশনগুলির একটি ইতিমধ্যে তৈরি হওয়ার পরে আমরা কেবল তাদের কল করি ।

দ্বিতীয় লুপে, প্রতিটি পুনরাবৃত্তির সময় আমরা get_petters()জেনারেটরকে fবিরতি দিচ্ছি এবং প্রতিটি বিরামের পরে কল করছি । সুতরাং, আমরা animalজেনারেটর ফাংশন বিরতি দেওয়া হয়েছে যে মুহুর্তে মান পুনরুদ্ধার শেষ ।

@ ক্লাডিউ যেমন একটি অনুরূপ প্রশ্নের উত্তর দিয়েছেন :

তিনটি পৃথক ফাংশন তৈরি করা হয় তবে তাদের প্রত্যেকটির পরিবেশ নির্ধারিত পরিবেশ বন্ধ হয়ে যায় - এক্ষেত্রে বৈশ্বিক পরিবেশ (বা লুপটি যদি অন্য ফাংশনের ভিতরে রাখা হয় তবে বাইরের ফাংশনের পরিবেশ)। এটি ঠিক সমস্যা, যদিও - এই পরিবেশে, animalরূপান্তরিত হয়, এবং বন্ধগুলি সমস্ত একইটিকে বোঝায় animal

[সম্পাদক দ্রষ্টব্য: iএতে পরিবর্তন করা হয়েছে animal]]

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