شی گرایی در پایتون ، بخش دوم

همانند دنیای واقعی که در بردارنده دو مفهوم هستی (چیز ، پدیده ، موجودیت) و طبقه بندی است. برنامه نویسی شی گرا (OOP) نیز بر محور دو مفهوم اساسی کلاس (Class) و شی (Object) شکل گرفته است. در برنامه نویسی شی گرا ، هر برنامه در قالب نهادهای کوچکی به نام شی (object) که از روی کلاس ها ساخته می شوند و با یکدیگر برهم کنش دارند در نظر گرفته می شوند. برای داشتن این اشیا ابتدا باید کلاس های مورد نیاز برنامه را تعریف کنیم. کلاس همانند نقشه یک ساختمان است این نقشه خود ساختمان نیست اما راهنمایی است که از روی آن یک خانه واقعی ساخته می شود. دردنیای زبان های برنامه نویسی شی گرا نیز همین گونه است ما ابتدا یک یا چندین کلاس تعریف می کنیم که همانند نقشه یک خانه در برگیرنده ویژگی ها و رفتارهایی است که در برنامه نیاز داریم ، سپس از روی این کلاس ها تعدادی شی می سازیم  همانگونه که از روی یک نقشه می توان تعداد زیادی خانه ساخت از یک کلاس هم می توان به تعداد دلخواه شی ساخت.به زبان ساده تر می توان گفت که : کلاس یک نقشه ساخت است و شی نمونه ای است که بر اساس آن ساخته می شود.

   کلاس در پایتون :

در پایتون برای تعریف یک کلاس رهنمون class بکارگرفته می شود. الگوی زیر نشان دهنده چگونگی تعرف کلاس در پایتون است :

Class  ClassName:

       <statement 1>

        .

        .

        .

       <statement n>

همانگونه که دیده می شود نام کلاس پس از رهنمون class نوشته می شود و پس از آن نویسه دو نقطه بیانی (:) جای می گیرد . ناگفته نماند که پس از نام کلاس و پیش از نویسه : می توان یک جفت کمانک باز و بسته () قرار داد اما پیشنهاد می شود تنها زمانی از این نگارش استفاده کنید که بخواهید مفهوم وراثت را پیاده سازی کنید.دستورات بدنه کلاس با اولین تورفتگی آغاز و با آخرین تورفتگی پایان می پذیرند. به زبان ساده تر هر سطر دستور موجود در یک کلاس با یک تورفتگی آغاز می شود.

 در سرتاسر بدنه کلاس یکنواختی در تورفتگی باید رعایت گردد.

بدنه یک کلاس نمی تواند خالی باشد از این رو در مواردی که به هردلیلی نمی خواهید دستوری در بدنه کلاس قرار گیرد می توانید از تنها یک دستور pass  استفاده کنید.

مثال :

class   myclass:

            pass

 

مثال : کلاسی به نام مستطیل می سازیم که دارای دو صفت به نامهای طول (Length) و عرض (Width) است :

class Rectangle:

    length=15

    width=10

 

نمونه سازی :

 به عملیات ایجاد یک شی از یک کلاس نمونه سازی (Instantiation) گفته می شود. از هر کلاس می توان بی شمار نمونه (شی) ساخت هر شی ساخته شده از یک کلاس حوزه (Scope) ویژه خود را خواهد داشت که جدای از دیگر اشیا آن کلاس خواهد بود از این رو نمونه های یک کلاس مستقل از یکدیگرند . به هنگام نمونه سازی از یک کلاس قراردادن جفت کمانک بازوبسته ()  پس از نام کلاس فراموش نشود.

حال که در مثال قبل ، کلاس دلخواه خود را ساخته ایم می توانیم از روی آن به تعداد مورد نیاز شی (object) بسازیم :

obj1= Rectangle ()

obj2= Rectangle ()

obj3= Rectangle ()

print(obj1.width)

الگوی دسترسی به هر یک از اعضای یک شی همانند زیر است :

نام صفت یا متد .  نام شی

مثال :

obj1.width

 

ساخت یک کلاس به معنای ایجاد یک نوع (Type) جدید در برنامه است که می توان چندین شی (object) یا نمونه (Instance) از آن پدید آورد. نام کلاس بیانگر نوع (type) نمونه ها یا همان اشیایی است که از روی آن ساخته می شوند. برای دست آوردن type یک شی از تابع type() استفاده می کنیم :

