۱۲/۰۶/۱۳۸۸

برنامه نویسی شیء گرا - قسمت پنجم

در این قسمت نوع خاصی از متودها در پایتون به نام classmethod ها را تعریف میکنم. منبع اصلی این قسمت با کلیک کردن روی عنوان پست قابل دسترس است و من اینجا تقریبا مطالب آن را ترجمه میکنم. (با کمی تصرف!)

ابتدا مفهوم وسیله ی نقلیه را تعریف میکنیم. با ارث بردن از آن مفاهیم موتور، اتومبیل و کامیون را تعریف میکنیم. خوب، حالا که چی؟! :دی
میخواهیم تعداد موتورها، اتومبیل ها، کامیونها و همچنین تعداد کل وسایل نقلیه را به راحتی و تنها با یک متغیر total در اختیار داشته باشیم.
تعداد هر نوع وسیله را با استفاده از متود set_total تعیین میکنیم و برای نمایش آن از متود print_total استفاده میکنیم.


#an example of an old-style class
class Vehicle:
    '''
    represents a vehicle.
    total is the
number
    of all vehicles
    '''
    total=0

    @classmethod
    def print_total(cls):
        '''
        prints total of cls kind vehicles!
        e.g:
        >>> Bike.set_total(3)
        >>> Bike.print_total()
        3
        '''
        print cls.total

    @classmethod
    def set_total(cls, value):
        '''
        sets total of cls kind vehicles!
        e.g:
        >>> Bike.set_total(3)
        '''
        cls.total = value
        #updating the total
        #number of all vehicles
        Vehicle.total+=value

    @staticmethod
    def vehicle_total():
        '''prints the total
        number of all vehicles'''
        print Vehicle.total

#using 'pass' keyword, leaving
#the class with no more definition
class Bike(Vehicle): pass

class Car(Vehicle): pass

class Truck(Vehicle): pass


خوب، همانطور که دیدید برای تعریف یک classmethod مثل تعریف staticmethod از دکوراتورها استفاده کردیم.  
دقت کنید که اولین ورودی هر classmethod را cls نامگذاری کردیم (مانند self این هم لزومی ندارد cls نامگذاری شود ولی معمولا این اسم را برمیگزینند! :دی)
این ورودی همانند self هنگام صدازدن متود مقدار دهی نمیشود اما فرقش با self این است که self نمونه ای از کلاس است که تابع را صدا میزند اما cls خود کلاسی (و نه نمونه ی آن) است که تابع را فرامیخواند، حال چه از طریق یک نمونه ی آن صدازده شود چه مستقیم از خود کلاس (شبیه staticmethod)


>>> Bike.set_total(3)   # Bike.total = 3
>>> Car.set_total(5)    # Car.total = 5
>>> Truck.set_total(2)  # Truck.total = 2
>>>
>>> b = Bike()
>>> c = Car()
>>> t = Truck()
>>>
>>> b.print_total(); c.print_total(); t.print_total()
3
5
2
>>> #
following results are just the same as above
...
... Bike.print_total(); Car.print_total(); Truck.print_total()
3
5
2
>>> 
>>> Vehicle.total #total of vehicles
10
>>> b.vehicle_total()
#total of vehicles
10
>>> Car.vehicle_total()
#total of vehicles
10
>>> Car.total #total of cars
5
>>>

همانطور که دیدید، همانند یک staticmethod فرقی نمیکند که یک classmethod را از نمونه ای از کلاس یا خود آن فرا بخوانیم.
توجه کنید که تفاوت staticmethod با classmethod در این است که اولی به طور کلی مستقل از خود کلاس است و تنها درون آن تعریف شده( به عبارت دیگر تفاوت آن با متودی همنام در بیرون از کلاس در namespace آنها است) اما دومی تنها مستقل از نمونه های یک کلاس است و نه خود آن.
 
 
با کمی ور رفتن با این مفاهیم بهتر میفهمیدشون :دی
برای اطلاعات بیشتر درباره ی classmethod به سایت رسمی پایتون اینجا یا اینجا مراجعه کنید. هر سوالی هم داشتید میتوانید به صورت پاسخ به همین پست بپرسید.
 
