পাইথন: functools.partial কেন প্রয়োজনীয়?


193

আংশিক প্রয়োগ দুর্দান্ত। functools.partialল্যাম্বডাসের মাধ্যমে আপনি কী পেতে পারবেন না এমন কোন কার্যকারিতা উপলব্ধ করে ?

>>> 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ল্যাম্বডাসের মাধ্যমে আপনি কী পেতে পারবেন না এমন কোন কার্যকারিতা উপলব্ধ করে ?

অতিরিক্ত কার্যকারিতার দিক থেকে খুব বেশি নয় (তবে পরে দেখুন) - এবং, পাঠযোগ্যতা দর্শকের চোখে পড়ে।
বেশীরভাগ মানুষই যারা কার্যকরী প্রোগ্রামিং ভাষা (বিশেষ করে পাতার মর্মর / স্কীম পরিবারের মধ্যে যারা) সাথে পরিচিত পছন্দ করে বলে মনে হচ্ছে lambdaশুধু জরিমানা - আমি বলতে "সবচেয়ে", স্পষ্টভাবে না সব, কারণ গাইডো এবং আমি নিঃসন্দেহে "সঙ্গে পরিচিত" মধ্যে হয় (ইত্যাদি ) তবুও lambdaপাইথনকে চোখের ব্যঙ্গ হিসাবে ভাবুন ...
তিনি কখনও এটিকে পাইথন হিসাবে গ্রহণ করার জন্য অনুতপ্ত ছিলেন এবং "পাইথনের গ্লিটস" হিসাবে এটি পাইথন 3 থেকে অপসারণের পরিকল্পনা করেছিলেন।
আমি তাকে পুরোপুরি সমর্থন করেছি। (আমি lambda স্কিমের সাথে ভালবাসি ... পাইথনের ক্ষেত্রে এর সীমাবদ্ধতা এবং অদ্ভুত উপায়ে এটি ঠিক করেনি ' বাকী ভাষার সাথে আমার ত্বককে ক্রল করুন)।

তবে তা নয়, 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]

এখন একটি একক অভিব্যক্তি মধ্যে নামে-আর্গুমেন্ট overridability, প্লাস তিন গুণাবলীর সেটিং একত্রিত করা, এবং আমাকে বলুন ঠিক কিভাবে পাঠযোগ্য যে হতে যাচ্ছে ...!


2
হ্যাঁ, আমি বলব functools.partialযে আপনার উল্লেখ করা অতিরিক্ত ক্রিয়াকলাপটি এটিকে ল্যাম্বদার চেয়ে উচ্চতর করে তোলে। সম্ভবত এটি অন্য পোস্টের বিষয়, তবে এটি এমন একটি ডিজাইন স্তরের কী যা আপনাকে এতটা বিরক্ত করে lambda?
নিক হেইনার

11
@ রোসার্চ, যেমনটি আমি বলেছিলাম: প্রথমত, এটির সীমাবদ্ধতা (পাইথন এক্সপ্রেশন এবং বক্তব্যগুলিকে তাত্পর্যপূর্ণভাবে পার্থক্য করে - একক অভিব্যক্তির মধ্যে আপনি অনেক কিছুই করতে পারেন না বা বোধগম্যভাবে করতে পারবেন না , এবং ল্যাম্বডা এর দেহ এটিই ); দ্বিতীয়ত, এটি একেবারে অদ্ভুত সিনট্যাক্স চিনি। যদি আমি সময় মতো ফিরে যেতে এবং পাইথনের মধ্যে একটি জিনিস পরিবর্তন করতে পারি, তবে এটি অযৌক্তিক, অর্থহীন, চোখের দুল defএবং lambdaকীওয়ার্ডগুলি হবে: তাদের উভয়কেই তৈরি করুন function(একটি নাম পছন্দ জাভাস্ক্রিপ্ট সত্যই সঠিকভাবে পেয়েছে ), এবং আমার আপত্তিগুলির মধ্যে কমপক্ষে 1/3 টি বিলুপ্ত হবে -)। যেমনটি আমি বলেছি, লিস্পে ল্যাম্বডায় আমার কোনও আপত্তি নেই ...! -)
অ্যালেক্স মার্টেলি