class TestClass:

    pass

class Rectangle:

    pass

obj1 = TestClass()

rect1 = Rectangle()

print(type(obj1))

#   <class '__main__.TestClass'>

print(type(rect1))

#  <class '__main__.Rectangle'>

 

کلاس ها نیز همانند توابع ، دامنه (scope) خود را دارند با تعریف هر کلاس یک حوزه محلی جدید در برنامه پایتون پدیدار می شود. به بیان دیگر هریک از اشیاء ساخته شده از یک کلاس دارای هویت (identity) مستقل از یکدیگر هستند و در مکانی جداگانه در حافظه قرارداده می شوند. تابع id() در پایتون برای نمایش شناسه یک شی بکاربرده می شود.

مثال: در مثال

 

class TestClass:

    pass

 

obj1 = TestClass()

obj2 = TestClass()

print(id(obj1))

print(id(obj2))

# output :

# 35888792

# 35888840

 

در زبان پایتون برای بررسی نوع (type) یک شی می توانید از دو روش زیر استفاده کنید :

1- بکار گیری  تابع type()

2- بکارگیری تابع isinstance()

تابع isinstance()  دارای دو پارامتر است ، نخستین پارامتر شی مورد نظر است و دومین پارامتر نوع مورد نظر می باشد. چنانچه شی ارسال شده به تابع از نوع دریافت شده باشد ، تابع مقدار True و گرنه مقدار False را برمی گرداند.

مثال :

class Rectangle:

    pass

num=255

rect1 = Rectangle()

print(isinstance(rect1,Rectangle))

print(isinstance(num,int))

سازنده (Constructor) در پایتون :

هنگام ساخت یک شی (ساخت یک نمونه جدید از کلاس) به صورت خودکارشگرد (Method) ویژه ای ازدرون کلاس فراخوانده می شود که به آن سازنده (Constructor) گفته می شود. فراخوانی خودکار این متد به برنامه نویس اجازه می دهد تا بتواند چگونگی ایجاد شی و مقداردهی اولیه به آن را انجام دهد. در فرآیند نمونه سازی از یک کلاس ، پایتون دو متد ویژه زیر را صدا می زند :

1- متد __new__()

2- متد __init__()

وظیفه متد __new__()  ساخت یک شی (نمونه) جدید از کلاس است و نخستین آرگومان ارسالی به آن نام کلاسی است که نمونه جدید باید از روی آن ساخته شود. به صورت خودکار و بی درنگ پس از اجرای متد __new__()  و پیش از آنکه شی جدید ساخته شده از متد  __new__()برگردانده شود متد ویژه دیگری به نام __init__() فراخوانی می شود . بنابراین نخستین پارامتر این متد شی جاری است یعنی همان شی که به دست متد __new__() ساخته شده است.  نام پیش فرض نخستین پارامتر این متد self است. اما می توانید به دلخواه نام دیگری برای آن برگزینید اما همواره به خاطر داشته باشید که این پارامترباید نخستین پارامتر تعریف شده دراین متد باشد. آرگومان همسان این پارامتر که همان شی جاری است به وسیله مفسر پایتون به این متد ارسال می شود.

براساس آنچه که تاکنون از فرآیند نمونه سازی در پایتون می دانید می توان گفت که متد وِیژه __init__()  سازنده (Constructor) کلاس نیست و در فرآیند نمونه سازی در زبان پایتون دو متد ویژه __new__  و __init__ و به همراه یکدیگر نقش سازنده را بازی می کنند. متد __new__ شی را می سازد و متد __init__ هم برای سفارشی کردن شی و دادن مقدار اولیه به آن بکار می رود.آرگومان های همسان با پارامترهای متد __init__ به جز self که به وسیله مفسر پایتون مقداردهی می گردد باید در زمان نمونه سازی کلاس و به درستی به این متد ارسال شوند

هر کلاس پایتون دارای یک پیاده سازی پیش فرض از دو متد __new__ و __init__ است. بنابراین در یک کلاس ساده نیازی به پیاده سازی دوباره آنها  وجود ندارد. در بسیاری از کلاس های بزرگ و پیچیده  نیازی به پیاده سازی متد __new__ احساس نمی شود اما پیاده سازی متد __init__  دراین گونه از کلاس ها ضرورتی گریزناپذیر است.

