Python حرفه‌ای: اصول برنامه‌نویسی شی‌گرا

Professional Python: Principles of Object-Oriented Programming

18 اسفند 1399
درسنامه درس 13 از سری پایتون حرفه‌ای
Python حرفه ای: اصول برنامه نویسی شیء گرا (قسمت 13)

آشنایی با classmethod@ و staticmethod@

ما در جلسه قبل یاد گرفتم که می توانیم بدون استفاده از self یک خصوصیت ویژه را برای کلاس خودمان تعریف کنیم که در تمام اشیاء ثابت است و نامش class object attribute می باشد اما آیا راهی برای انجام چنین کاری در متدها نیز وجود دارد؟ بله. برای انجام چنین کاری از decorator ها استفاده می شود. ما بعدا با مبحث decorator ها به صورت مفصل آشنا خواهیم شد بنابراین فعلا توضیح اضافی در مورد آن ها نمی دهم. برای هدفی که توضیح دادم دستوری به نام classmethod@ وجود دارد که باید دقیقا به همین شکل در کلاس نوشته شود. ما با استفاده از این دستور که یک decorator است، می توانیم متدی ثابت برای تمام اشیاء ساخته شده از این کلاس را ایجاد کنیم که برای اجرا شدن نیازی به نمونه سازی نداشته باشد. به مثال زیر توجه کنید:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def run(self):

        print(f"RUN {self.palyer_name}")




    @classmethod

    def adding_things(cls, num1, num2):

        return num1 + num2

اگر یادتان باشد ما می توانستیم با صدا زدن نام کلاس PlayerCharacter به membership دسترسی داشته باشیم. در این کد نیز چنین مسئله ای را داریم و بدون اینکه نیازی به ساخت یک شیء از این کلاس باشد می توانیم از متد adding_things استفاده کنیم. کار این متد فقط اضافه کردن دو عدد به هم است اما اگر به این متد توجه کنید پارامتری به نام cls را می بینید. همانطور که در متدهای عادی باید پارامتر self را پاس می دادیم در متدهایی که با classmethod نوشته می شوند باید پارامتر cls را پاس بدهیم که مخفف class است و اشاره دارد که این متد متعلق به چه کلاسی می باشد. حالا می توانیم بدون نمونه سازی (instantiation) از این متد استفاده کنیم:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def run(self):

        print(f"RUN {self.palyer_name}")




    @classmethod

    def adding_things(cls, num1, num2):

        return num1 + num2







# player1 = PlayerCharacter('Amir')




print(PlayerCharacter.adding_things(1, 7))

دقت کنید که من خط مربوط به ساخت شیء player1 را کامنت کرده ام بنابراین هیچ نمونه ای از این کلاس ساخته نشده است. با اجرای کد بالا نتیجه ۸ را می گیریم بنابراین متد ما به سادگی و بدون نیاز به نمونه سازی کار می کند در حالی که متدهای عادی اینطور نیستند. شما در اکثر کلاس هایتان از چنین قابلیتی استفاده نمی کنید اما خوب است بدانید چنین قابلیتی وجود دارد. در ضمن از آنجایی که در این نوع از متدها به cls یا کلاس فعلی دسترسی داریم می توانیم نمونه سازی را نیز در آن انجام بدهیم! قبل از اینکه این موضوع را به شما نشان بدهم باید بدانید که روش دیگری برای اجرای متد بدون نمونه سازی وجود دارد و آن دستور staticmethod@ است:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def run(self):

        print(f"RUN {self.palyer_name}")




    @classmethod

    def adding_things(cls, name):

        return cls(name)




    @staticmethod

    def adding_static (num1, num2):

        return num1 + num2







# player1 = PlayerCharacter('Amir')




print(PlayerCharacter.adding_things("Amir"))

print(PlayerCharacter.adding_static(1, 3))

در کد بالا مشاهده می کنید که من پارامتر adding_things را تغییر داده و فقط name را دریافت می کنم. حالا می توانیم با استفاده از cls از همین کلاس نمونه سازی کرده و شیء ساخته شده را برگردانیم. به عبارتی نمونه سازی را از درون کلاس انجام داده ایم! از طرف دیگر staticmethod@ را داریم که آن هم دقیقا مانند classmethod@ می باشد با این تفاوت که در staticmethod@ به کلاس یا همان پارامتر cls دسترسی نداریم. به متدهایی که با staticmethod تعریف شوند متد استاتیک می گوییم. با اجرای کد بالا نتیجه زیر را می گیرید:

