شی گرایی در پایتون

پایتون به طور کامل از object oriented پشتیبانی میکند. همه چیز در پایتون کلاس و یا شی ای از یک کلاس است. string, list, dictionary, tuple و .. همه و همه از جمله object هایی هستند که از کلاس های built-in پایتون نمونه سازی شده اند.کلاسکلاس در زبان های برنامه نویسی به منزله الگو یا pattern برای شی است. در حقیقت ساختار شی را چگونگی تعریف کلاس مشخص می کند. هر کلاس شامل مجموعه ای از رفتار ها ست که متد ها آن ها را پیاده سازی می کنند و مجموعه ای از خصوصیات.کلاس با استفاده از کلمه کلیدی class، نام کلاس و علامت : و سپس با indent در ادامه اعضای کلاس تعریف می شود:class <ClassName>: <statement1> <statement2> . . <statementN>اعضای تشکیل دهنده کلاس:ویژگی های کلاس (Class Attributes) متغیر هایی هستند که به طور مستقیم در کلاس تعریف می شوند و با اعضای کلاس به اشتراک گذاشته می شوند. به صورت instance_ name.variable_name یا class_name.variable_name می توان به آن ها دسترسی پیدا کرد.class Student: school_name = 'XYZ School'مقدار school_name برای همه نمونه ها یکسان خواهد ماند مگر اینکه صریحاً مقدار آن را در خود کلاس تغییر دهیم.>>> Student.school_name 'XYZ School' >>> std = Student() >>> std.school_name 'XYZ School'همانطور که مشخص است متغیر کلاس و متغیر های نمونه های ساخته شده از کلاس دقیقا یکی هستند.student1 = Student() student2 = Student() id(student1.school_name) == id(Student.school_name) == id (student2.school_name)برای تغییر مقدار متغیر های کلاس به صورت زیر عمل می کنیم (تغییر در نمونه تاثیری در مقدار متغیر کلاس ندارد).>>> Student.school_name = 'ABC School' >>> student1 = Student() >>> student1.school_name 'ABC School' >>> student1.school_name = 'My School' >>> student1.school_name 'My School' >>> Student.school_name 'ABC School' >>> student2 = Student() >>> student2.school_name 'ABC School'سازنده (Constructor)هرگاه یک شی از کلاس ساخته می شود، متد سازنده به طور خودکار صدا زده می شود(پرانتز جلوی نام کلاس constructor را فراخوانی می کند). در پایتون متدهای ()__init__ و ()__new__ به عنوان constructor شناخته می شود. ()__new__ هنگام ایجاد یک شی فراخوانی می شود و ()__init__ برای مقداردهی اولیه شی فراخوانی می شود، این متد با یک ورودی خاص به نام self است (self یک نام متعارف است ولی نامگذاری اولین پارامتر دلخواه است). متد سازنده برای تعریف attribute های یک نمونه و مقداردهی اولیه آنها استفاده می شود.class Student: def __init__(self): # constructor method print('Constructor invoked')با هر بار نمونه سازی از کلاس فوق تابع سازنده صدا زده می شود:>>>s1 = Student() Constructor invoked >>>s2 = Student() Constructor invokedویژگی های وهله ای (Instance Attributes)شامل attribute ها و property هایی هستند که به یک instance اضافه می شوند. این اعضا در متد سازنده کلاس اضافه می شوند. در مثال زیر name و age ویژگی هایی هستند که به کلاس اضافه شده اند.class Student: def __init__(self): # constructor self.name = &quot &quot # instance attribute self.age = 0 # instance attributeبرای دسترسی به instance attribute ها به صورت زیر عمل میکنیم:>>> std = Student() >>> std.name &quot&quot >>> std.age 0و برای مقدار دهی به آنها به صورت زیر عمل میکنیم:>>> std = Student() >>> std.name = &quotBill&quot # assign value to instance attribute >>> std.age=25 # assign value to instance attribute >>> std.name # access instance attribute value Bill >>> std.age # access value to instance attribute 25در صورتی که instance attribute ها را در متد سازنده مقدار دهی کنیم می توان نمونه سازی را به صورت زیر انجام داد:class Student: def __init__(self, name, age): self.name = name self.age = age>>> std = Student('Bill',25) >>> std.name 'Bill' >>> std.age 25پراپرتی ها (Properties)در پایتون property رابطی برای instance attribute ها فراهم می کند و آن ها را کپسوله می کند. property ها به دو صورت پیاده سازی می شوند: با استفاده از متد ()property و با استفاده از دکوراتور property@. هدف از تعریف یک property تغییر رفتار یک متد است، به نحوی که بتوان از آن مانند یک attribute استفاده کرد. encapsulate و اعتبار سنجی روی attribute ها از مهمترین دلایل تعریف یک property است.تعریف property با استفاده از متد ()proprty:class Student: def __init__(self): self.__name='' def setname(self, name): print('setname() called') self.__name=name def getname(self): print('getname() called') return self.__name name = property(getname, setname)در مثال بالا با استفاده از دو متد setname و getname یک property ایجاد کرده و آن را در متغیر name قرار دادیم. متد ()property متغیر name__ را به صورت private در آورده و برای دسترسی به آن باید از nameاستفاده کنیم که در واقع متد های getnam و setname را فراخوانی می کند.>>> std = Student() >>> std.name=&quotSteve&quot setname() called >>> std.name getname() called 'Steve'برای تعریف یک property بهتر است از دکوراتور property@ استفاده کنیم که یک دکوراتور برای متد ()property است و یک property را تعریف می کند. مثال زیر را در نظر بگیرید:class User: def __init__(self, age): self.age = age @property def age(self): return self._ageدر مثال بالا property@ تابع ()age را تعریف می کند که متغیر private به نام age__ را برمی گرداند. در این مرحله می توان از ()age به عنوان یک property استفاده کرد.class User: def __init__(self, age): self.__age = None self.age = age @property def age(self): return self.__age @age.setter def age(self, value): if value <= 0 or value > 99: self.__age = 0 print('Wrong age in input') return self.__age = value print(f'Age days is {self.__age * 365}')در این مرحله متد ()age را به گونه ای overload کردیم که یکی از آنها عملیات set کردن و دیگری عملیات get را انجام می دهد. این property سن کاربر را دریافت کرده و در صورتی که بین ۰ و ۹۹ سال باشد تعداد روزهای آن را باز می گرداند.متد ها (Methods)در هر کلاس می توان با استفاده از کلمه کلیدی def یک تابع ایجاد کرد. معمولا (به جز classmethod ها) متدها self را به عنوان اولین پارامتر در ورودی می پذیرند که این نام قراردادی است و استفاده از نام دلخواه نیز مجاز است، هرچند استاندارد نیست. self به نمونه ساخته شده از کلاس اشاره می کند. class Student: def __init__(self, name, age): self.name = name self.age = age def displayInfo(self): # class method print('Student Name: ', self.name,', Age: ', self.age)برای دسترسی به متد ها (instance method) ابتدا از کلاس instance گرفته و سپس به صورت instance_name.method_name به آن دسترسی خواهیم داشت.>>> std = Student('Steve', 25) >>> std.displayInfo() Student Name: Steve , Age: 25با استفاده از ()type می توان به نام کلاس از instance یک دسترسی پیدا کرد:>>> title = 'python' >>> print(type(title)) <class 'str'>کلاس ها با استفاده از pass می توانند هیچ عضوی نداشته باشد، مانند کلاس زیر:class Student: passکلمه کلیدی pass یک null operation است که وقتی که اجرا شود هیچ اتفاقی نمی افتد. در مواقعی کاربرد دارد که برای پرهیز از دریافت خطا نیاز باشد یک بلاک هیچ کاری نکند، مثلا هنوز پیاده سازی تکمیل نشده است . نمونه سازی (instantiation)برای نمونه سازی از کلاس تنها کافی است نام کلاس را شبیه به اجرای یک کلاس بنویسیم:student = Student()اگر در متد سازنده instance variable ست شده باشد آنگاه بایددر زمان نمونه سازی مقادیر مربوط به آن ها را در instance قرار داد:student = Student(instancevariable1, ... instance_variablen)برنامه نویسی شی گرا (Object Oriented Programming)شی گرایی یکی از روش های برنامه نویسی است که امکان استفاده مجدد (Code reusability) از کد ها را فراهم می کند و ساختار منسجم برنامه های ایجاد شده با این روش باعث محبوبیت در بین برنامه نویسان است.تکنیک های شی گرایی عبارت است از: inheritance, polymorphism, abstraction, ... .ارث بریبا فرض اطلاع از مفهوم ارث بری به سراغ پیاده سازی inheritance در پایتون می رویم:کلاس child از کلاس parent ارث بری می کند (inherit می شود) اگر داخل پرانتز بعد از نام کلاس child نام کلاس parent نوشته شود. به کلاس parent کلاس base یا پدر می گویند و به کلاسی را که ارث بری می کند کلاس فرزند گفته می شود.class Parent: statements class Child(Parent): statementsتمام مقادیر و متد های کلاس parent در کلاس child نیز در دسترس است و ارث بری این گونه موجب استفاده مجدد از کد ها می شود. class QuadriLateral: def __init__(self, a, b, c, d): self.side1=a self.side2=b self.side3=c self.side4=d def perimeter(self): p=self.side1 + self.side2 + self.side3 + self.side4 print(&quotperimeter=&quot,p)در کلاس QuadriLatral (چهار ضلعی) چهار ضلع را به عنوان instance variable تعریف کرده ایم و تابع perimeter مجموع ضلع های نمونه ساخته شده را برمی گرداند.>>> q1 = QuadriLateral(7, 5, 6, 4) >>> q1.perimeter() perimeter=22حال ارث بری را در کلاس جدید اعمال می کنیم:class Rectangle(QuadriLateral): def __init__(self, a, b): super().__init__(a, b, a, b)از آنجا که کلاس QuadriLateral در سازنده خود instance variable هایی را دریافت می کند، کلاس فرزند هم باید آن ها را به کلاس پدر ارسال کند، کلمه کلیدی super به کلاس پدر اشاره می کند. در مثال بالا کلاس فرزند instance variable ها را به سازنده کلاس پدر ارسال کرده است.>>> r1 = Rectangle(10, 20) >>> r1.perimeter() perimeter = 60همانطور که مشاهده می کنید کلاس فرزند متد perimeter را از کلاس پدر به ارث برده است.ارث بری چندگانهپایتون مانند ++C و برخلاف java و #C از ارث بری چندگانه (Multi Inheritance) پشتیبانی می کند. class Base1(object): def __init__(self): self.str1 = &quotGeek1&quot print(&quotBase1&quot) class Base2(object): def __init__(self): self.str2 = &quotGeek2&quot print(&quotBase2&quot) class Derived(Base1, Base2): def __init__(self): Base1.__init__(self) Base2.__init__(self) print(&quotDerived&quot) def printStrs(self): print(self.str1, self.str2) ob = Derived() ob.printStrs() خروجی:Base1 Base2 Derived Geek1 Geek2ارث بری چند سطحی (Multilevel inheritance)زمانی که ارث بری در چند مرحله و از کلاسی به کلاس دیگر انجام شود به آن Multilevel inheritance گفته می شود.class Base(object): def __init__(self, name): self.name = name def getName(self): return self.name class Child(Base): def __init__(self, name, age): Base.__init__(self, name) self.age = age def getAge(self): return self.age class GrandChild(Child): def __init__(self, name, age, address): Child.__init__(self, name, age) self.address = address def getAddress(self): return self.address g = GrandChild(&quotGeek1&quot, 23, &quotNoida&quot) print(g.getName(), g.getAge(), g.getAddress()) خروجی:Geek1 23 Noida()بازنویسی متد (Overriding in Python)در صورت لزوم می توان متد های کلاس پدر را در کلاس فرزند بازنویسی (override) کرد. instance به هر دو متد دسترسی خواهد داشت اما در زمان initialize متد override شده را فراخوانی می کند.class Rectangle(QuadriLateral): def __init__(self, a,b): super().__init__(a, b, a, b) def area(self): a = self.side1 * self.side2 print(&quotarea of rectangle=&quot, a) class Square(Rectangle): def __init__(self, a): super().__init__(a, a) def area(self): a = pow(self.side1, 2) print('Area of Square: ', a)آنجا همانطور که مشاهده می کنید متد override شده در کلاس Square (کلاس فرزند) اجرا خواهد شد.>>>s = Square(10) >>>s.area() Area of Square: 100چند ریختی (Polymorphism)چند ریختی یا polymorphism امکان تعریف متدهای همنام کلاس پدر در کلاس فرزند است. در حقیقت چند ریختی به معنی استفاده از توابع با نام های یکسان و فرم های متفاوت است.چند ریختی با استفاده از تابعدر این روش با استفاده از تابعی که در ورودی object دریافت می کند و متد مربوط به آن object را فراخوانی می کند polymorphism را پیاده سازی می کنند.class Bear(object): def sound(self): print(&quotGroarrr&quot) class Dog(object): def sound(self): print(&quotWoof woof!&quot) def make_sound(animalType): animalType.sound() bearObj = Bear() dogObj = Dog() make_sound(bearObj) make_sound(dogObj)در این مثال تابع makeSound در ورودی object کلاس را دریافت کرده و متد مربوط به آن را اجرا می کند.چند ریختی با استفاده از متد های کلاسclass Bird: def intro(self): print(&quotThere are different types of birds&quot) def flight(self): print(&quotMost of the birds can fly but some cannot&quot) class Parrot(Bird): def flight(self): print(&quotParrots can fly&quot) class Penguin(Bird): def flight(self): print(&quotPenguins do not fly&quot) obj_bird = Bird() obj_parr = Parrot() obj_peng = Penguin() obj_bird.intro() obj_bird.flight() obj_parr.intro() obj_parr.flight() obj_peng.intro() obj_peng.flight()همانگونه که در خروجی مشاهده می کنیم کلاس های Penguin و parrot متد flight را override کرده اند.There are different types of birds Most of the birds can fly but some cannot There are different types of bird Parrots can fly There are many types of birds Penguins do not flyچند ریختی با استفاده از abstract در این روش تمام متدهایی که کلاس فرزند باید پیاده سازی کند را در یک کلاس abstract قرار داده و بدنه متد ها در کلاس abstract شامل NotImplementedErro باشد. در این صورت کلاس فرزند موظف است متدهای کلاس پدر را پیاده سازی کند و هر کلاس آن گونه که لازم است پیاده سازی را انجام می دهد.class Document: def __init__(self, name): self.name = name def show(self): raise NotImplementedError(&quotSubclass must implement abstract method&quot) class Pdf(Document): def show(self): return 'Show pdf contents!' class Word(Document): def show(self): return 'Show word contents!' documents = [Pdf('Document1'), Pdf('Document2'), Word('Document3')] for document in documents: print(document.name + ': ' + document.show())خروجیDocument1: Show pdf contents! Document2: Show pdf contents! Document3: Show word contents!رابط کاربری (Interface)استفاده مهم interface در زبان هایی که single inheritance هستند به این دلیل است که امکان ارث بری یک کلاس از چندین کلاس وجود ندارد ولی ارث بری از چند interface ممکن است. در پایتون به دلیل پشتیبانی از multi inheritance نیاز به interface ضروری به نظر نمی رسد. اانتزاعی (Abstraction)کلاسی که شامل یک یا چند متد abstract باشد را کلاس abstract می گویند. یک متد abstract متدی است که اعلان (declaration) می شود ولی شامل بدنه پیاده سازی شده نیست. کلاس abstract معمولا به عنوان base کلاس مورد استفاده قرار می گیرند. abstract ها زمانی استفاده می شوند که بخواهیم برای یک متد چندین پیاده سازی مختلف انجام دهیم یا کلاس هایی وجود دارند که متد خاصی را به شیوه های مختلف پیاده سازی می کنند.برای ایجاد کلاس abstract در پایتون از کتابخانه abc استفاده می کنیم:from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def move(self): pass class Human(Animal): def move(self): print(&quotI can walk and run&quot) class Snake(Animal): def move(self): print(&quotI can crawl&quot) class Dog(Animal): def move(self): print(&quotI can bark&quot) class Lion(Animal): def move(self): print(&quotI can roar&quot) dog = Dog() dog.move()خروجیI can barkدر صورتی که از یک کلاس abstract نمونه سازی کنیم با خطای زیر مواجه می شویم:>>> animal = Animal() Traceback (most recent call last): File &quot/home/ffe4267d930f204512b7f501bb1bc489.py&quot, line 19, in c=Animal() TypeError: Can't instantiate abstract class Animal with abstract methods moveبا استفاده از دکوراتورabc.abstractproperty@می توان property های abstract تعریف کردimport abc from abc import ABC, abstractmethod class parent(ABC): @abc.abstractproperty def geeks(self): return &quotparent class&quot class child(parent): @property def geeks(self): return &quotchild class&quot try: r = parent() print(r.geeks) except Exception as err: print (err) r = child() print (r.geeks) کلاس abstract را با استفاده از NotImplemetError می توان به صورت ضمنی پیاده سازی کرد به این صورت که متدهایی که در باید کلاس فرزند پیاده سازی شوند را در کلاس پدر اعلان کرده و کلاس اکسپشن NotImplementedError را raise می کنیم. در این صورت کلاس هایی که از کلاس پدر ارث بری می کنند چنانچه متدها را پیاده سازی نکنن با خطا مواجه خواهند شد.class Document: def __init__(self, name): self.name = name def show(self): raise NotImplementedError(&quotThis method is not impelemented&quot) class PDF(Document): def show(self): print(&quotPDF Document has been shown&quot)در صورت فراخوانی تابع show از کلاس Document با خطای زیر مواجه می شوویم:document = Document('name') document.show()NotImplementedError: This method is not impelemented