1
@ অ্যালেক্স মার্তেল্লি, গুইডো ল্যাম্বডায় এই জাতীয় সীমা কেন স্থির করলেন: "দেহের একক অভিব্যক্তি"? সি # এর ল্যাম্বডা শরীর কোনও ফাংশনের শরীরে বৈধ কিছু হতে পারে। কেন গাইডো কেবল অজগর ল্যাম্বদার সীমাবদ্ধতা সরিয়ে ফেলবে না?
পিটার লং

3
@PeterLong আশা রাখি গাইডো আপনার প্রশ্নের উত্তর দিতে পারবেন না। এর সংক্ষিপ্তসারটি হ'ল এটি খুব জটিল হবে এবং আপনি যে defকোনও উপায়ে ব্যবহার করতে পারেন । আমাদের দানশীল নেতা কথা বলেছেন!
new123456

5
@ অ্যালেক্সমার্টেলি ড্রপবক্সের গিডোতে একটি আকর্ষণীয় প্রভাব রয়েছে - টুইটার.
ডেভিড

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) এর মধ্যে একটি বাগ ছাড়া আর কিছুই নয় , যেমন আমি অতীতে দীর্ঘ SO উত্তরগুলির দৈর্ঘ্যে দেখিয়েছি: আপনি খুব তাড়াতাড়ি বেঁধে যখন আপনি যা চান, দেরী বাঁধাই ডিফল্ট ব্যবহার করলে যে কি আপনি চান, এবং যে ঠিক ডান নকশা পছন্দ পাইথন এর নকশা বাকি প্রেক্ষাপটে দেওয়া।
অ্যালেক্স মার্টেলি

1
@ অ্যালেক্স মার্তেলি: হ্যাঁ, দুঃখিত। আমি ঠিক দেরীতে আবদ্ধ হওয়ার সঠিকভাবে অভ্যস্ত হতে ব্যর্থ হয়েছি, সম্ভবত কারণ যখন আমি কার্যকারিতা সংজ্ঞায়িত করার সময় মনে করি যে আমি আসলে ভালর জন্য কিছু সংজ্ঞায়িত করছি এবং অপ্রত্যাশিত বিস্ময়গুলি কেবল আমাকে মাথা ব্যথার কারণ করে। (আরো যখন আমি পাইথন তুলনায় জাভাস্ক্রিপ্ট মধ্যে কার্মিক কিছু করার চেষ্টা করুন, যদিও।) আমি বুঝি যে অনেক মানুষ আছে প্রয়াত বাঁধাই সঙ্গে আরামদায়ক, এবং এটি পাইথন এর নকশা বাকি সঙ্গে সামঞ্জস্যপূর্ণ যে। আমি এখনও আপনার অন্যান্য দীর্ঘ এসও উত্তরগুলি পড়তে চাই, যদিও - লিঙ্কগুলি? :-)
ShreevatsaR

3
অ্যালেক্স ঠিক আছে, এটি কোনও বাগ নয়। তবে এটি এমন একটি "গোটচা" যা অনেকগুলি ল্যাম্বদা উত্সাহীদের আটকে রাখে। : একটি haskel / ক্রিয়ামূলক ধরণ থেকে যুক্তির "বাগ" পাশ করার জন্য Andrej বাউইর এর পোস্ট দেখতে math.andrej.com/2009/04/09/pythons-lambda-is-broken
Ars

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

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
@ পোস্টিংটি পোস্টটি ২০১০ partialসালের। পাইথন ২.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))

পাইথন ৩.৩ এ এটি দেয়:

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

যার অর্থ এই যে সৃষ্টির জন্য আংশিক কিছুটা সময় প্রয়োজন তবে কার্যকর করার জন্য যথেষ্ট কম সময় রয়েছে। এটি আরস থেকে উত্তরে আলোচনা করা প্রথম এবং দেরীতে আবদ্ধ হওয়ার প্রভাবটি হতে পারে ।


3
আরও গুরুত্বপূর্ণ, partialখাঁটি পাইথনের পরিবর্তে সি-তে লেখা হয়েছে, যার অর্থ এটি কেবল একটি ফাংশন তৈরি করার চেয়ে আরও কার্যকর কল করতে পারে যা অন্য ফাংশন বলে।
চিপনার

12