موفق باشید ☺

۱۲/۰۲/۱۳۸۸

برنامه نویسی شیء گرا - قسمت چهارم > وراثت


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

در قسمت اول، مفهوم نقطه در صفحه را تعریف کردیم. با استفاده از آن به راحتی میتوان مفهوم نقطه در فضا را هم تعریف کرد. دقت کنید

class Point(object):
   
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __str__(self):
        return str(self.x)+' '+str(self.y)


#instead on inheriting object
#this time I inherit Point class
class Point3D(Point):
    '''
    represents a point in the space
    inherits Point class
    '''

    #overriding constructor method
    def __init__(self,x,y,z):
        #calling constructor of supper class
        #from this instance (self)
        super(Point3D,self).__init__(x,y)
        self.z=z

    #overriding __str__ method
    def __str__(self):

        #calling __str__ method of
        #the base class (Point)
        #it will return str(self.x)+' '+str(self.y)
        XandY=Point.__str__(self)

        return XandY+' '+str(self.z)

    #this is a new method I add for
Point3D
    def vector_len(self):
        '''
        returns the length of the vector
        which represents this point
        '''
        from math import sqrt
        x,y,z=self.x,self.y,self.z
        return sqrt(x**2+y**2+z**2)


ابتدا بجای object نام کلاسی که از آن ارث میبریم را مینویسیم. با همین کار ساده میتوانیم از تمامی ویژگیها و متودهای کلاس پایه (base class) در این کلاس استفاده کنیم. (به کلاس پایه superclass نیز میگویند)
در مواقعی خاص برای صدا زدن متودهای کلاس پایه از تابع super استفاده میکنیم. این تابع همواره دو ورودی میگیرد که اولی نام کلاس و دومی نمونه ای از کلاس است که تابع را صدا میزند (همان self)
اگر متودی همنام با یکی از متودهای کلاس پایه تعریف کنیم جایگزین آن در این کلاس میشود. یعنی اگر از این کلاس صدا زده شود متود جدید فراخوانده میشود و نه متود همنام آن در کلاس پایه! به این کار override کردن یک متود میگویند. در مثال بالا تابع سازنده و تابع __str__ "اوور راید" شده اند.



>>> p=Point3D(1,2,3)
>>> print p
1 2 3
>>> p.x
1
>>> p.z
3
>>> p1=Point(4,5)
>>> p1.x
4
>>> print p1
4 5
>>> p.vector_len()
3.7416573867739413
>>> Point3D.__doc__
'\n    represents a point in the space\n    inherits Point class\n    '





یک مثال هم از اشیاء:

در این مثال از ماژول turtle استفاده میشود که خارج از بحث این قسمت است. برای آشنایی با این ماژول به اینجا و برای آشنایی بیشتر به اینجا مراجعه کنید.

میخواهیم مفهوم توپ در صفحه را تعریف کنیم. برای اینکار به سادگی میتوان توپ را حالت خاصی از یک نمونه از کلاس turtle.Turtle در نظر گرفت که به شکل توپ(دایروی) درآمده است. در واقع برای تعریف توپ این کلاس را به ارث میبریم.

#importing Turtle class
from turtle import Turtle

#inheriting from Turtle class
class Ball(Turtle):
    '''represents a 2D ball on the screen'''

    #overriding Turtle's constructor method
    def __init__(self,radius,color='black'):
        '''
        radius is the radius of the ball in pixels
        color is name of the color of the ball
        (optional argument)
        '''
        #first we have to initialize the associated
        #Turtle instance for this ball
        super(Ball,self).__init__()
        #the above line is nearly the
        #same as Turtle.__init__(self)


        #2D balls are circle shaped :D
        self.shape('circle')

        #initializing size and color attributes
       
        #this float division here is because
        #turtlesize is nearly one tenth of
        #its real value in pixels
        self.turtlesize(radius/10.)
        self.color(color)

        #I want my ball to leave

        #no trace on the screen
        self.penup()

    def move(self,teta=0,step=10):
        '''
        moves the ball 'step' points on 
        'teta' direction
        (both arguments are optional)
        '''
        #setting the angle of the ball to teta
        self.setheading(teta)
        #forwarding step points on

        #the new direction (teta)
        self.forward(step)



