পাইথনে শৃঙ্খলাবদ্ধ অনুষ্ঠান


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
দেখে মনে হচ্ছে যে আপনি
কারিগরী

4
ইঙ্গিত: একটি নেস্টেড ফাংশনটি গতিশীলভাবে তৈরি করা হয়, এর প্যারেন্ট ফাংশনের স্থানীয়দের অ্যাক্সেস রয়েছে এবং এটি (কলযোগ্য) অবজেক্ট হিসাবে ফিরে আসতে সক্ষম।
জনাথন রাইনহার্ট

@ জোনাথনরাইনহার্ট এইভাবেই আমি সমস্যাটি নিয়ে ভাবছিলাম। তবে কীভাবে এটি বাস্তবায়ন করা যায় তা আমি সত্যি দেখিনি।
স্টিফান মেসকেন

4
একদিকে যেমন: পাইথন আপনাকে অবশ্যই গতিশীলভাবে ফাংশন তৈরি করতে দেয়। আপনি যদি আগ্রহী হন তবে এখানে কয়েকটি ধরণের সম্পর্কিত ধারণাটি পড়তে হবে: ডাব্লুপি: প্রথম শ্রেণির ফাংশন | পাইথনে আপনি কীভাবে একটি উচ্চতর অর্ডার ফাংশন করবেন? | functools.partial()| ডব্লিউপি: বন্ধ
লুকাশ গ্রাফ

@ লুকাসগ্রাফ আমি এটি দেখতে চাই। ধন্যবাদ!
স্টিফান মেসকেন

উত্তর:


100

এই হল কিনা আমি জানি না ফাংশন যতটা chaining যেমন এটা callable chaining কিন্তু, যেহেতু ফাংশন হয় 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তদ্ব্যতীত, একটি সাবক্লাস হিসাবে , প্রত্যাবর্তিত মান s এর আচরণ __repr__এবং __str__আচরণ ধরে রাখে intআরও জটিল ক্রিয়াকলাপের জন্য, আপনাকে অন্য ধোঁয়াগুলি যথাযথভাবে সংজ্ঞায়িত করা উচিত

@ কারিডরক যেমন একটি মন্তব্যে উল্লিখিত হয়েছে, addকেবল তেমনই লিখিত হতে পারে:

add = CustomInt 

addপরিবর্তে ক্লাসটির নামকরণও CustomIntএকইভাবে কাজ করে।


একটি ক্লোজার সংজ্ঞায়িত করুন, উত্পাদনের মানটির জন্য অতিরিক্ত কল প্রয়োজন:

আমি অন্য যেভাবে ভাবতে পারি তাতে নেস্টেড ফাংশন জড়িত যার ফলস্বরূপ ফিরে আসতে অতিরিক্ত খালি যুক্তি কল প্রয়োজন requires পাইথনের মধ্যে এটি পোর্টেবল করার জন্য আমি ফাংশন অবজেক্টগুলিতে অ্যাট্রিবিউটগুলি সংযুক্ত করার বিকল্প ব্যবহার করছি না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*__
বাকুরিউ

@ ক্যারিডরক বা এটি সংজ্ঞায়িত করার পরে CustomIntকেবল এটিকে কল করবেন না add
minipif

27

আপনি আমাকে ঘৃণা করতে পারেন, তবে এখানে একটি ওয়ান লাইনার রয়েছে :)

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

সম্পাদনা: ঠিক আছে, এটি কীভাবে কাজ করে? কোডটি @ জিমের উত্তরের মতো, তবে সবকিছুই এক লাইনে ঘটে।

  1. typeনতুন ধরনের গঠনে ব্যবহৃত করা যেতে পারে: type(name, bases, dict) -> a new type। যেহেতু nameআমরা খালি স্ট্রিং সরবরাহ করি, কারণ এই ক্ষেত্রে নামের প্রয়োজন নেই। জন্য bases(tuple) আমরা একটি প্রদান (int,), যা inheriting অভিন্ন intdictশ্রেণীর বৈশিষ্ট্যগুলি হল, যেখানে আমরা __call__ল্যাম্বডা সংযুক্ত করি ।
  2. self.__class__(self + v) অনুরূপ return CustomInt(self + v)
  3. নতুন প্রকারটি বাইরের ল্যাম্বডায় নির্মিত এবং ফিরে আসে।

17
বা এমনকি আরও ছোট:class add(int):__call__ = lambda self, v: add(self+v)
বাকুরিউ

4
শ্রেণীর ভিতরে থাকা কোডটি সাধারণ কোডের মতো হুবহু কার্যকর করা হয় যাতে আপনি অ্যাসাইনমেন্টের মাধ্যমে বিশেষ পদ্ধতিগুলি সংজ্ঞায়িত করতে পারেন। পার্থক্যটি হ'ল শ্রেণীর ক্ষেত্রটি কিছুটা ... অদ্ভুত।
বাকুরিউ

16

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

পরবর্তী পয়েন্টটি হ'ল আপনাকে সমস্ত যুক্তি সংরক্ষণ করতে হবে, যার এক্ষেত্রে আপনি Coroutines বা পুনরাবৃত্ত ফাংশনটি ব্যবহার করতে চাইতে পারেন । তবে মনে রাখবেন যে কর্টাইনগুলি বিশেষত এ জাতীয় কাজের জন্য পুনরাবৃত্ত ফাংশনগুলির চেয়ে অনেক বেশি অনুকূলিত / নমনীয়

এখানে Coroutines ব্যবহার করে একটি নমুনা ফাংশন দেওয়া হয়েছে, এটি নিজের সর্বশেষতম অবস্থাটি সংরক্ষণ করে। দ্রষ্টব্য যে এটি একাধিকবার বলা যাবে না যেহেতু রিটার্ন মানটি 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)

আপনি যে উত্তরটি সন্ধান করছেন এটি এটি নয় এবং আপনি এটি জানতেও পারেন তবে আমি ভেবেছিলাম যে আমি তা যাহাই হউক না কেন কারণ যদি কেউ এই কাজটি কৌতূহল ছাড়াই নয় বরং কাজের জন্য ভাবছিলেন। তাদের কাছে সম্ভবত "সঠিক জিনিস করার" উত্তর থাকা উচিত।


4
আমি তোমার ' পিএস ' নোট, নিকোচর সরিয়েছি। পাইথনটি কী মার্জিত তা আমরা সকলেই সচেতন :-) আমি মনে করি না যে এটি উত্তরের মূল অংশে অন্তর্ভুক্ত।
দিমিত্রিস ফাসারাকিস হিলিয়ার্ড

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হ'ল উপায়।

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