مثال :

 

class Rectangle:

   def  __init__( self,Length, Width)

             Self.Length= Length

             self. Width= Width

   

حال که کلاس دلخواه خود را به گونه ای ساخته ایم  که دارای یک سازنده است به روش زیر می توانیم از روی آن به تعداد مورد نیاز شی (object) بسازیم :

 

obj1= Rectangle(15,10)

print(obj1.width)

obj2= Rectangle(26,17)

print(obj2.width)

 

 

 

مثال : در مثال زیر برای کلاس TestClass هردو متد __new__ و __init__ را پیاده سازی کرده ایم برنامه را اجرا و خروجی آن را مشاهده کنید :

 

class TestClass:

    def __new__(cls,*args,**kwargs):

        print("Call of __new__() Method ")

        print("class name : " ,cls)

        print("Arbitrary Args : " ,args)

        print("Arbitrary Key Word Args : " , kwargs)

        obj = super().__new__(cls)

        return obj

    def __init__(self, name, family, age):

        print("Call of __init__() Method ")

        self.name = name

        self.family = family

        self.age = age

test1=TestClass("Omid","IranManesh",25)

 

صفت ها (Attributes) :

در دنیای شی گرایی دو نوع صفت (Attribute) وجود دارند :

1- صفت نمونه (Instance Attribute)

مقدار این نوع صفت ها از یک شی به شی دیگر متفاوت است. برای نمونه در مثال زیر مقدارهریک از  صفت های  name ، family و ident در دوشی prsn1 و prsn2  که هردو از روی یک کلاس به نام Person نمونه سازی شده اند می تواند متفاوت از یکدیگر باشد. به این گونه از صفت ها تنها با استفاده از نمونه (شی)  می توان دسترسی داشت و با استفاده از نام کلاس قابل دست یابی نیستند از این رو در مثال زیر افزودن دستور print(Person.name) به کد زیر کامپایلر پایتون خطای AttributeError: type object 'Person' has no attribute 'name' را اعلام خواهد کرد.

مثال :

class Person:

    def __init__(self, ident , name, family):

        self.ident = ident

        self.name = name

        self.family = family

prsn1 = Person(1020,"narges", "Iranmanesh")

prsn2 = Person(1030,"Ziba", "Bahary")

print(prsn1.name)

print(prsn2.name)

2- صفت کلاس (Class Attribute)

این صفت ها درون یک کلاس و بیرون از تمامی متدها تعریف می شوند. کاربرد این دسته از صفت ها به اشتراک گذاشتن یک متغیر در بین تمامی اشیای ساخته شده از روی کلاس است. به زبان ساده تر تمامی اشیای یک کلاس به class Attribute های آن دسترسی دارند. برای نمونه در مثال زیر مقدارهریک از  صفت های  uidو  objcount از نوع صفت های کلاس هستند.

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

print(Person.objcount)

print(prsn2.objcount)  

برای تغییر مقدار یک صفت کلاس در بیرون از کلاس ، از نام خود کلاس استفاده می شود. و در درون کلاس با استفاده از متد های کلاس (Class Method) صورت می پذیرد. برای نمونه در مثال زیر می توان نوشت :

Person.uid = "Bc45HKM"

 نکته بسیار مهمی که باید همواره به خاطرداشته باشید این است که برای تغییر مقدار یک صفت کلاس هرگز از شی استفاده نکنید چرا که این کار موجب ساخته شدن یک صفت شی (Instance Attribute) برای آن شی شده و تغییری در مقدار صفت کلاس ایجاد نخواهد کرد. همانگونه که در مثال زیر دیده می شود برای افزودن صفت به یک شی از متد __init__ استفاده می شود. و این یکی از کاربردهای اصلی متد __init__ در پایتون است.

مثال :

class Person:

    uid = "AB10RTGH"

    objcount=0

    def __init__(self, ident , name, family):

 

        self.ident = ident

        self.name = name

        self.family = family

    def __new__(cls, *args, **kwargs):

        cls.objcount += 1

        obj = super().__new__(cls)

        return obj

 

prsn1 = Person(1020,"narges", "Iranmanesh")

print(Person.objcount)

print(prsn1.objcount)

prsn2 = Person(1030,"Ziba", "Bahary")

print(Person.objcount)

print(prsn2.objcount)

print(Person.uid)

Person.uid = "Bc45HKM"