در مثال بالا توپ بسیار ساده تعریف شد و تنها یک متود برای حرکت آن تعریف کردیم. البته تمامی توابع کلاس Turtle نیز برای این توپ قابل استفاده هستند. (به ارث برده شده اند)

>>>
>>> myball=Ball(20,'blue')
>>> myball.move()
>>> myball.move(teta=45)
>>> myball.move(step=100,teta=60)
>>> myball.speed('lowest')
>>> myball.forward(-100)
>>> myball.speed(1)
>>> myball.move(45,45)
>>> myball.position()
(48.89,38.89)
>>> myball.circle(30)
>>>




در قسمتهای بعدی بیشتر درمورد وراثت در پایتون توضیح خواهم داد. برای اطلاعات بیشتر در این مورد به فارسی میتوانید به اینجا یا اینجا و به انگلیسی به اینجا مراجعه کنید.
در ضمن اطلاعات بیشتر درمورد متود super در سایت رسمی پایتون اینجا موجود است.

موفق باشید ☺

۱۱/۲۵/۱۳۸۸

برنامه نویسی شیء گرا - قسمت سوم

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

class MyMath(object):

    @staticmethod
    def fac(n):
        '''factorial of a number (a recursive method)'''
        return 1 if not n else n*MyMath.fac(n-1)


>>> MyMath.fac(5)
120
>>> aInstance=MyMath()
>>> aInstance.fac(5)
120
>>> 


همونطور که دیدیداصلا نیازی به نمونه گرفتن از کلاس نبود.

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

class Timer():
    '''
    An easy to use timer which

    reminds me of a VB8 stopwatch :D
    It uses python time module
    '''
    #double underlines __ here make
    #__time a private  attribute
    __time=0

    #making reset a static method, there is
    #no more need for a self argument.

    #instead reset=staticmethod(reset)
    #I use a decorator
    @staticmethod
    def reset():
        '''

        updates the starting time
        with local time
        '''
        from time import time
        Timer.__time=time()



    @staticmethod
    def elapsed():
        '''

        elapsed seconds since last reset() call
        '''
        from time import time
        return time()-Timer.__time

 
حالا من این کلاس را در فایل myassets.py و در این آدرس "E:\My Projects" ذخیره میکنم. 
حالا اگر بخواهیم در هر برنامه ای که در همین آدرس ذخیره شده از این کلاس استفاده کنیم کافیست بنویسیم


from myassets import Timer

Timer.reset()
# a block of code

#
print Timer.elapsed(), 'Seconds!'


اما اگر برنامه در آدرس دیگری ذخیره شده باشد مینویسیم
 
import sys
sys.path.append('E:\My Projects')
#now we can import any module from this address

#the rest is just the same

#instead of importing only Timer class
#I import everything that can be imported!
#into this module using *
from myassets import *

Timer.reset()
# a block of code
#
print Timer.elapsed(), 'Seconds!'


 برای اطلاعات بیشتر در مورد ماژول sys به اینجا و در مورد ماژول time به اینجا مراجعه کنید.

موفق باشید ☻ 

 

۱۱/۲۲/۱۳۸۸

برنامه نویسی شیء گرا - قسمت دوم

 بعضي از اشياء ويژگيهايي دارند که فقط حالات خاصي رو ميپذيرند. مثلا شعاع یک توپ نمیتواند 0 يا منفي باشد. ميتوانيم اين محدوديتها را براي هر ويژگي اي  که لازم دارد اعمال کنيم. با همين مثال توپ شروع ميکنيم. اول شعاع توپ را طوري تعريف ميکنيم که فقط موقع تعريف توپ مقدار دهی شود و بعد غير قابل تغيير باشد.
class Ball(object):
    '''represents a ball object'''
  
    def __init__(self,radius):
        #we make radius a private attribute by
        #adding __ at its beginning
        self.__radius=radius
   

    #we call this a getter method for __radius
    def get_radius(self)
        return self.__radius


    #we