অ্যালেক্স উল্লেখ করা অতিরিক্ত কার্যকারিতা ছাড়াও, ফান্টাকুলস.পার্পিয়ালটির আরেকটি সুবিধা হ'ল গতি। আংশিকর সাহায্যে আপনি অন্য স্ট্যাক ফ্রেম নির্মাণ (এবং ধ্বংস) এড়াতে পারবেন।

আংশিক বা ল্যাম্বডাস দ্বারা উত্পাদিত ফাংশনটির ডিফল্টরূপে ডকাস্ট্রিং নেই (যদিও আপনি কোনও বস্তুর জন্য ডক স্ট্রিং সেট করতে পারেন __doc__)।

আপনি এই ব্লগে আরও বিশদ জানতে পারেন: পাইথনে আংশিক ফাংশন অ্যাপ্লিকেশন


আপনি যদি গতির সুবিধাটি পরীক্ষা করে থাকেন তবে ল্যাম্বডা ওভার ল্যাম্বডিয়ায় আংশিক গতির কোন উন্নতি আশা করা যায়?
ট্রিলারিয়ান

1
যখন আপনি বলেন যে ডক্টরসিং উত্তরাধিকার সূত্রে প্রাপ্ত, আপনি কোন পাইথন সংস্করণটি উল্লেখ করেন? পাইথন 2.7.15 এবং পাইথন 3.7.2 এ তারা উত্তরাধিকার সূত্রে প্রাপ্ত হয় না। কোনটি ভাল জিনিস, কারণ আংশিক প্রয়োগিত যুক্তি সহ ফাংশনটির জন্য মূল ডকস্ট্রিং অগত্যা সঠিক নয়।
জানু

পাইথন ২.7-এর জন্য ( ডকস.পিথথন.আর. / ২ / লিবারি / ফান্টুলস। Html# পার্টিশিয়াল -অবজেক্টস ): " নাম এবং ডক বৈশিষ্ট্যগুলি স্বয়ংক্রিয়ভাবে তৈরি হয় না"। একই জন্য 3. [5-7]।
ইয়ারোস্লাভ নিকিতেনকো

আপনার লিঙ্কে একটি ভুল রয়েছে: লগ_ইনফো = আংশিক (লগ_টেম্পলেট, স্তর = "তথ্য") - এটি সম্ভব নয় কারণ উদাহরণে স্তরের কোনও কীওয়ার্ড যুক্তি নয়। পাইথন 2 এবং 3 উভয়ই বলে: "প্রকারের ত্রুটি: লগ_টেম্প্লেট () আর্গুমেন্ট 'স্তর' এর জন্য একাধিক মান পেয়েছে"।
ইয়ারোস্লাভ নিকিতেনকো

আসলে, আমি হাত দ্বারা একটি আংশিক (চ) তৈরি করেছি এবং এটি 'আংশিক (ফানক, * আরগস, ** কীওয়ার্ড) হিসাবে ডক ফিল্ড দেয় - প্রদত্ত যুক্তি এবং কীওয়ার্ডগুলির আংশিক প্রয়োগের সাথে নতুন ফাংশন \ n' (উভয়ই) অজগর 2 এবং 3 এর জন্য)।
ইয়ারোস্লাভ নিকিতেনকো

1

তৃতীয় উদাহরণে আমি দ্রুত উদ্দেশ্যটি বুঝতে পারি understand

আমি যখন ল্যাম্বডাস পার্স করি তখন আমি স্ট্যান্ডার্ড লাইব্রেরি থেকে সরাসরি প্রস্তাবিত চেয়ে আরও জটিলতা / বিজোড়তা আশা করি।

এছাড়াও, আপনি লক্ষ্য করবেন যে তৃতীয় উদাহরণটি একমাত্র যা সম্পূর্ণ স্বাক্ষরের উপর নির্ভর করে না sum2; সুতরাং এটি কিছুটা আলগাভাবে জোড়ায় তৈরি করে।


1
হুঁ, আমি আসলে বিপরীত প্ররোচনার মধ্যে আছি, functools.partialকলটি বিশ্লেষণ করতে আমি অনেক বেশি সময় নিয়েছি , যেখানে ল্যাম্বডাস স্ব-স্পষ্ট।
ডেভিড জেড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.