سلام؛ چند وقت پیش با یه چالش در وب سایت codewars در زمینه برنامه نویسی پایتون مواجه شدم که نظرم رو جلب کرد و تصمیم گرفتم توی ویرگول با شما هم درمیون بزارم.
تابعی بنویسید که اینطور عمل کنه
add(1)(2) #result: 3 add(4)(4) #result: 8 add(5)(5)(5) #result: 15
مطمئنم حدس زدید که عملکردش چیه. ما این تابع رو باید طوری پیاده سازی کنیم که هر چندبار که خواستیم بتونیم فراخوانیش کنیم و هر بار یک عدد جدید به عنوان آرگومان بهش بدیم. در نهایت این تابع باید جمع تمام اعدادی که بهش به عنوان آرگومان داده شده رو بهمون بده.
خوب بزارید جمع بندی کنیم:
خوب، این بزرگ ترین چالشی هستش که ما در این مسئله داریم. چطور ممکنه یه تابع، هم یک عدد رو به عنوان مقدار برگشتی برگردونه و هم یک تابع دیگه رو؟
در پایتون همه انواع داده، خودشون از نوع object هستند. یعنی اینکه شما می تونید از انواع داده در پایتون مثل str و int و dict و ... ارث بری کنید. اگه این قابلیت نبود خیلی این چالش سخت میشد.
در پایتون توابع مخصوصی وجود دارند که برای کار های خاصی در نظر گرفته شده اند. اگه با شیء گرایی در پایتون آشنا باشید؛ باید با برخی از این توابع رو بشناسید. مثل __init__ که برای سربار گذاری اولیه (initialization) استفاده میشه (اولین متد مخصوصی که باهاش آشنا میشید معمولا همینه).
از متد __call__ برای این استفاده میشه که instance های یک class قابل فراخوانی (callable) بشوند. برای اینکه مطلب جا بیفته به کد زیر توجه کنید.
class A: def __init__(self): print ("initialized") def __call__(self): print ("the instance is invoked") instance = A() # initializing the class instance() # calling the instance of type `A` #result: # initialized # the instance is invoked
خوب همونطور که میبینید، در کد بالا instance رو دقیقا مثل یک تابع صدا زدیم. این به لطف متد __call__ امکان پذیره. اگر یک object، متد __call__ رو داشته باشه، می تونیم فراخوانیش کنیم اما اگر نداشته باشه، موقع فراخوانی اروری مبنی بر اینکه این شیء قابل فراخوانی نیست خواهیم گرفت.
ما در پایتون می تونیم ویژگی هایی که کلاس های دیگه دارند رو به ارث ببریم.اگر کلاسی از کلاس دیگر ارث ببره، تمام متد ها و property های اون کلاس رو هم به ارث میبره .در پایین یه مثال ساده رو برای نشون دادن این مطلب آورده ام
class A: def hello(self): return "Hello " class B(A): # B inherits from A, so it has a method called hello as well def greet(self, name): return self.hello() + name obj = B() obj.greet("Ashkan") # result: 'Hello Ashkan'
ما در این کد، دو کلاس به نام A و B داریم، که B از A ارث بری کرده. پس تمام توابع کلاس A در کلاس B هم وجود دارند. اگر به کد های نوشته شده در تابع greet نگاهی بندازید، می فهمید که در اون از تابع hello استفاده شده. تابع hello از کلاس A به B به ارث رسیده.
خوب تا به اینجا تموم چیز های مورد نیاز رو یاد گرفتید. کاری که قراره انجام بدیم اینه:
class CustomNumber(int): #inherits from int data type def __call__(self, number_to_add): return CustomNumber(self + number_to_add)
فقط اینکه یه نکته ای در این کد وجود داره. مقدار برگشتی متد __call__، یک instance از نوع CustomNumber است. دلیل این کارم اینه که self (برخلاف چیزی که انتظار میره) از نوع int هستش. برای همین هربار یک instance جدید از روی CustomNumber میسازیم و به عنوان مقدار برگشتی، برمیگردونیم.
البته؛ خوب واقعا دلیلش رو نمیدونم که چرا تو این مورد self از نوع int هستش. اگه شما می دونید؛ خوشحال میشم که در کامنت هاتون بهم بگید.
add = CustomNumber(10) # initialize `self` with the value of 10 value = add(20)(50)(30) print (value) # 110