من در هفته‌ای که گذشت به یه مشکل با متلب برخوردم، برنامه‌م با اینکه همه چیش درست به نظر میومد، جواب اشتباه میداد! مشکل هم این بود که من با هر حیله و مکری که خواستم بفهم‌م مشکل از کجاست، نتونستم! به نظرم مشکل از اینجا ناشی میشد که متلب بیش از حد حرف‌گوش‌کن و محجوب‌ه و این وسط، اگه چیزی هم باشه به روم نمیاره! به هر حال این بهانه‌ای بود که با اون من تصمیم گرفتم برنامه رو با پایتون۱‍ بازنویسی کنم.
از اونجایی که من برنامه‌نویس حرفه‌ای نیستم، در این پست فقط قصد معادل‌سازی خیلی سریع صحبت با زبان پایتون برای متلب‌زبان‌ها دارم و به احتمال خیلی زیاد، برای یاد گرفتن این زبان، کتاب‌هایی مثل How to Think Like a Computer Scientist و Dive into Python خیلی مفیدتر از این پست هستن!

بلوک‌ها با دو نقطه شروع میشن و میرن تو!

در متلب بلوک با کلمه‌ی کلیدی شروع میشه و با end تموم میشه. مثلا برای تعریف یه شرطی داریم:

x = 4
if(x > 3)
    y = 3*x;
    z = x + y;
end

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

x = 4
if(x > 3):
    y = 3*x
    z = x + y

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

x = 4
if(x > 3)
    y = 3*x;
    z = x + y;
    
    if(z == 5)
        y = 2*x - z;
        z = y + 4*x;
    end
end

در پایتون به صورت زیر در میان:

x = 4
if(x > 3):
    y = 3*x
    z = x + y
    
    if(z == 5):
        y = 2*x - z
        z = y + 4*x

پایتون با پایتون اجرا میشه!

برای اجرای کدهای نوشته شده در پایتون، ۲ راه داریم، اولیش رو الان میگم، دومیش رو بعدا. اولین راه اینه که کد رو داخل یه فایل بنویسید و با پسوند py ذخیره کنید، بعد داخل ترمینال با دستور python3 fileName.py اجراش کنید. اون ۳ آخر پایتون به خاطر اینه که فعلا روی اکثر توزیع‌های لینوکس، مفسر پایتون۲ پیش‌فرض‌ه و برای تفسیر با پایتون۳، فعلا این اضافه کاری رو انجام میدیم. مثلا من تکه کد بالا رو در یه فایل به اسم first.py در پوشه‌ی /Documents/Python ذخیره میکنم و داخل ترمینال از پایتون میخوام که برام تفسیر کنه:
meysam@freedom:~/Documents/Python$ python3 first.py
و طبیعتا چون به پایتون نگفتیم چیزی چاپ کن، چاپ نمیکنه!

از خون دل نوشتم، در پایتون یک نامه! :دی

در این بخش میخوایم پایتون مقدار x و z در برنامه‌ای که در بالا ساختیم رو برامون چاپ کنه. برای چاپ در پایتون۳ از print() استفاده می‌کنیم. صورت کلی این تابع اینطوره: print(var1, var2, "str1", var3, "str2", "and another variable or string!")، یعنی متغیرها و رشته‌ها با یه کاما از هم جدا میشن. همونطور که اول پاراگراف بحث شد، میخواستیم اون دوتا متغیر رو چاپ کنیم، پس کدمون اینطور چیزی میشه:
x = 4
if(x > 3):
    y = 3*x
    z = x + y
    
    if(z == 5):
        y = 2*x - z
        z = y + 4*x

    print(x,z)
و کد رو اجرا می‌کنیم:
meysam@freedom:~/Documents/Python$ python3 first.py 
4 16

یک لیست از داده‌ها!

من میخوام برنامه‌ی بالا رو جوری تغییر بدم که به جای یک x ثابت، چندتا ایکس رو از یه لیست بخونه و آخر کار، مثل بالا نتیجه رو برای هر xی که گرفته، چاپ کنه. پس اولا من نیاز داریم به ساختن یه لیست که بتونم داده بریزم توش و دوم اینکه بتونم داده‌ها رو بخونم از لیست.