print(prsn1.uid)

print(Person.uid)

print(prsn2.uid)

شگردهای یک کلاس (Class Methods) :

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

1- شگردهای وابسته به شی (Instance Methods)

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

ObjectName.InstanceMethodName

 برای نمونه در مثال زیر شی rect1 از روی کلاس Rectangle ساخته می شود و در این کلاس شگرد شی به نام calc_erae() پیاده سازی شده است از این رو فراخوانی این متد به شکل زیر خواهد بود :

 rect1. calc_area()

 

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

مثال :

class Person:

    def Instance_method(self):

        pass

 

مثال : برای کلاس مستطیل ( Rectangle ) دو متد به نامهای محاسبه مساحت و محاسبه محیط تعریف می کنیم :

class Rectangle:

    def __init__(self,Length, Width):

           self.Length= Length

           self. Width= Width

    def calc_area(self) :

         return  self.Length * self.Width

    def calc_circum(self):

         return 2*( self.Length * self.Width)

rect1= Rectangle(22, 14)

print(rect1. calc_area())

print(rect1. calc_circum())

2- شگردهای کلاس (Class Methods)

چنانچه به هنگام پیاده سازی یک کلاس به خود کلاس یا صفت های ویژه کلاس (Class Attributes) نیاز داشتید باید این دسته از متدها را پیاده سازی کنید. این دسته از شگردها با رهنمون @classmethod به کامپایلر معرفی می شوند. در این دسته از شگردها همواره باید دست کم یک پارامتر تعریف شود. نام پیش فرض نخستین پارامتر این متد cls است. اما می توانید به دلخواه نام دیگری برای آن برگزینید اما همواره به خاطر داشته باشید که این پارامترباید نخستین پارامتر تعریف شده دراین متد باشد. آرگومان همسان این پارامتر که همان کلاس جاری است به وسیله مفسر پایتون به این دسته از متدها ارسال می شود و نیازی به ارسال آن از سوی برنامه نویس نیست.

class Person:

    @classmethod

    def class_method(cls):

        pass

 

این متد از اشیای ساخته شده از یک کلاس چیزی نمی داند و تنها کلاس را می شناسد بنابراین تنها می تواند Class Attribute ها را دستیابی و دستکاری کند. این دسته از متدها را می توان هم با استفاده از نام کلاس و هم با استفاده از اشیای ساخته شده از روی کلاس دستیابی کرد.

مثال :

class Student:

    univercity="Sharif"

    def __init__(self,stdid,name, family):

           self.stdid= stdid

           self. name= name

           self. family= family

      

@   classmethod     

    def set_name(cls,univercity_name) :

         cls.univercity = univercity_name

   

std1= Student(2014,"nazanin", "kashany")

print(Student.univercity)

print(std1.univercity)

Student.set_name("tehran")

std2 = Student(20115,"fereshteh", "kashany")

 

print(Student.univercity)

print(std1.univercity)

std2.set_name("Shiraz Univercity")

print(Student.univercity)

print(std2.univercity)

print(std1.univercity)

 

3- شگردهای ایستا (Static Methods)

یک شگرد ایستا نمی تواند به اعضای کلاس یا نمونه (شی) دسترسی داشته باشد چرا که نخستین پارامتر آن self یا cls نیست. به بیان دیگر این شگردها تنها با داده هایی که به صورت آرگومان به آنها ارسال می شود کار می کنند .  این شگردها هم با استفاده از نام کلاس و هم با استفاده از نام شی قابل دستیابی هستند. این دسته از شگردها با  استفاده از رهنمون @staticmethod ساخته می شوند :

مثال :

class Calculator:

@   staticmethod

    def Add(x, y) :

        return x + y

 

 

print(Calculator.Add(5,7))

calc2 = Calculator()

print(calc2.Add(5,7))

 

مثال :

class Person:

    @staticmethod

    def static_method():

        pass

مثال :

 

class Emploee:

    def __init__(self,name, salary, project_name):

           self. name= name

           self. salary= salary

           self.project_name = project_name

 

    def  work(self):

         task = self.gather_requirements(self.project_name)

         print("Completed :" ,task)

                 

@   staticmethod

    def gather_requirements(project_name):

        if project_name == "ABC":

             reuirement = "task1"

        else:

             reuirement = "task2"

        return  reuirement 

 

