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

پایتون به طور کامل از object oriented پشتیبانی میکند. همه چیز در پایتون کلاس و یا شی ای از یک کلاس است. string, list, dictionary, tuple و .. همه و همه از جمله object هایی هستند که از کلاس های built-in پایتون نمونه سازی شده اند.

کلاس

کلاس در زبان های برنامه نویسی به منزله الگو یا pattern برای شی است. در حقیقت ساختار شی را چگونگی تعریف کلاس مشخص می کند. هر کلاس شامل مجموعه ای از رفتار ها ست که متد ها آن ها را پیاده سازی می کنند و مجموعه ای از خصوصیات.

کلاس با استفاده از کلمه کلیدی class، نام کلاس و علامت : و سپس با indent در ادامه اعضای کلاس تعریف می شود:

123456class <ClassName>:
    <statement1>
    <statement2>
    .
    .
    <statementN>

اعضای تشکیل دهنده کلاس:

  • ویژگی های کلاس (Class Attributes)

متغیر هایی هستند که به طور مستقیم در کلاس تعریف می شوند و با اعضای کلاس به اشتراک گذاشته می شوند. به صورت instance_ name.variable_name یا class_name.variable_name می توان به آن ها دسترسی پیدا کرد.

12class Student:
    school_name = 'XYZ School'

مقدار school_name برای همه نمونه ها یکسان خواهد ماند مگر اینکه صریحاً مقدار آن را در خود کلاس تغییر دهیم.

12345>>> Student.school_name 
'XYZ School' 
>>> std = Student()
>>> std.school_name 
'XYZ School'

همانطور که مشخص است متغیر کلاس و متغیر های نمونه های ساخته شده از کلاس دقیقا یکی هستند.

123student1 = Student()
student2 = Student()
id(student1.school_name) == id(Student.school_name) == id (student2.school_name)

برای تغییر مقدار متغیر های کلاس به صورت زیر عمل می کنیم (تغییر در نمونه تاثیری در مقدار متغیر کلاس ندارد).

123456789101112>>> 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 های یک نمونه و مقداردهی اولیه آنها استفاده می شود.

123class Student:
    def __init__(self): # constructor method
        print('Constructor invoked')

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

1234>>>s1 = Student()
Constructor invoked
>>>s2 = Student()
Constructor invoked
  • ویژگی های وهله ای (Instance Attributes)

شامل attribute ها و property هایی هستند که به یک instance اضافه می شوند. این اعضا در متد سازنده کلاس اضافه می شوند. در مثال زیر name و age ویژگی هایی هستند که به کلاس اضافه شده اند.

12345class Student:

    def __init__(self): # constructor
        self.name = " " # instance attribute
        self.age = 0 # instance attribute

برای دسترسی به instance attribute ها به صورت زیر عمل میکنیم:

12345>>> std = Student()
>>> std.name
""
>>> std.age
0

و برای مقدار دهی به آنها به صورت زیر عمل میکنیم:

1234567>>> std = Student()
>>> std.name = "Bill"  # 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 ها را در متد سازنده مقدار دهی کنیم می توان نمونه سازی را به صورت زیر انجام داد:

1234class Student:
    def __init__(self, name, age): 
        self.name = name
        self.age = age

12345>>> std = Student('Bill',25)
>>> std.name
'Bill'
>>> std.age
25
  • پراپرتی ها (Properties)

در پایتون property رابطی برای instance attribute ها فراهم می کند و آن ها را کپسوله می کند. property ها به دو صورت پیاده سازی می شوند: با استفاده از متد ()property و با استفاده از دکوراتور [email protected]

هدف از تعریف یک property تغییر رفتار یک متد است، به نحوی که بتوان از آن مانند یک attribute استفاده کرد. encapsulate و اعتبار سنجی روی attribute ها از مهمترین دلایل تعریف یک property است.

تعریف property با استفاده از متد ()proprty:

12345678910class 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 را فراخوانی می کند.

123456>>> std = Student()
>>> std.name="Steve"
setname() called
>>> std.name
getname() called
'Steve'

برای تعریف یک property بهتر است از دکوراتور [email protected] استفاده کنیم که یک دکوراتور برای متد ()property است و یک property را تعریف می کند.

مثال زیر را در نظر بگیرید:

1234567class User:
    def __init__(self, age):
        self.age = age
 
    @property
    def age(self):
        return self._age

در مثال بالا [email protected] تابع ()age را تعریف می کند که متغیر private به نام age__ را برمی گرداند. در این مرحله می توان از ()age به عنوان یک property استفاده کرد.

1234567891011121314151617class 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 به نمونه ساخته شده از کلاس اشاره می کند.

