سه متد جادویی init ،call و getitem در برنامه نویسی شئی گرای پایتون


یکی از ویژگی های خیلی خوب برنامه نویسی شئی گرا، متدهای جادویی است. میشه گفت متدهای جادویی در برنامه نویسی شئی گرا همه چیز هستند و با تعریفشون به معنای واقعی جادو به کدهامون اضافه می‌کنیم. متدهای جادویی هر کدام کار خاصی انجام می‌دهند و میتوانیم یک یا چند تا از آنها را در داخل یک کلاس تعریف کنیم. سه متد جادویی __init__، __call__ و __getitem__ در طراحی  DataLoader ها و شبکه‌های عصبی عمیق در پایتورچ بسیار پرکاربرد هستند. برای همین در این پست با یک مثال عملی با هر سه متد و تفاوتشون باهم آشنا خواهیم شد.

نکته: در انتهای پست، ویدیوی مربوط به پست قرار داده شده است.

اسم گذاری متدهای جادویی

اسم متدهای جادویی با دو تا underscore(__magicMehtod__)  اول و آخر مشخص می شود و برای پایتون اینها شناخته شده هستند. برای همین ما نباید متدی را با این فرمت تعریف کنیم. این نوع اسم گذاری فقط برای متدهای جادویی هست. همین و بس!

البته باید اشاره کنم که متدهای جادویی از اول برای پایتون شناخته شده هستند، و ما فقط آنها را در کلاسمون تعریف کنیم و در کارمون استفاده کنیم.

نحوه فراخوانی متدهای جادویی

ما نیازی به فراخوانی مستقیم متدهای جادویی نداریم، چرا که آنها به وقت نیاز به صورت خودکار فراخوانی می‌شوند. ولی اگر یکی بخواهد مستقیما خودش فراخوانی کند میتواند مثل سایر متدهای یک کلاس به شکل زیر فراخوانی کند.

Python

objectName.__magicMethodName__(<any arguments>)

فرق سه متد جادویی __init__، __call__ و __getitem__

به طور کلی اگر بخواهیم با تفاوت این سه متد جادویی آشنا شویم، باید بگویم، متد جادویی init در زمان ساخت یک آبجکت فراخوانی شده و کدهای داخل آن اجرا می‌شود. متد جادویی call در زمان فراخوانی یک object فراخوانی شده و کدهای داخل آن اجرا می‌‎شود، و متد getitem هم در زمان آدرس‌دهی (indexing) یک object فراخوانی شده و کدهای داخل آن اجرا می‌شود.

متد جادویی __init__

میشه گفته در اکثر کلاسها این متد تعریف می‌شود. همانطور که از اسم این متد مشخص است، در این متد مقداردهی اولیه و آماده سازی کلاس برای انجام پردازش‌ها استفاده می‌شود. شناسه‌های object در داخل این متد جادویی تعریف می شوند. برای مثال در شبکه های عصبی، وزنهای اولیه و مقادیر پارامترهای شبکه اینجا مقداردهی می شوند. یا در ساخت DataLoader نوع تبدیلات روی داده اینجا مشخص می شود.

چه زمانی متد جادویی __init__ فراخوانی می شود؟

متد جادویی __init__ در زمان ساخت object از روی کلاس فراخوانی شده و کدهای داخل آن اجرا می شود. به عبارتی میتوان گفت پروسه instantiation بدین ترتیب انجام می شود:

که در زمان ساخت یک object، پایتون یک فضا در حافظه برای object در نظر می‌گیرد، سپس کلاس را بررسی می‌کند، اگر داخل کلاس متدِ __init__ فراخوانی شده باشد، این متد فراخوانی شده و کدهای داخل آن اجرا می شود.

ورودی‌های متد جادویی __init__ چه زمانی مقداردهی می شوند؟

اگر در تعریف متد جادویی به غیر از self ورودی های دیگری تعریف شود، این ورودیها در زمان ساخت object باید مقدار دهی شوند، در غیراینصورت پایتون خطا خواهد داد.

Python

class ClassName:
     def __init__(self,input1,input2,…):
          …

objectName= ClassName(input1=vlaue1, input2=vlaue2,…)

متد جادویی __getitem__

در برنامه نویسی شئی گرای پایتون ما با تعریف یک کلاس، در واقع ساختار جدیدی از داده را تعریف می کنیم. مثل list, str . در خیلی از مواقع ما نیاز به دسترسی به آیتمهای یک داده هستیم. Object کلاسی که تعریف کردیم، به خودی خود قابلیت آدرس‌دهی را ندارد. یعنی اگر به شکل زیر عمل کنیم پایتون خطا خواهد داد.

Python

objectName[index]