emp1= Emploee("Amir",5000000,"HTC")

emp1.work()

 

 

دستکاری اشیاء :

شما می توانید ویژگیهای یک شی را ویرایش کرده و یا حذف کنید.برای حذف یک ویژگی در شی ازدستور del استفاده می شود. همچنین می توانید با بکارگیری دستور del یک شی را حذف کنید

مثال : ویرایش ویژگی یک شی

rect2= Rectangle(22,14)

rect2. Length=36

rect2. Width =22

print(rect2. calc_area())

مثال : حذف ویژگی یک شی

del  rect2. Length

 

مثال : حذف یک شی

del  rect2

 

 

ارث بری در پایتون :

کلاسی است که می خواهیم کلاس های دیگر آن را به ارث ببرند را کلاس پایه Base Class)) یا ابر کلاس (Super Class)  می نامند و کلاس یا کلاس هایی که از کلاس پایه ارث می برند را زیر کلاس (Sub class) و یا کلاس مشتق شده (Derived class) می گویند.

در زبان پایتون نام کلاس پایه در کمانک بازوبسته پس از نام کلاس فرزند قرارمی گیرد ، چنانچه کلاس فرزند از بیش از یک کلاس پایه ارث بری کند کلاس های پایه با نویسه , از هم جدا می شوند :

class  BaseClass1:

    pass

class  BaseClass2:

    pass

class  BaseClass3:

    pass

class  ChildClass(BaseClass1, BaseClass2, BaseClass3):

    pass

 

برای آشنایی با روش پیاده سازی مفهوم وراثت در پایتون ابتدا کلاسی به نام افراد(persons) می سازیم که قرار است  به عنوان کلاس پدر برای کلاسی به نام کارمند (emploee) نقش بازی کند از این رو ابتدا کلاس پدر را می سازیم این کلاس دارای سه ویژگی به نام های : نام  (firstname) ، نام خانوادگی (lastname) و شناسه ملی (personid) است . همچنین دارای متدی به نام  getfullname  است که نام کامل فرد را برمی گرداند.

 

class persons:

       def __init__(self, name , family,identify):

              self.firstname = name

              self.lastname = family

              self.personid = identify

       def getfullname(self):

              return self.personid + ":" + self.firstname + " " + self.lastname

 

prsn1 = persons("Arman" , "Izanlou" , "0650041235")

print(prsn1.getfullname())

حال نوبت تعریف کلاس فرزند فرا رسیده است، کلاس فرزند  دلخواه ما کارمند (employee) نام دارد و دارای 7 ویژگی به نام های : نام  (firstname) ، نام خانوادگی (lastname) و شناسه ملی (personid) ، حقوق (salary) بخش (department) و نوع استخدام (emploeetype) است همچنین دارای متدی به نام   get_net_salary() که خالص حقوق کارمند را محاسبه می کند :

class emploee(persons):

      

       def __init__(self, name , family,identify,salary, department ,emploeetype):

              self.firstname = name

              self.lastname = family

              self.personid = identify

              self.salary = salary

              self.department = department

              self.emploeetype = emploeetype

        

       def get_net_salary(self, taxrate):

              return self.salary - ((taxrate * self.salary)/ 100)

 

emp=emploee("Arash" , "Izanlou" , "097571438", 190000000 , "it" , "Rasmi")

print(emp.getfullname())

print (emp.get_net_salary(15))

 

هنگامی که متد __init__()  را به کلاس فرزند اضافه می کنید کلاس فرزند دیگر متد __init__() کلاس پدر را به ارث نخواهد برد به بیان ساده تر سازنده کلاس فرزند سازنده کلاس پدر را لغو کرده و آن را نادیده می گیرد. از این رو چنانچه به هر دلیلی کلاس فرزند نیاز به ارث بری از متد سازنده کلاس پدر داشته باشد یعنی در کنارسازنده ویژه خود  نیاز به سازنده کلاس پدر نیزداشته می توانید به یکی از دو شیوه زیر این کار را انجام دهید :

1-  فراخوانی متد سازنده کلاس پدر در بدنه متد سازنده کلاس فرزند با استفاده از نام کلاس پدر

  این روش به ویژه زمانی مفید است که کلاس فرزند از بیش از یک کلاس پایه ارث بری داشته باشد. هنگامی که از این شیوه بهره می گیرید به یاد داشته باشید که نخستین پارامتر متد __init__() کلاس پدر یعنی self هم باید در فهرست آرگومان های ارسالی به آن وجود داشته باشد.