123456class 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 به آن دسترسی خواهیم داشت.

123>>> std = Student('Steve', 25)
>>> std.displayInfo()
Student Name: Steve , Age: 25

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

123>>> title = 'python'
>>> print(type(title))
<class 'str'>

کلاس ها با استفاده از pass می توانند هیچ عضوی نداشته باشد، مانند کلاس زیر:

12class Student:
    pass

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

نمونه سازی (instantiation)

برای نمونه سازی از کلاس تنها کافی است نام کلاس را شبیه به اجرای یک کلاس بنویسیم:

1student = Student()

اگر در متد سازنده instance variable ست شده باشد آنگاه بایددر زمان نمونه سازی مقادیر مربوط به آن ها را در instance قرار داد:

1student = Student(instancevariable1, ... instance_variablen)

برنامه نویسی شی گرا (Object Oriented Programming)

شی گرایی یکی از روش های برنامه نویسی است که امکان استفاده مجدد (Code reusability) از کد ها را فراهم می کند و ساختار منسجم برنامه های ایجاد شده با این روش باعث محبوبیت در بین برنامه نویسان است.

تکنیک های شی گرایی عبارت است از: inheritance, polymorphism, abstraction, … .

ارث بری

با فرض اطلاع از مفهوم ارث بری به سراغ پیاده سازی inheritance در پایتون می رویم:

کلاس child از کلاس parent ارث بری می کند (inherit می شود) اگر داخل پرانتز بعد از نام کلاس child نام کلاس parent نوشته شود. به کلاس parent کلاس base یا پدر می گویند و به کلاسی را که ارث بری می کند کلاس فرزند گفته می شود.

12345class Parent:
    statements
                    
class Child(Parent):
    statements

تمام مقادیر و متد های کلاس parent در کلاس child نیز در دسترس است و ارث بری این گونه موجب استفاده مجدد از کد ها می شود.

12345678910class 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("perimeter=",p)

در کلاس QuadriLatral (چهار ضلعی) چهار ضلع را به عنوان instance variable تعریف کرده ایم و تابع perimeter مجموع ضلع های نمونه ساخته شده را برمی گرداند.

123>>> q1 = QuadriLateral(7, 5, 6, 4)
>>> q1.perimeter()
perimeter=22

حال ارث بری را در کلاس جدید اعمال می کنیم:

123class Rectangle(QuadriLateral):
    def __init__(self, a, b):
        super().__init__(a, b, a, b)

از آنجا که کلاس QuadriLateral در سازنده خود instance variable هایی را دریافت می کند، کلاس فرزند هم باید آن ها را به کلاس پدر ارسال کند، کلمه کلیدی super به کلاس پدر اشاره می کند. در مثال بالا کلاس فرزند instance variable ها را به سازنده کلاس پدر ارسال کرده است.

123>>> r1 = Rectangle(10, 20)
>>> r1.perimeter()
perimeter = 60

همانطور که مشاهده می کنید کلاس فرزند متد perimeter را از کلاس پدر به ارث برده است.

ارث بری چندگانه

پایتون مانند ++C و برخلاف java و #C از ارث بری چندگانه (Multi Inheritance) پشتیبانی می کند.

1234567891011121314151617181920class Base1(object): 
    def __init__(self):
        self.str1 = "Geek1"
        print("Base1") 

class Base2(object): 
    def __init__(self): 
        self.str2 = "Geek2"		
        print("Base2") 

class Derived(Base1, Base2): 
    def __init__(self): 
        Base1.__init__(self) 
        Base2.__init__(self) 
        print("Derived") 
    def printStrs(self): 
        print(self.str1, self.str2) 

ob = Derived() 
ob.printStrs() 

خروجی:

1234Base1
Base2
Derived
Geek1 Geek2

ارث بری چند سطحی (Multilevel inheritance)

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

12345678910111213class 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
 
12345678910class GrandChild(Child):
    def __init__(self, name, age, address): 
        Child.__init__(self, name, age) 
        self.address = address 

    def getAddress(self): 
         return self.address	

g = GrandChild("Geek1", 23, "Noida") 
print(g.getName(), g.getAge(), g.getAddress()) 

خروجی:

1Geek1 23 Noida()

بازنویسی متد (Overriding in Python)

در صورت لزوم می توان متد های کلاس پدر را در کلاس فرزند بازنویسی (override) کرد. instance به هر دو متد دسترسی خواهد داشت اما در زمان initialize متد override شده را فراخوانی می کند.

12345678class Rectangle(QuadriLateral):
    def __init__(self, a,b):
        super().__init__(a, b, a, b)

    def area(self):
        a = self.side1 * self.side2
        print("area of rectangle=", a)