<__main__.PlayerCharacter object at 0x7fbd531a9cd0>

4

مقدار اول همان نمونه سازی از کلاس است و در واقع محل ذخیره سازی این کلاس در مموری را نمایش می دهد. ما می توانیم به جای print کردنش به شکل بالا، آن را برابر با یک متغیر قرار دهیم و از آن مانند یک نمونه عادی از کلاس PlayerCharacter استفاده کنیم. مقدار دوم نیز عدد ۴ است که حاصل جمع ۱ و ۳ بوده و مربوط به متد adding_static می باشد.

مهارت یادگیری ۵#

در نکته پنجم از سری مهارت یادگیری دوست دارم قانون بسیار مهمی در یادگیری را به شما یاد بدهم. شاید قبلا به عبارت هایی مانند Test your assumption در مقالات انگلیسی برخورد کرده باشید. این عبارت به معنی «گمان هایت را تست کن» می باشد. زمانی که در حال یادگیری مبحثی در برنامه نویسی یا حتی غیر از آن هستید، باید دانسته هایتان را تست کنید تا متوجه اشکالات آن بشوید و درک عمیق تری داشته باشید. هنگامی که ما در حال یادگیری مطلب جدیدی هستیم «گمان» های زیادی داریم که نشئت گرفته از مطالب دریافتی است اما در بسیاری از اوقات این گمان ها اشتباه هستند و یا حداقل ما آن ها را به صورت اشتباه درک کرده ایم. تست کردن چنین مباحثی در کد، باعث می شود هم یادگیری صحیحی داشته باشید و هم اینکه مهارت بیشتری به عنوان یک توسعه دهنده پیدا کنید.

به طور مثال اگر یادتان باشد من توضیح دادم که self به کلاس فعلی اشاره می کند. ما چطور می توانیم این مسئله را تست کنیم؟ یک راه ساده این است که self را از درون کلاس return کنیم تا ببینیم self واقعا چه چیزی درون خودش دارد:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def returning_self(self):

        return self







player1 = PlayerCharacter('Amir')




print(player1.returning_self())

همانطور که در کد بالا مشاهده می کنید من متدی به نام returning_self دارم که self را برمی گرداند. حالا برای مشاهده self باید این متد را صدا زده و سپس مقدار برگردانده شده (self) را چاپ کنیم. با اجرای این کد نتیجه زیر را می گیریم:

<__main__.PlayerCharacter object at 0x7f602ca40a00>

همانطور که می بینید کلاس PlayerCharacter برایمان چاپ شده است (0x7f602ca40a00 آدرس آن در مموری سیستم) بنابراین متوجه می شویم که self واقعا به این کلاس اشاره می کند. ما در این مثال گمان و پیش فرض های ذهنی خودمان را تست کرده ایم و حالا درک بهتری از موضوع داریم در حالی که اگر فقط به حرف من بسنده کنید، بحث را به خوبی درک نخواهید کرد.

چهار سنگ بنای OOP

زمانی که بحث از برنامه نویسی شی گرا یا Object-oriented Programming به میان می آید، چه در پایتون و چه در زبان های دیگر، چهار ستون اصلی به عنوان سنگ بنای این پارادایم برنامه نویسی مطرح می شوند. ما می خواهیم در این قسمت در رابطه با این چهار ستون اصلی صحبت کنیم.

کپسوله سازی (encapsulation)

اولین ستون از این چهار ستون encapsulation یا کپسوله سازی نام دارد و بدون اینکه متوجه شده باشیم با آن آشنا شده ایم! کپسوله سازی یعنی قرار دادن داده ها و متدهای آن ها (یک کلاس) درون یک کپسول یا به زبان ساده تر درون یک محیط ایزوله شده و مستقل!

به مثال هایی که تا این قسمت حل کرده ایم فکر کنید. در صورتی که کلاسی در کار نبود، فقط چند متغیر و چند تابع داشتیم اما با استفاده از مفهوم کپسوله سازی می توانیم کدهایمان را درون یک پکیج واحد قرار بدهیم یا‌ آن را کپسوله کنیم. شاید بپرسید چرا باید به دنبال قرار دادن کدهایمان در یک پکیج باشیم؟ به دلیل اینکه چنین طرحی به ما انعطاف و قدرت بیشتری می دهد تا به تمام ابزار ها و اطلاعات مورد نیاز خود به صورت یکجا دسترسی داشته باشیم.

انتزاع (abstraction)

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

player1.speak()