make a property read-only by only defining 
    #a getter method for it
    #I also add a docstring for this property
    radius=property(get_radius,

                    doc='''radius of the ball''')
 


>>> myball=Ball(5)
>>> myball.radius
5
>>> myball.radius=6

Traceback (most recent call last):
  File "", line 1, in
    myball.radius=6
AttributeError: can't set attribute
>>>
>>> myball.radius
5
 ما الان یک ویژگی غیر قابل تغییر تعریف کردیم(در اصطلاح یک "ریدونلی پراپرتی")
حالا قابلیت تغییر دادن هم اضافه می کنیم با این محدودیت که شعاع توپ باید مثبت باشد

class Ball(object):

    def __init__(self,radius): self.__radius=radius
   
    def get_radius(self):
        return self.__radius




    #we call this a setter method for __radius
    def set_radius(self,radius):
        if radius>0:
            #if it is a valid value then assign it
            self.__radius=radius
        else:
            #no assignments happen here
            #printing a message to warn user
            print 'can\'t assign this value,',\

                  'radius must be positive'

    #both getter and setter methods are included
    radius=property(get_radius,set_radius)


>>> myball=Ball(5)
>>> myball.radius
5
>>> myball.radius=6
>>> myball.radius
6
>>> myball.radius=-1
can't assign this value, radius must be positive
>>> myball.radius
6
>>> 

اگر دقت کرده باشيد در تابع سازنده ویژگی شعاع رو با __ شروع کرديم. اين باعث ميشود که اين "اتريبوت" پوشيده بماند يعني نميشود بيرون از کلاس تغييرش داد یا حتی مقدارش را گرفت.  ببينيد

>>> myball.__radius
Traceback (most recent call last):
  File "", line 1, in
    myball.__radius
AttributeError: 'Ball' object has no attribute '__radius'
>>>

یکی از ایرادهایی که در در برنامه نویسی شیء گرا به پایتون وارده همین بحث خصوصی سازی ویژگیها و متود هاست. در حقیقت وقتی اسم یک متود یا ویژگی را با __ شروع کنیم پایتون فقط بااسم دیگری ذخیره اش میکند! دقت کنید
>>> myball._Ball__radius
6
>>> myball._Ball__radius=-1
>>> myball.radius
-1
>>>

این به این معنی است که عملا در پایتون همه ی ویژگیها و متود های یک کلاس همگانی (پابلیک) اند
راه قشنگ تری هم برای تعریف پراپرتی ها هست و آن استفاده از "دکوراتور" هاست. اینجا معادل مثال اول را با این روش مینویسیم 
class Ball(object):

    def __init__(self,radius): self.__radius=radius

    @property
    def radius(self):
        '''radius of the ball'''
        return self.__radius 


>>> myball=Ball(5)
>>> myball.radius
5
 
هر پراپرتی میتواند 3 متود "گتر"، "ستر" و "دیلیتر" داشته باشد به علاوه ی یک "داک استرینگ". الان من شعاع توپ را به صورت یک پراپرتی کامل بااستفاده از "دکوراتور" ها تعریف میکنم.

class Ball(object):

    def __init__(self,r): self.__radius=r

    @property
    def radius(self):
        '''radius of the ball'''
        return self.__radius

    @radius.setter
    def radius(self,r):
        if r>0: self.__radius=r
        else: print 'must be positive'

    @radius.deleter
    def radius(self):
        #this method will delete __radius attribute
        #and so will its related property
        del self.__radius
 
>>> myball=Ball(5)
>>> myball.radius=0
must be positive
>>> del myball.radius
>>> myball.radius
Traceback (most recent call last):
  File "", line 1, in
    myball.radius
  File "C:\Users\SEVEN\Desktop\basic oop\basic oop.py", line 55, in radius
    return self.__radius
AttributeError: 'Ball' object has no attribute '_Ball__radius'
>>>

با حذف کردن یک پراپرتی در واقع متود دیلیترش صدا زده میشود.

برای اطلاعات بیشتر در مورد پراپرتی ها به سایت رسمی پایتون اینجا و یا اینجا مراجعه کنید. در ضمن اگه هر سوال یا پیشنهادی هم دارید به صورت نظر به همین پست بفرستید.
موفق باشید ☺