123456class 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 (کلاس فرزند) اجرا خواهد شد.

123>>>s = Square(10)
>>>s.area()
Area of Square: 100

چند ریختی (Polymorphism)

چند ریختی یا polymorphism امکان تعریف متدهای همنام کلاس پدر در کلاس فرزند است. در حقیقت چند ریختی به معنی استفاده از توابع با نام های یکسان و فرم های متفاوت است.

  • چند ریختی با استفاده از تابع

در این روش با استفاده از تابعی که در ورودی object دریافت می کند و متد مربوط به آن object را فراخوانی می کند polymorphism را پیاده سازی می کنند.

1234567891011121314151617class Bear(object):
    def sound(self):
        print("Groarrr")
    
class Dog(object):
    def sound(self):
        print("Woof woof!")
    
def make_sound(animalType):
    animalType.sound()
    

bearObj = Bear()
dogObj = Dog()
    
make_sound(bearObj)
make_sound(dogObj)

در این مثال تابع makeSound در ورودی object کلاس را دریافت کرده و متد مربوط به آن را اجرا می کند.

  • چند ریختی با استفاده از متد های کلاس
1234567891011121314151617181920212223242526class Bird:
    def intro(self):
        print("There are different types of birds")
    def flight(self):
        print("Most of the birds can fly but some cannot")

class Parrot(Bird):
    def flight(self):
        print("Parrots can fly")

class Penguin(Bird):
    def flight(self):
        print("Penguins do not fly")


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 کرده اند.

123456There 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 باشد. در این صورت کلاس فرزند موظف است متدهای کلاس پدر را پیاده سازی کند و هر کلاس آن گونه که لازم است پیاده سازی را انجام می دهد.

123456789101112131415161718192021class Document:
    def __init__(self, name):
        self.name = name
    
    def show(self):
        raise NotImplementedError("Subclass must implement abstract method")
    
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())

خروجی

123Document1: 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 استفاده می کنیم:

12345678910111213141516171819202122232425from abc import ABC, abstractmethod 

class Animal(ABC): 
    @abstractmethod
    def move(self): 
        pass

class Human(Animal): 
    def move(self): 
        print("I can walk and run") 

class Snake(Animal): 
    def move(self): 
        print("I can crawl") 

class Dog(Animal): 
    def move(self): 
        print("I can bark") 

class Lion(Animal): 
    def move(self): 
        print("I can roar") 

dog = Dog() 
dog.move()

خروجی

1I can bark

در صورتی که از یک کلاس abstract نمونه سازی کنیم با خطای زیر مواجه می شویم:

12345>>> animal = Animal()
Traceback (most recent call last):
  File "/home/ffe4267d930f204512b7f501bb1bc489.py", line 19, in 
    c=Animal()
TypeError: Can't instantiate abstract class Animal with abstract methods move

با استفاده از دکوراتور[email protected]می توان property های abstract تعریف کرد

123456789101112131415161718192021import abc 
from abc import ABC, abstractmethod 

class parent(ABC): 
    @abc.abstractproperty 
    def geeks(self): 
        return "parent class"

class child(parent): 
    @property
    def geeks(self): 
        return "child class"

try: 
    r = parent() 
    print(r.geeks) 

except Exception as err: 
    print (err) 
    r = child() 
    print (r.geeks) 

کلاس abstract را با استفاده از NotImplemetError می توان به صورت ضمنی پیاده سازی کرد به این صورت که متدهایی که در باید کلاس فرزند پیاده سازی شوند را در کلاس پدر اعلان کرده و کلاس اکسپشن NotImplementedError را raise می کنیم. در این صورت کلاس هایی که از کلاس پدر ارث بری می کنند چنانچه متدها را پیاده سازی نکنن با خطا مواجه خواهند شد.

123456789class Document:
    def __init__(self, name):
        self.name = name
    def show(self):
        raise NotImplementedError("This method is not impelemented")

class PDF(Document):
    def show(self):
        print("PDF Document has been shown")

در صورت فراخوانی تابع show از کلاس Document با خطای زیر مواجه می شوویم:

12document = Document('name')
document.show()

1NotImplementedError: This method is not impelemented

نویسنده مطلب: مهدی نظری

منبع مطلب

به فکر سرمایه‌گذاری هستی؟

با هر سطحی از دانش در سریع‌ترین زمان با آموزش گام به گام، سرمایه گذاری را تجربه کن. همین الان میتونی با لینک زیر ثبت نام کنی و ۱۰ درصد تخفیف در کارمزد معاملاتی داشته باشی

ثبت نام و دریافت جایزه
ممکن است شما بپسندید
نظر شما درباره این مطلب

آدرس ایمیل شما منتشر نخواهد شد.