ما با نگاه کردن به کد بالا می دانیم که player1 متدی به نام speak دارد اما نمی دانیم این متد به چه صورتی پیاده سازی شده است. چه فرآیندی در این متد انجام می شود تا نتیجه ای برای ما نمایش داده شود؟ ما هیچ اطلاعی در این مورد نداریم. به عبارت نحوه کار این متد در یک لایه انتزاعی محافظت شده است. البته این محافظت به معنی واقعی محافظت نیست. به مثال زیر توجه کنید:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def run(self):

        print(f"RUN {self.palyer_name}")







player1 = PlayerCharacter('Amir')




player1.run = "This is no longer a method"




print(player1.run)

در کلاسی که مشاهده می کنید متدی به نام run داریم بنابراین زمانی که نمونه ای مانند player1 را از این کلاس می سازیم، متد run نیز در دسترس خواهد بود. من به جای صدا زدن این متد رشته جدید را به آن منتسب کرده ام! حالا player1.run را بدون پرانتز و مانند یک attribute صدا زده ام. با اجرای کد بالا نتیجه This is no longer a method را می گیریم. آیا متوجه شدید؟ ما به راحتی می توانیم یک متد را تغییر داده و یک مقدار جدید را برایش بنویسیم! من در اینجا یک رشته را به جای آن قرار داده ام اما شما می توانید هر مقدار دیگری (boolean یا int یا یک تابع دیگر و ...) را برایش قرار بدهید. برای اثبات این موضوع می توانیم run را به صورت متد و با پرانتزهایش صدا بزنیم:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self.palyer_name = name




    def run(self):

        print(f"RUN {self.palyer_name}")







player1 = PlayerCharacter('Amir')




player1.run = "This is no longer a method"




print(player1.run())

با اجرای کد بالا با خطایی شبیه به خطای زیر روبرو می شویم:

Traceback (most recent call last):

  File "/mnt/Development/Roxo Academy/Python/test.py", line 15, in <module>

    print(player1.run())

TypeError: 'str' object is not callable

همانطور که می بینید run دیگر یک متد نیست بلکه یک رشته ساده بوده و متدی که برایش نوشته بودیم از بین رفته است. طبیعتا این مسئله یک مشکل بزرگ است چرا که ما نمی خواهیم به هر کسی اجازه بدهیم کدهای ما را ویرایش کند و هر چه دلش خواست به جای آن ها بگذارد.

به نظر شما راه حل چیست؟ در بعضی از زبان های برنامه نویسی مانند PHP یا Java مفهومی به نام attribute های خصوصی وجود دارد که کارمان را راحت می کنند اما در زبان پایتون (مثل زبان جاوا اسکریپت) چنین قابلیتی وجود ندارد بنابراین توسعه دهندگان قراردادی را ساخته اند تا به یکدیگر بگویند که خصوصیت X یا Y نباید ویرایش شود؛ ما نام attribute های خصوصی را با یک علامت آندراسکور شروع می کنیم:

class PlayerCharacter:

    membership = True




    def __init__(self, name):

        self._palyer_name = name




    def run(self):

        print(f"RUN {self._palyer_name}")







player1 = PlayerCharacter('Amir')







print(player1._palyer_name)

همانطور که می بینید در کد بالا خصوصیت player_name را به شکل player_name_ نوشته ام. از نظر فنی انجام این کار هیچ تغییری در کد ما ایجاد نمی کند اما از نظر انسان ها (دیگر توسعه دهندگان) خصوصیاتی که علامت _ داشته باشند نباید ویرایش شوند. با این کار هماهنگی بین توسعه دهندگان بیشتر می شود اما همانطور که گفتم هنوز هم می توانیم این خصوصیات و متدها را ویرایش کنیم. در ضمن متد init نیز به همین دلیل بین دو آندراسکور قرار گرفته است. متدهایی مانند __init__ متدهای خاصی در پایتون هستند که به آن ها Dunder Methods یا Magic Methods می گوییم. توسعه دهندگان پایتون با نام گذاری این متدها بدین شکل به شما هشدار داده اند که نباید تحت هیچ شرایطی این متدها را ویرایش کنید. بنابراین هیچ وقت است خصوصیات خود را با دو علامت آندراسکور شروع نکنید چرا که این قرارداد مخصوص متدهای Dunder است.

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

وراثت (inheritance)

ستون سوم از سنگ بنای برنامه نویسی شی گرا،‌ وراثت یا inheritance است. وراثت به ارث بری کدها از یک کلاس به کلاس های دیگر گفته می شود.

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

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')