مثال :

persons.__init__(self,name , family,identify)

2- فراخوانی متد سازنده کلاس پدر در بدنه متد سازنده کلاس فرزند با استفاده از رهنمون super()

 هنگامی که از این شیوه بهره می گیرید به یاد داشته باشید که نخستین پارامتر متد __init__() کلاس پدر یعنی self نباید در فهرست آرگومان های ارسالی به آن وجود داشته باشد. وگرنه مفسرپایتون اعلام خطا خواهد کرد.

 

مثال :

 super().__init__(name , family,identify)

 

 

برای بیان بهتر آنچه که گفته شد مثال  بالا را با روش های گفته شده بازنویسی می کنیم :

class persons:

       def __init__(self, name , family,identify):

 

              self.firstname = name

              self.lastname = family

              self.personid = identify

       def getfullname(self):

              return self.personid + ":" + self.firstname + " " + self.lastname

 

prsn1 = persons("Arman" , "Izanlou" , "0650041235")

print(prsn1.getfullname())

 

class emploee(persons):

   def __init__(self, name , family,identify,salary, department ,emploeetype):

              super().__init__(name , family,identify)

                  # persons.__init__(self,name , family,identify)

 

              self.salary = salary

              self.department = department

              self.emploeetype = emploeetype

             

    def get_net_salary(self, taxrate):

 

              return self.salary - ((taxrate * self.salary)/ 100)

emp=emploee("Arash" , "Izanlou" , "097571438", 190000000 , "it" , "Rasmi")

print(emp.getfullname())

print (emp.get_net_salary(15))

 

 ارث بری از چند کلاس :

 مثال : کلاس دانشجوی دوره کارشناسی (bstudents) می تواند از دو کلاس افراد (persons) و کلاس دانشجویان (students) ارث بری کند با کلاس person پیشتر آشنا شده اید کلاس student دارای ویژگی های دوره تحصیلی (course) ، دانشگاه (univercity) و رشته تحصیلی (study) است و نیز متدی به نام get_course_info() است همچنین کلاس فرزند دارای ویژگی های کلاس persons و students بوده و افزون براین دو ویژگی دانشکده (college) و سال تحصیلی (academicyear) را نیز دارد

 

class persons:

       def __init__(self, name , family,identify):

 

              self.firstname = name

              self.lastname = family

              self.personid = identify

       def getfullname(self):

              return self.personid + ":" + self.firstname + " " + self.lastname

 

 

class students:

      

       def __init__(self,course, study,univercity):

             

              self.course = course

              self.study = study

              self.univercity = univercity

             

             

       def get_course_info(self):

 

              return str(self.course) + " " + self.study + " " + self.univercity 

 

 

 

class bstudent(persons, students):

 

      def __init__(self, name , family, identify, course, study,univercity, academicyear, college):

 

             

              persons.__init__(self, name , family,identify)

              students.__init__(self,course, study,univercity)

              self.academicyear=academicyear

              self.college=college

      

 

     

bstd1=bstudent("Arash", "izanlou", "0860781587", 4, "Computer Engineer", "Sharif" ,1400, "Fanni")

print(bstd1.get_course_info())

 

 

زمانی که یک کلاس از چندین کلاس دیگر ارث بری می کند مهم ترین چالش پیش رو چگونگی دستیابی به هریک از متدهای موجود در کلاس های پایه است. برای مثال اگر در تمامی کلاس های پایه متد __init__ پیاده سازی شده باشد با توجه به اینکه این متد در تمامی آنها دارای نامی یکسان است چگونه باید از مفسر پایتون بخواهیم تا آرگومان های دلخواه ما را از بین تمام کلاس های پایه به متد __init__ کلاس پایه مورد نظر ارسال کند. همانگونه که در بالا گفته شد ساده ترین شیوه چیره شدن بر این چالش بکارگیری نام کلاس پایه است در این روش برای تمامی پارامترهای متد مورد نظر در کلاس پایه باید آرگومان های همسان با آنها به متد کلاس فرزند ارسال شوند. از جمله برای پارامتر self که همواره نخستین پارامتراست.

مثال :

class BaseClass1:

    def  __init__(self, name):

        self.name = name

class BaseClass2:

    def  __init__(self, family):

        self.family = family

