سلام دوستان...
در این قسمت از آموزش پایتون به مفهوم وراثت می پردازیم :
البته که یک ویژگی زبان به نام کلاس، بدون پشتیبانی از وراثت ارزش نخواهد داشت. نحوه نگارش برای تعریف یک کلاس مشتق شده به صورت زیر است:
class DerivedClassName(BaseClassName): <statement-1> . . <statement-n> </statement-n></statement-1>
نام BaseClassName باید در حوزهی در بر دارنده تعریف کلاس مشتق شده، تعریف شود. به جای نام یک کلاس پایه، سایر عبارات دلخواه نیز مجاز است. برای مثال زمانی که کلاس پایه در ماژول دیگری تعریف شده باشد، این می تواند مفید باشد.
class DerivedClassName(modname.BaseClassName):
آموزش پایتون : روند اجرای تعریف یک کلاس مشتق شده همانند کلاس پایه است. زمانی که شی کلاس ساخته شود، کلاس پایه به یاد می آید. از این برای بر طرف کردن ارجاع های ویژگی استفاده می شود. اگر یک ویژگی درخواست شده در کلاس موجود نباشد، روند جستجو به بررسی کلاس پایه می پردازد. در صورتی که خود کلاس پایه نیز از کلاس دیگری مشتق شده باشد، این قانون به صورت بازگشتی اعمال می شود.
هیچ چیز خاصی درباره نمونه گیری از کلاس های مشتق شده و جود ندارد: DerivedClassName() یک نمونه جدید از کلاس ایجاد می کند. ارجاع های متد به این صورت انجام می شوند: ویژگی کلاس مربوطه جستجو می شود، در صورت لزوم از زنجیره کلاس های پایه پایین می آید، و اگر این به یک شی تابع برسد (اگر این عملیات به نتیجه برسد)، مرجع متد معتبر است.
کلاس های مشتق شده ممکن است متدهای کلاس های پایه خود را تغییر دهند. زیرا متدها در زمان فراخوانی سایر متدهای یک شی مشابه، هیچ امتیاز خاصی ندارند. یک متد از یک کلاس پایه، که متد دیگری که در همان کلاس پایه تعریف شده است را فراخوانی کند، ممکن است به فراخوانی یک متد از یک کلاس مشتق شده که آن را تغییر داده است منجر شود. (برای برنامه نویسان C++: تمامی متدهای پایتون به طور موثری مجازی (virtual) هستند.)
متد تغییر دهنده در یک کلاس مشتق شده ممکن است در واقع به جای جایگزینی متد کلاس پایه هم نام، صرفا بخواهد آن را توسعه دهد. یک روش ساده برای فراخوانی مستقیم متد کلاس پایه وجود دارد: فقط BaseClassName.methodname(self, arguments) را صدا کنید. همچنین این گاهی برای مشتری ها نیز مفید است. (توجه داشته باشید این تنها در صورتی که کلاس پایه به عنوان BaseClassName در حوزه سراسری قابل دسترس باشد، کار می کند. )
پایتون دارای دو تابع داخلی است که با وراثت کار می کنند:
همچنین پایتون از گونه ای از وراثت چندگانه (multiple inheritance) پشتیبانی می کند. تعریف یک کلاس با چندین کلاس پایه به صورت زیر است:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-n> </statement-n></statement-1>
برای بیشتر مقاصد، در ساده ترین حالت، می توانید جستجو برای ویژگی های به ارث رسیده از یک کلاس والد را به صورت اول-عمق، چپ –به- راست، در نظر بگیرید که در یک کلاس مشابه که همپوشانی در سلسله مراتب وجود دارد دو بار جستجو نکنید. بنابراین، اگر یک ویژگی در DerivedClassName یافت نشود، در Base1 جستجو شده است، سپس (به صورت بازگشتی) در کلاس پایه Base1 جستجو شده است، و اگر در آنجا نیز یافت نشود، در Base2 جستجو می شود و این روند ادامه دارد.
در حقیقت قضیه کمی پیچیده تر از این است. ترتیب وضوح متد، برای پشتیبانی از فراخوانی های مشارکتی super() به صورت پویا تغییر می کند. این روند در سایر زبان های وراثت چندگانه با عنوان فراخوانی متد بعدی (call-next-method ) شناخته شده است و بسیار قدرتمند تر از فراخوانی super() موجود در زبان های تک وراثتی است.
از آنجایی که همه موارد وراثت چندگانه، یک یا چند رابطه لوزی گون (جایی که حد اقل یکی از کلاس های والد می تواند از طریق چندین مسیر از پایین ترین کلاس دسترس پذیر باشد). را ارائه می کنند، مرتب سازی پویا ضروری است . برای مثال، تمامی کلاس ها از object ارث می برند، بنابراین هر موردی از وراثت چندگانه، بیشتر از یک مسیر برای دستیابی به object را فراهم می کند. برای جلوگیری از دسترسی به کلاس های پایه، بیشتر از یک بار، الگوریتم پویا به گونه ای ترتیب جستجو را خطی می کند که ترتیب چپ به راست مشخص شده در هر کلاس، که هر والد را فقط یک بار صدا میزند، را حفظ کند، و این یکنواخت است (به این معنی که یک کلاس می تواند بدون اثر گذاری روی ترتیب حق تقدم والدین خود، زیر کلاس شود). همه این ویژگی ها با هم، طراحی کلاس های قابل اطمینان و قابل توسعه با وراثت چندگانه را امکان پذیر می کند.
متغیر های نمونه خصوصی که از جایی غیر از درون یک شی قابل دسترس نباشند، در پایتون وجود ندارد. اگر چه قراردادی وجود دارد که توسط اکثر کدهای پایتون رعایت می شود: نامی که بعد از خط زیرین بیاید، مانند (_spam) باید به عنوان یک بخش غیر عمومی از API در نظر گرفته شود( چه یک تابع باشد یا یک متد یا یک عضو داده). این باید به عنوان جزییات پیاده سازی در نظر گرفته شود و بدون اطلاع قبلی تغییر کند.
از آنجایی که یک کاربرد معتبر برای اعضای خصوصی کلاس وجود دارد( برای جلوگیری از درگیری نام ها با نام های تعریف شده توسط زیر کلاس ها)، یک پشتیبانی محدود به نام ‘تغییرات نام’ (name mangling) برای چنین مکانیزمی وجود دارد.
هر شناسه ای در قالب __spam (حد اقل دو خط زیرین در قبل، حداکثر یک خط زیرین در ادامه) از لحاظ نگارشی با _classname__spam جایگزین می شود، که classname نام کلاس جاری است که خط زیرین قبل از آن حذف شده است. این تغییرات تا زمانی که درون تعریف کلاس رخ دهند ، بدون توجه به مکان نوشته شدن شناسه انجام می شوند.
تغییرات نام برای امکان پذیر کردن تغییر متدها توسط زیر کلاس ها، بدون شکستن فراخوانی های متد درون-کلاسی مفید هستند. برای مثال:
def __init__(self, iterable): self.items_list = [] iterable self.__update() def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item)
مثال بالا حتی اگر MappingSubclass مجبور به معرفی شناسه __update باشد نیز کار می کند، زیرا به ترتیب با _Mapping__update در کلاس Mapping ، و _MappingSubclass__update در کلاس MappingSubclass جایگزین شده است.
توجه داشته باشید، قوانین تغییرات اکثرا برای جلوگیری از تصادفات طراحی شده اند اما همچنان احتمال دسترسی یا تغییر متغیری که خصوصی فرض می شود وجود دارد. در بعضی شرایط خاص مانند اشکال زدایی، این امر می تواند حتی مفید باشد.
توجه کنید، کد پاس داده شده به exec() یا eval() ، نام کلاس صدا زننده کلاس را به عنوان کلاس جاری در نظر نمی گیرد. این مشابه اثر عبارت global است، اثری که به همین ترتیب به کدی که با هم کامپایل (byte-compiled) شده اند محدود می شود. همین محدودیت روی getattr() ، setattr() و delattr() ، و نیز در زمان ارجاع مستقیم به __dict__ اعمال می شود.
گاهی اوقات داشتن یک نوع داده، مشابه record در پاسکال یا struct در C می تواند مفید باشد تا تعدادی آیتم داده نام گذاری شده را با هم جمع کنیم. یک تعریف کلاس خالی در اینجا مناسب خواهد بود:
class Employee: pass john = Employee() # Create an empty employee record # Fill the fields of the record john.name = 'John Doe' john.dept = 'computer lab' john.salary = 1000
یک قطعه کد پایتون که انتظار یک نوع داده انتزاعی خاص را دارد، اغلب می تواند از یک کلاسی که به جای آن، متدهای آن نوع داده را تقلید می کند، عبور داده شود . برای مثال، اگر تابعی داشته باشید که برخی داده ها را از یک شی فایل فرمت(format) می کند، می توانید یک کلاس با متد های read() و readline() تعریف کنید که داده را از بافر رشته دریافت کند و آن را به عنوان یک آرگومان پاس دهد.
اشیای متد نمونه نیز دارای ویژگی هستند: m.__self__ شی نمونه با متد m() است، و m.__func__ شی تابع متناظر با متد است.
آموزش پایتون ادامه دارد