برای ساختن لیست در پایتون، از list() استفاده میشه. مثلا من میخوام یه لیست w بسازم و اعداد ۱.۱، ۱.۲، ۴، ۹ و ۱۱.۳۴۵ رو بریزم توش:
w = list([1.1,1.2,4,9,11.345])
یا به صورت خیلی ساده‌تر، عبارت زیر، معادل عبارت بالاست (ساختارش مثل بردار تعریف کردن در متلب هست):
w = [1.1,1.2,4,9,11.345]
از شکل اولی برا تعریف لیست، من برای ساختن لیست خالی استفاده میکنم و از شکل دومی، برای وقتایی که نیازه داده‌ها رو به صورت دستی وارد یه لیست کنم. یه قانون من‌درآوردی! :)
برای دسترسی به عناصر از listName[index] استفاده میشه، یعنی از لیستی به نام listName، اندیس index رو برام برگردون (در متلب از پرانتز برای دسترسی به موقعیت یک بردار استفاده می‌کردیم، اینجا برای دسترسی به یک درایه‌ی لیست، از کروشه استفاده می‌کنیم)، مثلا در لیست w که در بالا تعریف کردیم، مقدار w[0] ۱.۱ هست و مقدار w[2] چهار (پایتون یه زبان آدم‌حسابی‌ه! اندیس‌ها به صورت معمول از صفر شروع میشن!)، برای دسترسی به آخرین عنصر لیست هم از اندیس منفی یک استفاده می‌کنیم :دی یعنی w[-1] مقدارش ۱۱.۳۴۵ هست.

الان نصف راه این بخش رو اومدیم، تونستیم یه لیست بسازیم و در صورت نیاز، به عناصر داخلش دسترسی داشته باشیم. حالا میخوایم برنامه‌مون، به جای اینکه فقط x برابر ۴ رو برای محاسبات داشته باشه، روی هر درایه‌ی لیست محاسبات انجام بده. اینجا طبیعتا ساختار مربوط به حلقه‌ها سر و کله‌شون پیدا میشه. ساده‌ترین راه پیمایش لیست، استفاده از حلقه‌ی for هست. ما از for این شکلی استفاده می‌کنیم: for element in dataset:، یعنی دونه به دونه عناصر مجموعه داده‌ی dataset رو بردار و اسمش رو بذار element و فلان کار رو انجام بده. مثلا اگه بخوایم یه برنامه داشته باشیم که عناصر لیست w که در بالا تعریف کردیم رو دونه دونه بخونه و چاپ کنه، کدمون این شکلی میشه:
w = [1.1,1.2,4,9,11.345]
for x in w:
    print(x)
که نتیجه‌ی اجراش هم میشه اینطوری:
meysam@freedom:~/Documents/Python$ python3 printList.py 
1.1
1.2
4
9
11.345
الان تمام چیزی رو که برای هدف این بخش‌مون می‌خواستیم، داریم. پس کدی که میخواد «هر درایه رو از لیست بخونه و محاسبات روش انجام بده و داده‌های مربوط به اون رو چاپ کنه»، به این صورت میشه:
w = [1.1,1.2,4,9,11.345]

for x in w:
    if(x > 3):
        y = 3*x
        z = x + y
    
        if(z == 5):
            y = 2*x - z
            z = y + 4*x

        print(x,z)

یک برنامه‌ی کاربردی!

حالا که تا اینجا اومدیم، میخوایم یه برنامه بنویسیم که معدل ترم رو حساب کنه. معدل اینطور بدست میاد: «[مجموع (تعداد واحد درسی×نمره‌ی درس)] تقسیم بر مجموع تعداد واحد‌ها.». بنابراین میخوایم روی دوتا لیست هم‌طول (که داخل یکی‌شون نمره‌ی درس هست، مثلا ۱۴.۲۵ و داخل اون یکی تعداد واحد، مثلا ۳)، درایه‌های متناظر رو ضرب کنیم و آخر کار، حاصل رو بر مجموع تعداد واحدها تقسیم کنیم. کد پایتونش اینطور میشه:
marks = [14.25, 20, 11, 8.5, 18, 19.25, 17]
units = [3, 4, 2, 2, 3, 4, 4]

sumOfMark = 0.0
numOfUnit = 0
for index, mark in enumerate(marks):
    sumOfMark += units[index]*mark
    numOfUnit += units[index]

average = sumOfMark / numOfUnit