class BaseClass3:

    def  __init__(self, age):

          self.age = age

class ChildClass(BaseClass1, BaseClass2, BaseClass3):

    def  __init__(self, name, family, age ,salary):

         self.salary = salary

         BaseClass1.__init__(self, name)

         BaseClass2.__init__(self, family)

         BaseClass3.__init__(self, age)

    def printinfo(self):

          print(self.name + " " + self.family + "\n" + str(self.age) + "\n" + str(self.salary))

child1 = ChildClass("Arash", "Izanlou",25, 253000000)

child1.printinfo()    

روش دوم ، بکارگیری تابع super() است ، از این تابع در کلاس فرزند برای دستیابی به اعضای کلاس پایه استفاده می شود. این تابع برای پیمایش کلاس های پایه که کلاس فرزند جاری از آنها ارث بری می کند و یافتن متد مورد نظر در آنها از الگوریتمی به نام MRO

  ( Method Resolution Order) بهره می برد.

هرکلاس پایتون یک صفت ویژه به نام __mro__ دارد که دربردارنده ترتیب کلاس هایی است که پایتون براساس آن به دنبال یک متد می گردد. این ترتیب نتیجه بدست آمده از الگوریتم MRO است. برای نمونه در مثال بالا دستور print(ChildClass.__mro__)   نتیجه زیر را برمیگرداند :

(<class '__main__.ChildClass'>, <class '__main__.BaseClass1'>, <class '__main__.BaseClass2'>, <class '__main__.BaseClass3'>, <class 'object'>)

همانگونه که در نتیجه بدست آمده از دستور دیده می شود در این مثال مفسر پایتون برای جستجوی یک متد (در مثال ما متد __init__ ) ابتدا داخل خود کلاس ChildClass را بررسی می کند و سپس شروع به پیمایش کلاس های پایه آن  به ترتیب BaseClass1 ، BaseClass2 و BaseClass3 خواهد کرد. آخرین کلاسی که پیمایش خواهد شد همواره کلاس object است. این کلاس کلاسی است که تمامی کلاس های پایتون به صورت ضمنی از آن ارث بری می کنند.

با آگاهی از نتیجه الگوریتم MRO که همانا ترتیب پیمایش کلاس های پایه است متد مورد نظر ( برای مثال __init__) را به هنگام فراخوانی تابع Super() مقداردهی خواهیم کرد. یعنی ترتیب تعریف پارامترها و نیز ترتیب ارسال آرگومان ها به این متد در کلاس فرزند را به گونه ای انجام می دهیم که انگار ابتدا قرار است متد همسان آن در کلاس پایه مقداردهی گردد.

با توجه به آنکه مفسر پایتون با بدست آوردن نخستین نتیجه موفق از یافتن متد مورد نظر پایش را پایان می دهد از این رو نیاز است تا هریک از متدهای همسان با متد مورد نظر ما در کلاس های پایه نیز تابع super() را فراخوانی کنند. همچنین لازم است تا متدهای همسان در کلاس های پایه بگونه ای تعریف شده باشند که بتوانند هر تعداد پارامتر را بپذیرند از این رو آخرین پارامتر این متدها *args خواهد بود. این پارامتر تمامی آرگومان های اضافی ارسال شده به متد را در خود نگه می دارد. از این رو برای ادامه روند فراخوانی متدهای همسان باقی مانده تنها کافیست که این مقدار ارسال گردد.

مثال :

 

class BaseClass1:

    def  __init__(self, name, *args):

       

        self.name = name

        super().__init__(*args)

 

class BaseClass2:

    def  __init__(self, family,*args):

       

        self.family = family

        super().__init__(*args)

 

class BaseClass3:

    def  __init__(self, age,*args):

       

        self.age = age

        super().__init__(*args)

 

class ChildClass(BaseClass1, BaseClass2, BaseClass3):

 

    def  __init__(self, name, family, age ,salary):

       

        self.salary = salary

        super().__init__(name, family, age)

        

        

    def printinfo(self):

 

        print(self.name + " " + self.family + "\n" + str(self.age) + "\n" + str(self.salary))

 

child1 = ChildClass("Arash", "Izanlou",25, 253000000)

 

child1.printinfo()

 

 

نظرات 0 + ارسال نظر
امکان ثبت نظر جدید برای این مطلب وجود ندارد.