ما با تعریف متد getitem به object کلاسمون (ساختار جدید داده) قابلیت آدرس‌دهی را اضافه می‌کنیم.

Python

class ClassName:
     def __init__(self,input1,input2,…):
          …
     def __getitem__(self,index):
         …

متد getitem علاوه بر self، یک ورودی دیگر به اسم index دارد که باید تعریف کنیم. میتوان داخل این متد هر چیزی را نوشت. در داخل متدهای جادویی منطق را شما تعیین می‌کنید که چی باشه و چه اتفاقی بیافتد. اما ما معمولا از اینها در راستای هدفی که برای آن ساخته شده اند استفاده می‌کنیم. برای مثال ما از این متد برای برگرداندن یک نمونه از داده در DataLoader استفاده می‌کنیم.

چه زمانی متد جادویی __getitem__ فراخوانی می شود؟

متد جادویی __getitem__ در زمان آدرس‌دهی یک  object فراخوانی شده و کدهای داخل آن اجرا می شود.

Python

objectName[index]

ورودی‌های متد جادویی __getitem__ چه زمانی مقداردهی می شوند؟

همانطور که اشاره کردیم، این متد کلا یک ورودی دارد، و زمان آدرس‌دهی مقدار می‌گیرد.

متد جادویی __call__

object ساخته شده از روی یک کلاس، به خودی خود قابل فراخوانی نیست. توجه کنیم که ما میتوانیم متدهای تعریف شده در داخل کلاس برای object را به شکل فراخوانی کنیم:

Python

objectName.methodName(<any arguments>)

اما اگر متد call را تعریف نکنیم، نمیتوانیم خود object را فراخونی کنیم. یعنی اگر به شکل زیر عمل کنیم پایتون خطا خواهد داد.

Python

objectName(<any arguments>)

ما با تعریف متد جادویی call به object کلاسمون قابلیت فراخوانی را اضافه می‌کنیم.

Python

class ClassName:
     def __init__(self,input1,input2,…):
          …
     def __getitem__(self,index):
         …
     def __call__(self,input1,input2,…): 
         …

این متد میتواند علاوه بر self ورودی‌های دیگه ای داشته باشد. ما بسته به نیازمون، یک یا چند ورودی براش در نظر می‌گیریم یا اصلا ورودی برای آن در نظر نمی‌گیریم.

چه زمانی متد جادویی __call__ فراخوانی می‌شود؟

متد جادویی __call__ در زمان فراخونی یک  object فراخوانی شده و کدهای داخل آن اجرا می‌شود.

Python

objectName(<any arguments>)

ورودیهای متد جادویی __call__ چه زمانی مقداردهی می شوند؟

ورودیهای تعیین شده در این متد در زمان فراخونی تعیین می‌شوند.

Python

objectName(input1=vlaue1, input2=vlaue2,…)

برای اینکه مباحث گفته شده بهتر جا بیافتد بیایید دو تا مثال ساده انجام دهیم.

مثال اول: یک کلاسی به اسم SimpleClass تعریف کنید که هر موقع از روی آن objectی ساخته شده عبارت 1، و هر زمان object آن کلاس فراخوانی شد، عبارت 2 و هر زمان خود object فراخوانی شد، عبارت 3 را چاپ شود.

1- Object is created!
2- Object is indexed!
3- Object is called!

Python

class SimpleClass:
     def __init__(self):
         print('Object is created!')
     def __call__(self):
         print('object is called!')
     def __getitem__(self,index):
         print('object is indexed!')

p= SimpleClass()
p[0]
p()
>>>
  Object is created!
  Object is indexed!
  Object is called!

مثال دوم: کلاسی به اسم Person تعریف کنید که مشخصات و ویژگی های زیر را داشته باشد.

  • یک شناسه به عنوان اسم در در نظر بگیرد و در داخل آنها اسم افراد را قرار دهد.
  • در زمان آدرس دهی object، اسم فرد index از اسامی را برگرداند.
  • در زمان فراخوانی object، قد، وزن و شماره فرد (عددی اسکالر) دریافت نماید، سپس شاخص توده بدنی فرد را محاسبه کرده چاپ نماید.

Python

class Person:
     def __init__(self,names):
         self.names= names
     def __call__(self,weigth, height,index):
         height= height/100
         bmi= (weigth/height**2)
         name=self[index]
         print(f'{name}  with BMI: {round(bmi,ndigits=2)}')
     def __getitem__(self,index):
         name= self.names[index]
         return name
names= ['Ali','Goli','Cherloo','Mari','Rose']
p= Person(names)
print(p[2])
p(78,178,2)
>>> 
    Cherloo
    Cherloo  with BMI: 24.62



دیدگاه ها

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

code