print(average)
تنها نکته‌ی برنامه، خط مربوط به حلقه‌ست. اینجا لیست marks رو تبدیل به یه نوع داده‌ی شمارش‌پذیر کردیم (داخل متلب که نداشتیم اینطور چیزی رو، داخل سی یا سی++، نوع enum رو داشتیم که این همونه.). منظورم از نوع داده‌ی شمارش‌پذیر ایجاد یک تناظر یک به یک، بین یه سری عدد طبیعی و اون داده‌هاست. مثلا یه لیست داریم که داخلش داده‌های [Meysam, Saleh, MeysamS, Filan] هستن. وقتی من میگم میخوام این لیست رو شمارش‌پذیر کنم، منظورم اینه که میخوام بتونم بگم داده‌ی اول، Meysamه، داده‌ی دوم Salehه، داده‌ی سوم MeysamSه و الی آخر. خب این فایده‌ش چیه؟ یه فایده‌ی من‌درآوردی‌ش۳ میتونه این باشه که شما یه لیست از خطاهای مربوط به برنامه‌تون رو تهیه می‌کنید، بعد اون رو شمارش‌پذیر می‌کنید، حالا به جای اینکه هر دفه یه رشته‌ی فلان قدری به برنامه‌هاتون پاس بدین و یا به کاربر نشون بدین، یه عدد رو بهش برمیگردونین، این مخصوصا موقعی که با زبان json بین دو ابزار صحبت می‌کنید، به نظر میتونه خیلی کاربردی باشه. برای یه مثال هم، اون لیست از اسامی که ساختم رو شمارش‌پذیر می‌کنم و بعدش هم درایه‌هاش رو چاپ می‌کنم:
enumNames = enumerate(["Meysam", "Saleh", "MeysamS", "Filan"])
for i in enumNames:
    print(i)
نتیجه‌ی اجراش اینه:
meysam@freedom:~/Documents/Python$ python3 printEnumTuples.py 
(0, 'Meysam')
(1, 'Saleh')
(2, 'MeysamS')
(3, 'Filan')
خلاصه اینکه اون خط حلقه میاد لیست marks رو شمارش‌پذیر میکنه و شروع میکنه ازش خوندن، مثل مثال بالا، محتوای هر عنصری که از لیست شمارش‌پذیرشده، یه دوتائی‌ه که اولی یه عدده که for اون رو انتساب میده به index و دومی یه مقدار (رشته، عدد، هر چی! بسته به اینکه لیستی که اول کار داشتیم و شمارش‌پذیرش کردیم، چی توش بوده باشه.) هست که انتساب میده به mark.
و اجرای این برنامه:
meysam@freedom:~/Documents/Python$ python3 avgMarkFunctions.py 
16.397727272727273
خوبه! مشروط نمیشه! :دی البته طبیعی‌ترین آدمی که اینطور برنامه‌ای می‌نویسه، آدمی هست که میخواد حساب کنه اون ترم مشروط میشه یا نه! :دی

دوباره! دوباره!

بهتر به نظر میاد که برنامه رو جوری تغییر بدیم که قابلیت استفاده‌ی دوباره داشته باشه و حتی بشه در برنامه‌های دیگه هم از اون استفاده کرد. این یعنی همون مفهوم آشنای تابع. برای تعریف تابع در پایتون از قالب def funcName(arg1, arg2, etc): استفاده می‌کنیم. مثلا میخوایم یه تابع بنویسیم که دوتا لیست بگیره و معدل رو برگردونه، پس طبق قالب کلی تعریف تابع، برنامه‌مون اینطور میشه:
def avgMarks(marks, units):
    sumOfMark = 0.0
    numOfUnit = 0
    for index, mark in enumerate(marks):
        sumOfMark += units[index]*mark
        numOfUnit += units[index]

    average = sumOfMark / numOfUnit
    
    return average

بیوه! بیوه! بیوه!