کاربران برای بازی نیاز به یک حساب کاربری دارند بنابراین یک کلاس کلی به نام user را می سازیم که متدی به نام sign_in (ورود به حساب) و متدی به نام sign_out (خروج از حساب کاربری) دارد. با این حساب کلاس User مسئول ورود و خروج کاربران از حسابشان می باشد. در ضمن از آنجایی که در کلاس بالا attribute خاصی را نداریم، نیازی به متد __init__ نمی باشد.

حالا که کاربر وارد حساب کاربری خود شده است باید شخصیت خود را انتخاب کند بنابراین ما باید کدهای این شخصیت را بنویسیم:

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')







class Wizard:

    pass





class Archer:

    pass

ما در اینجا دو کلاس دیگر به نام های wizard (جادوگر) و archer (تیرانداز) را داریم. برای اینکه این دو کلاس جدید از کلاس User ارث بری داشته باشند (کدهایش را به ارث ببرند) باید به شکل زیر عمل کنیم:

class User:

    def sign_in(self):

        print('logged in')



    def sign_out(self):

        print('signed out')





class Wizard(User):

    pass





class Archer(User):

    pass





wizard1 = Wizard()

wizard1.sign_in()

همانطور که می بینید برای ارث بری از یک کلاس باید در مقابل نام کلاس فرزند (کلاسی که می خواهد ارث ببرد - در اینجا کلاس های wizard و archer) مانند توابع یک پرانتز گذاشته و نام کلاس پدر (کلاسی که ارث می دهد - در اینجا کلاس User) را به آن بدهید. شما می توانید کدهای دیگری را به کلاس های فرزند اضافه کنید اما من چنین قصدی ندارم بنابراین فقط pass را در آن ها نوشته ام تا به خطا برخورد نکنیم. حالا اگر از کلاس wizard یک نمونه بسازیم، این نمونه به کدهای درون کلاس پدر (مانند متد sign_in) دسترسی خواهد داشت چرا که این کدها را به ارث برده است. ما با اجرای کد بالا عبارت logged in را دریافت می کنیم بنابراین کدها بدون مشکل کار می کنند.

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

یک دستور بسیار کاربردی در این زمینه، تابع isinstance می باشد که مشخص می کند یک شیء نمونه ای (instance ای) از یک کلاس خاص می باشد یا خیر:

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')







class Wizard(User):

    pass







class Archer(User):

    pass







wizard1 = Wizard()

print(isinstance(wizard1, Wizard))

print(isinstance(wizard1, User))

با این کار عبارت true دو بار برایمان چاپ می شود بنابراین شیء wizard1 از کلاس Wizard ساخته شده است و از آنجایی که کلاس Wizard فرزند کلاس User است،‌ شیء wizard1 یک نمونه از User نیز محسوب می شود.

چندریختی (Polymorphism)

آخرین ستون برنامه نویسی شی گرا (polymorphism) است. ما می دانیم که متدها متعلق به اشیاء هستند اما روشی وجود دارد که در آن یک متد خاص در تمام کلاس های فرزند به ارث برده می شود اما عملکرد آن متفاوت است. یعنی نام متد در تمام این کلاس ها یکی است اما بر اساس اینکه کدام شیء این متد را صدا زده باشد، رفتار متد متفاوت خواهد بود. برای روشن تر شدن موضوع به مثال زیر توجه کنید:

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')







class Wizard(User):

    def __init__(self, name, power):

        self.name = name

        self.power = power




    def attack(self):

        print(f'{self.name} just attacked with {self.power} power')





class Archer(User):

    def __init__(self, name, arrows):

        self.name = name

        self.arrows = arrows




    def attack(self):

        print(f'{self.name} just attacked with {self.arrows} arrows')





wizard1 = Wizard('Amir', 50)

archer1 = Archer('Ahmad', 13)




wizard1.attack()

archer1.attack()

من کد جلسه قبل را کمی توسعه داده ام. در این کد هر دو کلاس Wizard و Archer متدهای init و attack را دارند اما همانطور که می بینید نحوه پیاده سازی آن ها کاملا متفاوت است. متد attack را در این دو کلاس در نظر بگیرید. در هر کدام از این کلاس ها متن متفاوتی را print می کند (ممکن است کدهای شما اصلا print نکنند بلکه دو کار کاملا متفاوت را انجام بدهند). به همین سادگی در هر دو کلاس متدی با نام یکسان (attack) را داریم اما این متدها دو کار متفاوت را انجام می دهند. به این مسئله چندریختی می گویند که انعطاف پذیری بالایی را به ما می دهد. به مثال زیر توجه کنید:

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')