۱۱/۲۰/۱۳۸۸

برنامه نویسی شیء گرا - قسمت اول



با استفاده از کلاس ها میشه تقریبا هر شیء یا مفهوم تو دنیای واقعی رو وارد دنیای برنامه های کامپیوتری کرد. این یعنی اینکه میشه هر شیء رو با ویژگیهاش برای یه برنامه تعریف کرد. حتی میشه رفتارهای مختلفشو با یه سری توابع تعریف کرد
با یه مثال ساده شروع میکنیم. میخوایم مفهوم نقطه در صفحه رو تعریف کنیم

class Point(object):
    x=None
    y=None
فعلا نقطه رو فقط با دو ویژگی تعریف کردیم. حالا با این تعریف دو تا نقطه میسازیم

>>> p1=Point()
>>> p2=Point()
>>> p1.x=3
>>> p1.y=5
>>> print p1.x, p1.y
3 5
>>> p2.y=6
>>> p2.x=1
>>> p2.x+p2.y
7
>>> 

تو خط اول گفتیم پی 1 یه نقطس و تو خط های سوم و چهارم ویژگیهاشو مشخص کردیم. بعد تو خط پنجم اومدیم به ترتیب اونارو پرینت کردیم
میشه همزمان با تعریف کردن  پی 1 ، ویژگیهاشم مشخص کرد. در ضمن مثل موقعی که یه متغیر رو پرینت میکنیم و مقدارش چاپ میشه ، یه نمونه از یه کلاس رو هم میتونیم مستقیم پرینت کنیم و تعیین کنیم چیش چاپ شه! دقت کنید

class Point(object):
   
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __str__(self):
        return str(self.x)+' '+str(self.y)

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

>>> p3=Point(4,12)
>>> print p3
4 12
>>> str(p3)
'4 12'
>>>  
 
دقت کنید در تمامی توابع یه کلاس به جای اولین ورودی،  اون نمونه ای از کلاس که تابع رو صدا میزنه قرار میگیره. مثلا تو مثال بالا تو خط اول، پی3 به جای اولین آرگومان قرار گرفت و 4 و 12 به ترتیب دومین و سومین ورودیهای تابع سازنده میشن

به سه دستور زیر دقت کنید. اینا معادلن


>>> str(p3) ; p3.__str__() ; Point.__str__(p3)
'4 12'
'4 12'
'4 12'
>>> 

همیشه بهتره کدتونو جوری بنویسید که اگه کسی خواست ازش استفاده کنه یا خودتون در آینده خواستین ازش استفاده کنین به راحتی قابل فهم باشه. راهش اینه که از کامنت ها و "داک استرینگ" ها استفاده کنیم. الان دوباره نقطه رو تعریف میکنیم و این بار در مورد کدش توضیحم میزاریم. در ضمن یه متود (تابع) هم بهش اضافه میکنیم که فاصله ی این نقطه از یه نقطه ی دیگه رو حساب کنه

class Point(object):
    '''represents a point on a 2D screen'''
   
    def __init__(self,x,y):
        '''
        x is the coordinate along x-axis
        y is the coordinate along y-axis
        '''
        self.x=x
        self.y=y

    def __str__(self):
        '''

        point's coordinates in string seperated
        by a white space
        ''' 
        return str(self.x)+' '+str(self.y)

    def distance_from(self,aPoint):
        '''
        calculates distance of aPoint

        from this point.
        (returns a floating point number)
        '''
        #we need sqrt
in our calculation
        from math import sqrt
        #using Pythagorean theorem
        dx=self.x-aPoint.x
        dy=self.y-aPoint.y
        return sqrt(dx**2+dy**2)

حالا متود جدیدو امتحان میکنیم

>>> p=Point(3,5)
>>> o=Point(0,0)
>>> p.distance_from(o)
5.8309518948453007
>>> 

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

>>> print p.distance_from.__doc__

        calculates distance of aPoint from this point
        (returns a floating point number)
       
>>> print Point.__doc__
represents a point on a 2D screen
>>>  

فک کنم برای قسمت اول کافی باشه☺