فرض کنید در پروسه‌ی حل یه مسئله یه تابع نوشتیم، الان میخوایم اون رو «تست کنیم و تغییر بدیم» و در صورت نیاز این کار رو بارها انجام بدیم. طبیعتا یک راه ممکن برای این کار، فراخوانی فایل از خط فرمان‌ه. این کار یه مشکل خیلی بزرگ داره! برای تغییر داده‌های تست، باید هر بار فایل رو ویرایش کرد و ذخیره کرد و دوباره اجرا کرد. اینطور آدم خسته میشه خب :(، پس مطمئنا یه راه آسون‌تر برای فراخوانی تابع نسبت به اون چیزی که در بخش دوم اومد، باید باشه و اون هم استفاده از محیط تعاملی (Interactive) پایتون هست. برای رفتن به محیط تعاملی پایتون دستور python3 رو بدون هیچ چیزی در ترمینال وارد کنید، پس از این دستور، محتویات ترمینال باید شامل اینطور چیزایی باشه:
meysam@freedom:~/Documents/Python$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
از این به بعد، میشه جلوی علامت >>> دستورات پایتون رو تایپ کرد، بعد از وارد کردن هر دستور در این محیط، کلید اینتر برای پایان‌دادن نوشتن دستور و اجرای اون استفاده میشه. برای مثال دوتا لیست نمره‌ها و تعداد واحدها رو می‌سازیم:
>>> marks = [14.25, 20, 11, 8.5, 18, 19.25, 17]
>>> units = [3, 4, 2, 2, 3, 4, 4]
و marks رو چاپ می‌کنیم:
>>> print(marks)
[14.25, 20, 11, 8.5, 18, 19.25, 17]
خب الان وقتشه که برنامه‌ی معدل‌گیری رو داخل محیط تعاملی اجرا کنیم. برای اجرای تابع در متلب، تنها کافی بود فایل در پوشه‌ای باشه که متلب اون رو بشناسه (و این مشکل من با متلب بود! دروغ چرا!)، اما در پایتون علاوه بر این، نیازه یه کار دیگه هم انجام بدیم: «به پایتون بگیم که من میخوام از این تابعی که منظور خودم هست، استفاده کنم۴.». برای این کار، ما ماژول رو فراخوانی می‌کنیم:
>>> import avgMarkFunctions as avgModu
اینجا الان چیکار کردم؟ من تابعی که برای محاسبه معدل نوشته بودم رو در یک فایل به اسم avgMarkFunctions.py ذخیره کرده بودم، پس موقعی که میخوام از اون استفاده کنم، میام به پایتون میگم شما برو اون فایل رو برا من بردار بیار! اما اگه دقت کنید اسم فایل خیلی بلنده، کی حال و حوصله داره اونو هی تایپ کنه، پس با پایتون قرار میذارم که از این به بعد ما به avgMarkFunctions میگیم avgModu. به بیان نادقیق ولی ساده، به اون فایلی که تابع -یا تابع‌ها- در اون ذخیره شدن میگیم ماژول و اون avgModu هم فضای نامی هست که برای این ماژول در نظر گرفتیم (فضای نام، مثل اسم محله تو یه شهر میمونه، وقتی پلیس میخواد دنبال اصغرکلنگ بگرده، ممکنه ۴ تا اصغرکلنگ تو محله‌های مختلف پیدا کنه که در اینصورت قاط میزنه، ولی اگه به آقای پلیس بگن برو اصغرکلنگ تو محله‌ی کفترپلنگ رو بیار، زارت میره در خونه‌ی طرف و میگه بپر بالا!).
پس الان ما ماژول رو فراخوانی کردیم و نوبت به اون رسیده که اون رو اجرا کنیم، در این اجرا مقدار رو به متغیر a اختصاص میدیم و در آخر متغیر رو چاپ می‌کنیم.
>>> a = avgModu.avgMarks(marks,units)
>>> print(a)
16.397727272727273
طبق یه افسانه‌ی قدیمی، میگن تو دانشگاه‌های بهشت، هر کس معدلش بین ۱۶ تا ۱۸ باشه، بهش ۲ نمره اضافه میکنن و هر کی هم معدلش بالای ۱۸ باشه، بهش میدن بیست. از اونجایی که جهنم رو میریم و می‌بینیم، پس خوبه که این افسانه رو در برنامه‌مون پیاده کنیم ببینیم چه حالی داره :دی. یعنی من میخوام برنامه‌م، بعد از محاسبه‌ی معدل، اگه بین ۱۶ تا ۱۸ بود، به مقدار معدل ۲ نمره اضافه کنه و اگه بالای ۱۸ بود، مقدار رو بذاره ۲۰. کد اینطوری میشه:
def avgMarks(marks, units):
    sumOfMark = 0.0
    numOfUnit = 0
    for index, mark in enumerate(marks):
        sumOfMark += units[index]*mark
        numOfUnit += units[index]

    average = sumOfMark / numOfUnit
    
    if(16 <= average < 18):
        average += 2
    elif(18 <= average < 20):
        average = 20
    
    return average
اما قبل از تست این برنامه‌ی جدید، باید به یه نکته توجه کنیم: «پایتون به خاطر یه سری بهینه‌سازی‌ها، در هر جلسه‌ی محیط تعاملی، ماژول فراخوانی شده رو در خودش ذخیره میکنه.». این یعنی پایتون الان کاری به کدی که من تغییر دادم نداره و همون قبلی رو اجرا میکنه، برای رفع این مشکل، باید ماژول رو بازفراخوانی کنیم. اینکار توسط تابع reload از ماژول importlib صورت میگیره. پس من اول نیاز دارم که این تابع رو از ماژول مذکور فراخوانی کنم و سپس ماژول خودم رو بازفراخوانی کنم:
>>> from importlib import reload
>>> reload(avgModu)
<module 'avgMarkFunctions' from '/home/meysam/Documents/Python/avgMarkFunctions.py'>
و حالا میتونم برنامه‌م رو تست کنم:
>>> avgModu.avgMarks(marks,units)
18.397727272727273

البته لازم میدونم عرض کنم که عنوان biyoh هست، همون که کفتربازها برا غذا دادن به کفتراشون مرتبا تکرار میکنن :دی.

ماتریس

ماتریس چیه؟ ماتریس یه آرایه‌ی دوبعدی‌ه! مسلما تعریف گنگ‌ه. بهتر بخوایم بگیم، ماتریس یه لیست‌ه که اعضاش لیست‌ن. مثلا وقتی میگیم عنصر ۲ و ۳ ماتریس چیه؟ در حقیقت سوالمون اینه که تو لیست دومی، عنصر سومی چیه؟ برای بیان بهتر موضوع، شکل زیر رو داریم:

اینجا یه لیست بیرونی داریم که رنگش خاکستری‌ه و داخل‌ش چهارتا لیست داریم که رنگشون آبی ِ داف‌پسنده که داخل این لیست‌های داخلی، عددهایی هستن. مثلا لیست ۲، عددهای ۲، ۳، ۴ و ۵ داخلشن. بنابراین در موقعیت ۳ و ۱ (یعنی در لیست سوم و موقعیت اول آن) عدد ۳ هست. هوم؟ خب با همین ایده میشه یه ماتریس دوبعدی یا هر چند بعدی ساخت. برای مثال، برای ساختن همین ماتریسی که در شکل بالا داریم، دستورات زیر رو در محیط تعاملی وارد می‌کنیم:
>>> mat = list()
>>> mat.append([1,2,3,4])
>>> mat.append([2,3,4,5])
>>> mat.append([3,4,5,6])
>>> mat.append([4,5,6,7])
متد append میاد عنصری که بهش دادین رو به آخر لیست اضافه میکنه. البته میتونیم این لیست رو به شکل ماتریسی چاپ کنیم:
>>> for v in mat:
...    for i in v:
...       print(i, end=" ")
...    print("\n")
... 
در کد بالا، میاد میگه داخل لیست a اعضای رو بخون و دونه دونه اسمشون رو بذار v (پس در هر مرتبه v خودش یک لیست‌ه)، حالا برای هر کدوم از vها، اعضای v رو چاپ کن، فقط به جای اینکه به صورت پیش‌فرض بعد از چاپ کردن بری خط بعد، بعد از هر بار چاپ یه خط فاصله بذار. در ضمن وقتی داخل محیط تعاملی باید تورفتگی ایجاد کرد (مثلا مثل همین استفاده از حلقه‌ی for)، این کار رو من خودم انجام میدم و محیط به صورت خودکار کاری برا من نمیکنه. و نتیجه‌ی اجرای اون:
1 2 3 4 

2 3 4 5 

3 4 5 6 

4 5 6 7 
البته به صورت مستقیم هم میشه به درایه‌ها دسترسی داشت، برای مثال برای دیدن موقعیت ۳ و ۱ ماتریسی که ساختیم داریم (دقت کنیم که در پایتون اندیس‌ها از صفر شروع میشن):
>>> mat[2][0]
3

چیا موندن؟

تا اینجا تونستیم همه‌ی چیزهایی که برای ترجمه‌ی یه کد معمولی متلب به پایتون۳ مورد نیاز هست رو داشته باشیم، اما این تمام اونچه در پایتون هست نبود، مثلا در این پست

  • کار با فایل‌ها،
  • کار با سیستم‌عامل،
  • کار با رشته‌ها،
  • کار با دیکشنری‌ها و n-تایی‌ها۲،
  • تعداد پارامتر دلخواه توابع،
  • مدیریت خطا و استثناء،
  • شی‌گرایی.

نبود! برای یه آموزش خوب، دوتا کتابی که اول کار معرفی شد و یا این لینک کافی به نظر میان!


۱. پیتون هم تلفط میشه و فکر کنم درست‌تر باشه، ولی پایتون خوش‌آهنگ‌تره!
۲. همون tuples، تو ریاضی به اینطور ساختاری میگیم فلان‌تایی، مثلا، (۲,۳) یه ۲تایی‌ه.
۳. من‌درآوردی ِ من‌درآوردی هم که نیست! بعضا حتی دقیقا برعکس اینکار انجام میشه. بعضیا اعتقاد دارن که پاس دادن یه عبارت منظور رو بهتر منقل میکنه تا یه عدد. به هر حال این هم به نظرم به سلیقه‌ی برنامه‌نویس برمیگرده :)).
۴. وضعیتی رو در نظر بگیرید که تابع اصلی که شما میخواستین، توسط یه تابع با اولویت بالاتر Override شده.