class Wizard(User):

    def __init__(self, name, power):

        self.name = name

        self.power = power




    def attack(self):

        print(f'{self.name} just attacked with {self.power} power')







class Archer(User):

    def __init__(self, name, arrows):

        self.name = name

        self.arrows = arrows




    def attack(self):

        print(f'{self.name} just attacked with {self.arrows} arrows')







wizard1 = Wizard('Amir', 50)

archer1 = Archer('Ahmad', 13)







def player_attack(char):

    char.attack()







player_attack(wizard1)

player_attack(archer1)

من در کد بالا تابعی به نام player_attack را تعریف کرده ام که یک کاراکتر را گرفته و متد attack را روی آن صدا می زند. سپس دو کاراکتر wizard1 و archer1 را به آن پاس داده ام. با اجرای کد بالا نتیجه زیر را می گیریم:

Amir just attacked with 50 power

Ahmad just attacked with 13 arrows

آیا متوجه انعطاف پذیری و قدرت این روش هستید؟ ما یک تابع را نوشته ایم اما این تابع به دلیل تفاوت متد attack، کار های کاملا مختلفی را انجام می دهد! ما می توانیم همین کار را با یک حلقه for نیز انجام بدهیم.

حالا سوال دیگری از شما دارم. اگر کلاس User متدی به نام attack داشته باشد چطور؟

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')




    def attack(self):

        return 13







class Wizard(User):

    def __init__(self, name, power):

        self.name = name

        self.power = power




    def attack(self):

        print(f'{self.name} just attacked with {self.power} power')







class Archer(User):

    def __init__(self, name, arrows):

        self.name = name

        self.arrows = arrows







user1 = User()

wizard1 = Wizard('Amir', 50)

archer1 = Archer('Ahmad', 13)




print(user1.attack())

wizard1.attack()

print(archer1.attack())

من متدی به نام attack را به کلاس User نیز اضافه کرده ام که تنها عدد ۱۳ را برمی گرداند بنابراین کلاس های فرزند (wizard و archer) نیز این متد را به صورت پیش فرض خواهند داشت. اگر دقت کرده باشید من برای اثبات این مسئله به شما، متد attack را از کلاس archer حذف کرده ام اما آن را در کلاس Wizard باقی گذاشته ام. با اجرای کد بالا نتیجه زیر را می گیریم:

13

Amir just attacked with 50 power

13

آیا متوجه شدید؟ زمانی که متدی از کلاس پدر به ارث برده می شود، در کلاس های فرزند نیز موجود خواهد بود (مثل کلاس archer) اما اگر یکی از فرزندان (مثل Wizard) متدی با همان نام را در خودش تعریف کند، باعث overwrite شدن متد کلاس پدر می شود. یعنی چه؟ یعنی متد تعریف شده در کلاس پدر دیگر در این فرزند موجود نیست چرا که فرزند تصمیم گرفته است متد خودش را تعریف کند.

سوال بعدی من این است که آیا می توانیم متد کلاس پدر را درون کلاس فرزند صدا بزنیم؟ مثال زیر این موضوع را مشخص می کند:

class User:

    def sign_in(self):

        print('logged in')




    def sign_out(self):

        print('signed out')




    def attack(self):

        return 13







class Wizard(User):

    def __init__(self, name, power):

        self.name = name

        self.power = power




    def attack(self):

        print(User.attack(self))

        print(f'{self.name} just attacked with {self.power} power')







class Archer(User):

    def __init__(self, name, arrows):

        self.name = name

        self.arrows = arrows







user1 = User()

wizard1 = Wizard('Amir', 50)

archer1 = Archer('Ahmad', 13)




print(user1.attack())

wizard1.attack()

print(archer1.attack())

اگر به کد بالا دقت نمایید متوجه خواهید شد که من User.attack را درون کلاس Wizard صدا زده ام! از آنجایی که User به عنوان کلاس پدر به کلاس Wizard پاس داده شده است ما دسترسی کامل به آن داریم بنابراین می توانیم به شکل بالا آن را صدا بزنیم. با اجرای کد بالا نتیجه زیر را می گیریم:

13

13

Amir just attacked with 50 power

13

این بار نسبت به کد قبلی یک ۱۳ اضافه چاپ شده است که به خاطر صدا زدن User.attack می باشد.

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

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری پایتون حرفه‌ای توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.