diff --git a/lectures/_toc.yml b/lectures/_toc.yml index 524b302..f7ba511 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -8,9 +8,9 @@ parts: - file: getting_started - file: python_by_example - file: functions - # - file: python_essentials - # - file: oop_intro - # - file: names + - file: python_essentials + - file: oop_intro + - file: names # - file: python_oop # - file: workspace # - caption: The Scientific Libraries diff --git a/lectures/functions.md b/lectures/functions.md index 64d8ccf..b3fda59 100644 --- a/lectures/functions.md +++ b/lectures/functions.md @@ -23,18 +23,18 @@ kernelspec: ```{index} single: Python; User-defined functions ``` -## مرور کلی +## مقدمه -توابع (Functions) یکی از ساختارهای بسیار مفید هستند که تقریباً در تمام زبان‌های برنامه‌نویسی وجود دارند. +توابع (Functions) یکی از ساختارهای بسیار کاربردی هستند که تقریباً در تمام زبان‌های برنامه‌نویسی وجود دارند. ما تاکنون با چندین تابع آشنا شده‌ایم، مانند -* تابع `sqrt()` از کتابخانه NumPy و -* تابع داخلی `print()` +* تابع `()sqrt` از کتابخانه NumPy و +* تابع داخلی `()print` در این درس ما: -1. توابع را به صورت سیستماتیک بررسی می‌کنیم و نحوه نوشتن و موارد استفاده را پوشش می‌دهیم، و +1. توابع را به صورت سیستماتیک بررسی می‌کنیم و نحوه نوشتن و موارد استفاده از آنها را پوشش می‌دهیم، و 2. یاد می‌گیریم که چگونه توابع سفارشی خودمان را بسازیم. ما از import های زیر استفاده خواهیم کرد. @@ -44,15 +44,15 @@ import numpy as np import matplotlib.pyplot as plt ``` -## مبانی توابع +## اصول اولیه توابع تابع یک بخش نام‌گذاری شده از یک برنامه است که یک وظیفه خاص را اجرا می‌کند. -توابع زیادی از قبل وجود دارند و ما می‌توانیم از آنها به همین شکل استفاده کنیم. +توابع زیادی از قبل وجود دارند و ما می‌توانیم از آنها به همان شکل استفاده کنیم. ابتدا این توابع را بررسی می‌کنیم و سپس بحث می‌کنیم که چگونه می‌توانیم توابع خودمان را بسازیم. -### توابع داخلی +### توابع داخلی(Built-In) پایتون تعدادی تابع **داخلی** دارد که بدون نیاز به `import` در دسترس هستند. @@ -76,7 +76,7 @@ type(22) لیست کامل توابع داخلی پایتون در [اینجا](https://docs.python.org/3/library/functions.html) موجود است. -### توابع شخص ثالث +### توابع خارجی(Third Party) اگر توابع داخلی نیاز ما را پوشش ندهند، یا باید توابع را import کنیم یا توابع خودمان را بسازیم. @@ -91,7 +91,7 @@ calendar.isleap(2024) ## تعریف توابع -در بسیاری از موارد، توانایی تعریف توابع خودمان مفید است. +در بسیاری از موارد، باید بتوانیم توابع مدنظر خودمان را تعریف کنیم. بیایید با بحث در مورد نحوه انجام آن شروع کنیم. @@ -116,7 +116,7 @@ f(10) در اینجا یک تابع طولانی‌تر داریم که قدر مطلق یک عدد داده شده را محاسبه می‌کند. -(چنین تابعی قبلاً به عنوان یک تابع داخلی وجود دارد، اما بیایید برای تمرین، تابع خودمان را بنویسیم.) +(چنین تابعی از قبل به عنوان یک تابع داخلی وجود دارد، اما بیایید برای تمرین، تابع خودمان را بنویسیم.) ```{code-cell} python3 def new_abs_function(x): @@ -130,9 +130,9 @@ def new_abs_function(x): بیایید نحو را در اینجا بررسی کنیم. * `def` یک کلمه کلیدی پایتون است که برای شروع تعریف توابع استفاده می‌شود. -* `def new_abs_function(x):` نشان می‌دهد که نام تابع `new_abs_function` است و یک آرگومان واحد `x` دارد. +* `:def new_abs_function(x)` نشان می‌دهد که نام تابع `new_abs_function` است و یک آرگومان واحد `x` دارد. * کد تورفتگی‌دار یک بلوک کد است که *بدنه تابع* نامیده می‌شود. -* کلمه کلیدی `return` نشان می‌دهد که `abs_value` شیء‌ای است که باید به کد فراخوانی‌کننده برگردانده شود. +* کلمه کلیدی `return` نشان می‌دهد که `abs_value` شیئی است که باید به کد فراخوانی‌کننده برگردانده شود. تمام این تعریف تابع توسط مفسر پایتون خوانده می‌شود و در حافظه ذخیره می‌شود. @@ -145,7 +145,7 @@ print(new_abs_function(-3)) توجه کنید که یک تابع می‌تواند تعداد دلخواهی دستور `return` داشته باشد (از جمله صفر). -اجرای تابع زمانی که به اولین return برسد، خاتمه می‌یابد و این امکان را می‌دهد که کدهایی مانند مثال زیر بنویسیم +اجرای تابع زمانی که به اولین return برسد، خاتمه می‌یابد و این امکان را می‌دهد که کدهایی مانند مثال زیر بنویسیم: ```{code-cell} python3 def f(x): @@ -172,32 +172,32 @@ def f(x): plt.plot(x, 'b-', label="white noise") ``` -در این فراخوانی تابع `plot` کتابخانه Matplotlib، توجه کنید که آخرین آرگومان با نحو `name=argument` ارسال می‌شود. +در فراخوانی تابع `plot` از کتابخانه Matplotlib، باید توجه داشته باشید که آخرین آرگومان با نحو `name=argument` ارسال می‌شود. -این را یک *آرگومان کلیدواژه‌ای* می‌نامند، که `label` کلیدواژه است. +این را یک *آرگومان کلیدواژه‌ای(keyword argument)* می‌نامند، که `label` همان کلیدواژه است. -آرگومان‌های غیر کلیدواژه‌ای را *آرگومان‌های موضعی* می‌نامند، زیرا معنای آنها با ترتیب مشخص می‌شود +آرگومان‌های غیر کلیدواژه‌ای را *آرگومان‌های ترتیبی(positional argument)* می‌نامند، زیرا معنای آنها با ترتیب مشخص می‌شود. * `plot(x, 'b-')` با `plot('b-', x)` متفاوت است -آرگومان‌های کلیدواژه‌ای به ویژه زمانی مفید هستند که یک تابع آرگومان‌های زیادی دارد، در این صورت به خاطر سپردن ترتیب صحیح سخت است. +آرگومان‌های کلیدواژه‌ای زمانی کاربردی هستند که یک تابع آرگومان‌های زیادی دارد، در این صورت به خاطر سپردن ترتیب صحیح سخت است. شما می‌توانید آرگومان‌های کلیدواژه‌ای را در توابع تعریف شده توسط کاربر بدون مشکل به کار ببرید. -مثال بعدی نحو را نشان می‌دهد +به مثال بعدی توجه کنید: ```{code-cell} python3 def f(x, a=1, b=1): return a + b * x ``` -مقادیر آرگومان کلیدواژه‌ای که در تعریف `f` ارائه کردیم، به مقادیر پیش‌فرض تبدیل می‌شوند +مقادیر آرگومان کلیدواژه‌ای که در تعریف تابع `f` مشخص شده‌اند، به عنوان مقادیر پیش‌فرض درنظر گرفته می‌شوند ```{code-cell} python3 f(2) ``` -آنها را می‌توان به شکل زیر تغییر داد +آنها را می‌توان به شکل زیر تغییر داد: ```{code-cell} python3 f(2, a=4, b=5) @@ -209,13 +209,12 @@ f(2, a=4, b=5) به طور خاص -* هر تعداد تابع می‌تواند در یک فایل معین تعریف شود. -* توابع می‌توانند (و اغلب) در داخل توابع دیگر تعریف شوند. -* هر شیء می‌تواند به عنوان آرگومان به یک تابع ارسال شود، از جمله توابع دیگر. +* هر تعداد تابع می‌تواند در یک فایل مشخص تعریف شود. +* توابع می‌توانند (و اغلب هم می شوند) در داخل توابع دیگر تعریف شوند. +* هر شیء می‌تواند به عنوان آرگومان به یک تابع داده شود، از جمله توابع دیگر. * یک تابع می‌تواند هر نوع شیء را برگرداند، از جمله توابع. -ما در بخش‌های بعدی مثال‌هایی از اینکه چقدر ساده است که یک تابع را به یک تابع دیگر ارسال کنیم، ارائه خواهیم داد. - + در بخش‌های بعدی، با مثال‌هایی نشان خواهیم داد که انتقال یک تابع به یک تابع دیگر تا چه حد ساده است. ### توابع یک خطی: `lambda` ```{index} single: Python; lambda functions @@ -223,28 +222,24 @@ f(2, a=4, b=5) کلمه کلیدی `lambda` برای ایجاد توابع ساده در یک خط استفاده می‌شود. -به عنوان مثال، تعریف‌های زیر +برای نمونه، مثال های زیر کاملا معادل یکدیگر هستند: ```{code-cell} python3 def f(x): return x**3 ``` -و - ```{code-cell} python3 f = lambda x: x**3 ``` -کاملاً معادل هستند. - -برای اینکه ببینیم چرا `lambda` مفید است، فرض کنید می‌خواهیم $\int_0^2 x^3 dx$ را محاسبه کنیم (و حساب دبیرستانمان را فراموش کرده‌ایم). +برای اینکه ببینیم چرا `lambda` کاربردی است، فرض کنید می‌خواهیم $\int_0^2 x^3 dx$ را محاسبه کنیم (و حسابان دبیرستانمان را فراموش کرده‌ایم). کتابخانه SciPy تابعی به نام `quad` دارد که این محاسبه را برای ما انجام می‌دهد. نحو تابع `quad` به صورت `quad(f, a, b)` است که `f` یک تابع و `a` و `b` اعداد هستند. -برای ایجاد تابع $f(x) = x^3$ می‌توانیم از `lambda` به شکل زیر استفاده کنیم +برای ایجاد تابع $f(x) = x^3$ می‌توانیم از `lambda` به شکل زیر استفاده کنیم: ```{code-cell} python3 from scipy.integrate import quad @@ -252,16 +247,16 @@ from scipy.integrate import quad quad(lambda x: x**3, 0, 2) ``` -در اینجا تابع ایجاد شده توسط `lambda` *ناشناس* نامیده می‌شود زیرا هرگز نامی به آن داده نشده است. +در اینجا تابع ایجاد شده توسط `lambda` *ناشناس(anonymous)* نامیده می‌شود زیرا هرگز نامی به آن داده نشده است. -### چرا توابع بنویسیم؟ +### چرا تابع بنویسیم؟ -توابع تعریف شده توسط کاربر برای بهبود وضوح کد شما از طریق موارد زیر مهم هستند: +توابع تعریف شده توسط کاربر برای بهبود شفافیت و خوانایی کد شما اهمیت دارند، زیرا: -* جداسازی رشته‌های مختلف منطق -* تسهیل استفاده مجدد از کد +* جنبه‌های مختلف منطق برنامه را از هم جدا می‌کند +* امکان استفاده مجدد کد را فراهم می‌کنند -(نوشتن یک چیز دو بار [تقریباً همیشه ایده بدی است](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)) +(نوشتن یک چیز یکسان برای دو بار [تقریباً همیشه ایده بدی است](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)) ما بعداً بیشتر در این مورد صحبت خواهیم کرد. @@ -310,16 +305,16 @@ plt.show() وقتی مفسر به عبارت `generate_data(100)` می‌رسد، بدنه تابع را با `n` برابر با 100 اجرا می‌کند. -نتیجه خالص این است که نام `data` به لیست `ϵ_values` برگردانده شده توسط تابع *متصل* می‌شود. +نتیجه نهایی این است که نام `data` به لیست `ϵ_values` برگردانده شده توسط تابع *متصل* می‌شود. ### اضافه کردن شرط‌ها ```{index} single: Python; Conditions ``` -تابع `generate_data()` ما نسبتاً محدود است. +تابع `()generate_data` ما نسبتاً محدود است. -بیایید آن را با دادن قابلیت برگرداندن یا متغیرهای تصادفی نرمال استاندارد یا متغیرهای تصادفی یکنواخت در $(0, 1)$ بر اساس نیاز، کمی مفیدتر کنیم. +بیایید آن را با دادن قابلیت برگرداندن یا متغیرهای تصادفی نرمال استاندارد یا متغیرهای تصادفی یکنواخت در $(0, 1)$ بر اساس نیاز خودمان، کمی کاربردی‌تر کنیم. این کار در قطعه کد بعدی انجام می‌شود. @@ -340,18 +335,18 @@ plt.plot(data) plt.show() ``` -امیدواریم نحو عبارت if/else خود توضیح‌دهنده باشد، با تورفتگی که دوباره محدوده بلوک‌های کد را مشخص می‌کند. +امیدواریم نحو عبارت if/else خودش گویا باشد؛ در اینجا نیز همانند قبل، تورفتگی محدوده بلوک‌های کد را مشخص می‌کند. -نکات +نکات: -* ما آرگومان `U` را به عنوان یک رشته ارسال می‌کنیم، به همین دلیل آن را به صورت `'U'` می‌نویسیم. +* ما آرگومان `U` را به عنوان یک رشته بکار می‌بریم، به همین دلیل آن را به صورت `'U'` می‌نویسیم. * توجه کنید که برابری با نحو `==` آزمایش می‌شود، نه `=`. * به عنوان مثال، دستور `a = 10` نام `a` را به مقدار `10` اختصاص می‌دهد. - * عبارت `a == 10` به `True` یا `False` ارزیابی می‌شود، بسته به مقدار `a`. + * عبارت `a == 10` با توجه به مقدار`a` میتواند `True` یا `False` ارزیابی شود . حالا، چندین راه وجود دارد که می‌توانیم کد بالا را ساده کنیم. -به عنوان مثال، می‌توانیم شرط‌ها را کاملاً حذف کنیم و فقط نوع تولیدکننده مورد نظر را *به عنوان یک تابع* ارسال کنیم. +به عنوان مثال، می‌توانیم شرط‌ها را کاملاً حذف کنیم و فقط نوع خروجی مورد نظر را *به عنوان یک تابع* به برنامه بدهیم. برای درک این موضوع، نسخه زیر را در نظر بگیرید. @@ -369,7 +364,7 @@ plt.plot(data) plt.show() ``` -حالا، وقتی تابع `generate_data()` را فراخوانی می‌کنیم، `np.random.uniform` را به عنوان آرگومان دوم ارسال می‌کنیم. +حالا، وقتی تابع `()generate_data` را فراخوانی می‌کنیم، `np.random.uniform` را به عنوان آرگومان دوم ارسال می‌کنیم. این شیء یک *تابع* است. @@ -377,7 +372,7 @@ plt.show() * در حالی که این خطوط اجرا می‌شوند، نام‌های `generator_type` و `np.random.uniform` "مترادف" هستند و می‌توانند به روش‌های یکسان استفاده شوند. -این اصل به طور کلی‌تر کار می‌کند---به عنوان مثال، قطعه کد زیر را در نظر بگیرید +این اصل به طور کلی هم کار می‌کند؛ به عنوان مثال، قطعه کد زیر را در نظر بگیرید: ```{code-cell} python3 max(7, 2, 4) # max() is a built-in Python function @@ -388,23 +383,23 @@ m = max m(7, 2, 4) ``` -در اینجا ما نام دیگری برای تابع داخلی `max()` ایجاد کردیم که سپس می‌توانست به روش‌های یکسان استفاده شود. +در اینجا ما نام دیگری برای تابع داخلی `()max` ایجاد کردیم که سپس می‌توانست به روش‌های یکسان استفاده شود. -در زمینه برنامه ما، توانایی اتصال نام‌های جدید به توابع به این معنی است که هیچ مشکلی در *ارسال یک تابع به عنوان آرگومان به تابع دیگر* وجود ندارد---همانطور که در بالا انجام دادیم. +در چارچوب برنامه ما، توانایی اتصال نام‌های جدید به توابع به این معنی است که هیچ مشکلی در *ارسال یک تابع به عنوان آرگومان به تابع دیگر* وجود ندارد؛همانطور که در بالا انجام دادیم. (recursive_functions)= -## فراخوانی‌های بازگشتی تابع (پیشرفته) +## فراخوانی‌ توابع بازگشتی (پیشرفته) ```{index} single: Python; Recursion ``` -این یک موضوع پیشرفته است که می‌توانید آن را رد کنید. +این یک مبحث پیشرفته است که می‌توانید از مطالعه آن صرفنظر کنید. -در عین حال، این ایده جالبی است که باید در مرحله‌ای از حرفه برنامه‌نویسی خود آن را یاد بگیرید. +با این حال، ایده‌ی جالبی است که در مراحل بعدی برنامه‌نویسی خود با آن آشنا شوید. -اساساً، یک تابع بازگشتی تابعی است که خودش را فراخوانی می‌کند. +اساساً، یک تابع بازگشتی(Recursive Function) تابعی است که خودش را فراخوانی می‌کند. -به عنوان مثال، مسئله محاسبه $x_t$ برای برخی از t را در نظر بگیرید که +به عنوان مثال، مسئله محاسبه $x_t$ برای برخی از مقادیر t را در نظر بگیرید که ```{math} :label: xseqdoub @@ -424,7 +419,7 @@ def x_loop(t): return x ``` -همچنین می‌توانیم از یک راه‌حل بازگشتی استفاده کنیم، به شرح زیر +همچنین می‌توانیم از یک راه‌حل بازگشتی همانند زیر استفاده کنیم: ```{code-cell} python3 def x(t): @@ -434,13 +429,13 @@ def x(t): return 2 * x(t-1) ``` -آنچه در اینجا اتفاق می‌افتد این است که هر فراخوانی متوالی از *فریم* خود در *پشته* استفاده می‌کند +آنچه در اینجا اتفاق می‌افتد این است که هر فراخوانی متوالی، فریم(frame) مخصوص خودش را در پشته(stack) استفاده می‌کند. -* فریم جایی است که متغیرهای محلی یک فراخوانی تابع معین نگهداری می‌شود +* فریم جایی است که متغیرهای محلی یک فراخوانی تابع مشخص نگهداری می‌شود * پشته حافظه‌ای است که برای پردازش فراخوانی‌های تابع استفاده می‌شود - * یک صف First In Last Out (FILO) + * و مانند یک صف ورود اول، خروج آخر یا First In Last Out (FILO) عمل می کند. -این مثال تا حدودی ساختگی است، زیرا اولین راه‌حل (تکراری) معمولاً به راه‌حل بازگشتی ترجیح داده می‌شود. +این مثال تا حدودی ساختگی است، زیرا معمولا راه‌حل تکراری(iterative) معمولاً به جای راه‌حل بازگشتی(recursive) ترجیح داده می‌شود. ما بعداً با کاربردهای کمتر ساختگی بازگشت آشنا خواهیم شد. @@ -467,7 +462,7 @@ $n! = n \times (n - 1) \times \cdots \times 2 \times 1$ تعریف می‌شود :class: dropdown ``` -در اینجا یک راه‌حل است: +یک راه حل این است: ```{code-cell} python3 def factorial(n): @@ -503,7 +498,7 @@ factorial(4) :class: dropdown ``` -در اینجا یک راه‌حل است: +یک راه حل این است: ```{code-cell} python3 from numpy.random import uniform @@ -526,15 +521,15 @@ binomial_rv(10, 0.5) :label: func_ex3 ``` -اولاً، تابعی بنویسید که یک تحقق از دستگاه تصادفی زیر را برگرداند +ابتدا، تابعی بنویسید که یک نمونه از دستگاه تصادفی زیر را برگرداند: -1. یک سکه بی‌طرفانه را 10 بار پرتاب کنید. -1. اگر شیر `k` بار یا بیشتر به طور متوالی در این دنباله حداقل یک بار رخ دهد، یک دلار پرداخت کنید. +1. یک سکه را عادلانه 10 بار پرتاب کنید. +1. اگر شیر `k` بار یا بیشتر به طور متوالی در این دنباله حداقل یک بار ظاهر شود، یک دلار پرداخت کنید. 1. در غیر این صورت، چیزی پرداخت نکنید. -ثانیاً، تابع دیگری بنویسید که همان کار را انجام دهد به جز اینکه قانون دوم دستگاه تصادفی بالا به این شکل تبدیل شود +سپس، تابع دیگری بنویسید که همان کار را انجام دهد به جز اینکه قانون دوم دستگاه تصادفی بالا به این شکل تبدیل :شود -- اگر شیر `k` بار یا بیشتر در این دنباله رخ دهد، یک دلار پرداخت کنید. +- اگر شیر `k` بار یا بیشتر در این دنباله ظاهر شود، یک دلار پرداخت کنید. از هیچ import به جز `from numpy.random import uniform` استفاده نکنید. @@ -545,7 +540,7 @@ binomial_rv(10, 0.5) :class: dropdown ``` -در اینجا تابعی برای دستگاه تصادفی اول است. +در اینجا تابعی برای دستگاه تصادفی اول آورده شده است: ```{code-cell} python3 from numpy.random import uniform @@ -567,7 +562,7 @@ def draw(k): # pays if k consecutive successes in a sequence draw(3) ``` -در اینجا تابع دیگری برای دستگاه تصادفی دوم است. +در اینجا تابع دیگری برای دستگاه تصادفی دوم وجود دارد: ```{code-cell} python3 def draw_new(k): # pays if k successes in a sequence @@ -592,7 +587,7 @@ draw_new(3) ## تمرینات پیشرفته -در تمرینات زیر، ما با هم توابع بازگشتی خواهیم نوشت. +در تمرینات زیر، ما با هم توابع بازگشتی را خواهیم نوشت. ```{exercise-start} :label: func_ex4 @@ -617,7 +612,7 @@ x_{t+1} = x_t + x_{t-1}, \quad x_0 = 0, \; x_1 = 1 :class: dropdown ``` -در اینجا راه‌حل استاندارد است +در اینجا راه‌حل استاندارد این مسئله را می توانید ببینید: ```{code-cell} python3 def x(t): @@ -642,7 +637,7 @@ print([x(i) for i in range(10)]) :label: func_ex5 ``` -تابع `factorial()` از [تمرین 1](factorial_exercise) را با استفاده از بازگشت بازنویسی کنید. +تابع `()factorial` از [تمرین 1](factorial_exercise) را با استفاده از بازگشت بازنویسی کنید. ```{exercise-end} ``` @@ -651,7 +646,7 @@ print([x(i) for i in range(10)]) :class: dropdown ``` -در اینجا راه‌حل استاندارد است +در اینجا راه‌حل استاندارد را میتوانید ببینید: ```{code-cell} python3 def recursion_factorial(n): diff --git a/lectures/getting_started.md b/lectures/getting_started.md index 85a7794..6ecc345 100644 --- a/lectures/getting_started.md +++ b/lectures/getting_started.md @@ -42,7 +42,7 @@ kernelspec: ساده ترین راه برای شروع کدنویسی در پایتون، اجرای آن در فضای ابری است. ( یعنی با استفاده از یک سرور از راه دور که از قبل روی آن پایتون نصب شده است.) یکی دیگر از گزینه های رایگان و قابل اعتماد نیز [Google Colab](https://colab.research.google.com/) است. علاوه براین Colab دارای مزیت GPU نیز هست که ما از آن در دوره های پیشرفته تر استفاده خواهیم کرد. آموزش نحوه ی استفاده از Google Colab را می توانید در اینترنت جستجو کنید و ویدیوهای مربوط به آن را پیدا کنید. -بیشتر دوره های ما شامل یک دکمه «راه اندازی نوت بوک» (با یک آیکون پخش) در بالای سمت راست هستند که شما را به یک نسخه قابل اجرا در Colab متصل می کند. +بیشتر دوره های ما یک دکمه «راه اندازی نوت بوک» (با یک آیکون پخش) در بالای صفحه سمت چپ دارد که شما را به یک نسخه قابل اجرا در Colab متصل می کند. ## نصب محلی اگر به سیستم مناسب دسترسی دارید و قصد دارید حجم قابل توجهی برنامه نویسی با پایتون انجام دهید، نصب محلی انتخاب بهتری است. این روش به شما امکان کنترل و انعطاف پذیری بیشتری در استفاده از ابزارها را می دهد. @@ -411,7 +411,7 @@ debugger breakpoint ```{index} single: Jupyter Notebook; nbviewer ``` -فایل های نوت بوک صرفا فایل های متنی با ساختار [JSON](https://en.wikipedia.org/wiki/JSON) هستند که معمولا پسوند `.ipynb` دارند. +فایل های نوت بوک صرفا فایل های متنی با ساختار [JSON](https://en.wikipedia.org/wiki/JSON) هستند که معمولا پسوند `ipynb.` دارند. شما می توانید آن ها را به همان شیوه که سایر فایل ها را به اشتراک می گذارید، ارسال کنید یا اینکه از سرویس های وب مانند [nbviewer](http://nbviewer.jupyter.org/) استفاده کنید. @@ -485,7 +485,7 @@ conda upgrade quantecon ``` ## کار با فایل های پایتون -تا اینجا بیشتر روی اجرای کدهای پایتون در سلول های ژوپیتر نوت بوک تمرکز کردیم. اما به صورت سنتی، بیشتر کدهای پایتون به شکل دیگری اجرا می شوند. به این شکل که ابتدا کدها در یک فایل متنی روی سیستم ذخیره می شوند که به طور قراردادی دارای پسوند `.py` هستند. +تا اینجا بیشتر روی اجرای کدهای پایتون در سلول های ژوپیتر نوت بوک تمرکز کردیم. اما به صورت سنتی، بیشتر کدهای پایتون به شکل دیگری اجرا می شوند. به این شکل که ابتدا کدها در یک فایل متنی روی سیستم ذخیره می شوند که به طور قراردادی دارای پسوند `py.` هستند. برای نمونه می توانیم یک نوع از این فایل ایجاد کنیم: @@ -497,11 +497,11 @@ print("foobar") دستور بالا خط `print("foobar")` را در فایلی به نام `foo.py` در مسیر فعلی ذخیره می کند. -در این کد دستور `%%writefile` نمونه ای از یک جادوی سلولی ([cell magic](http://ipython.readthedocs.org/en/stable/interactive/magics.html#cell-magics)) است! +در این کد دستور `writefile%%` نمونه ای از یک جادوی سلولی ([cell magic](http://ipython.readthedocs.org/en/stable/interactive/magics.html#cell-magics)) است! ### ویرایش و اجرا -اگر با کدی مواجه شدید که در یک فایل با پسوند `*.py` ذخیره شده است، لازم است به این دو سوال توجه کنید: +اگر با کدی مواجه شدید که در یک فایل با پسوند `py.*` ذخیره شده است، لازم است به این دو سوال توجه کنید: 1. چگونه باید آن را اجرا کرد؟ @@ -514,7 +514,7 @@ print("foobar") ```{index} single: JupyterLab ``` -محیط [JupyterLab](https://github.com/jupyterlab/jupyterlab) یک محیط توسعه ی یکپارچه (IDE) است که بر پایه ی نوت بوک ها ژوپیتر ساخته شده است. با استفاده از JupyterLab می توانید فایل هایی با پسوند `*.py` را همانند ژوپیتر نوت بوک ها را ویرایش و اجرا کنید. +محیط [JupyterLab](https://github.com/jupyterlab/jupyterlab) یک محیط توسعه ی یکپارچه (IDE) است که بر پایه ی نوت بوک ها ژوپیتر ساخته شده است. با استفاده از JupyterLab می توانید فایل هایی با پسوند `py.*` را همانند ژوپیتر نوت بوک ها را ویرایش و اجرا کنید. برای اجرای JupyterLab می توانید آن را در فهرست برنامه ها جستجو کنید یا در ترمینال دستور `jupyter-lab` را وارد نمایید. @@ -528,7 +528,7 @@ print("foobar") به طور کلی، ویرایشگر متن برنامه ای است که به طور اختصاصی برای کار با فایل های متنی مانند برنامه های پایتون طراحی شده است. -البته هیچ چیز قدرت و کارایی یک ویرایشگر متن خوب را برای کار با کدهای برنامه نویسی را ندارد. +البته هیچ چیز قدرت و کارایی یک ویرایشگر متن خوب را برای کار با کدهای برنامه نویسی ندارد. یک ویرایشگر خوب امکانات زیر را ارائه می دهد: @@ -551,7 +551,7 @@ print("foobar") حالا دوباره ژوپیتر را این بار با استفاده از کد دستوری `jupyter notebook --no-browser` باز کنید. -با این کار، هسته ی ژوپیتز (kernel) بدون آنکه مرورگر به صورت خودکار باز شود، راه اندازی می شود. +با این کار، هسته ی ژوپیتر (kernel) بدون آنکه مرورگر به صورت خودکار باز شود، راه اندازی می شود. همچنین به پیامی که هنگام شروع نمایش داده می شود دقت کنید: باید یک آدرس(URL) مانند `http://localhost:8888` به شما بدهد که نوت بوک در آن درحال اجرا است. diff --git a/lectures/names.md b/lectures/names.md new file mode 100644 index 0000000..e9e3a61 --- /dev/null +++ b/lectures/names.md @@ -0,0 +1,570 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +heading-map: + overview: مقدمه + variable-names-in-python: نام‌های متغیر در پایتون + namespaces: فضاهای نام + viewing-namespaces: مشاهده فضاهای نام + interactive-sessions: محیط تعاملی + the-global-namespace: فضای نام سراسری + local-namespaces: فضاهای نام محلی + the-__builtins__-namespace: فضای نام داخلی (The `__builtins__` Namespace ) + name-resolution: Name Resolution + indexmutable-single-mutable-versus-indeximmutable-single-immutable-parameters: 'پارامترهای {index}`تغییرپذیر ` در مقابل {index}`تغییرناپذیر `' +--- + +(oop_names)= +```{raw} jupyter + +``` + +# نام‌ها و فضاهای نام + +## مقدمه + +این درس کاملا درباره نام‌ متغیرها است،اینکه چگونه استفاده می‌شوند و مفسر پایتون چگونه آن ها را درک می‌کند. + +ممکن است این موضوع کمی خسته‌کننده به نظر برسد، اما مدلی که پایتون برای مدیریت نام‌ها اتخاذ کرده است، ظریف و جالب است. + +علاوه بر این، اگر درک خوبی از نحوه کار نام‌ها در پایتون داشته باشید، ساعت‌های زیادی را برای اشکال‌زدایی صرف نخواهید کرد. + +(var_names)= +## نام‌های متغیر در پایتون + +```{index} single: Python; Variable Names +``` + +دستور پایتون زیر را در نظر بگیرید: + +```{code-cell} python3 +x = 42 +``` + +اکنون می‌دانیم که وقتی این دستور اجرا می‌شود، پایتون یک شیء از نوع `int` در حافظه کامپیوتر شما ایجاد می‌کند که شامل موارد زیر است: + +* مقدار `42` +* برخی صفات مرتبط + +اما خود `x` چیست؟ + +در پایتون، `x` یک **نام** نامیده می‌شود، و دستور `x = 42` نام `x` را به شیء عدد صحیح که تازه در موردش صحبت کردیم **متصل** می‌کند. + +در پشت صحنه، این فرآیند اتصال نام‌ها به اشیاء به عنوان یک دیکشنری پیاده‌سازی می‌شود، که درباره این موضوع در ادامه بیشتر خواهیم گفت. + +هیچ مشکلی برای اتصال دو یا چند نام به یک شیء وجود ندارد، صرف‌نظر از اینکه آن شیء چه باشد: + +```{code-cell} python3 +def f(string): # Create a function called f + print(string) # that prints any string it's passed + +g = f +id(g) == id(f) +``` + +```{code-cell} python3 +g('test') +``` + +در مرحله اول، یک شیء تابع(function object) ایجاد می‌شود و نام `f` به آن متصل(bind) می‌شود. + +پس از اتصال نام `g` به همان شیء، می‌توانیم از آن در هر جایی که از `f` استفاده می‌کنیم، استفاده کنیم. + +وقتی تعداد نام‌های متصل به یک شیء به صفر می‌رسد، چه اتفاقی می‌افتد؟ + +در اینجا مثالی از این وضعیت آورده شده است، که در آن نام `x` ابتدا به یک شیء متصل می‌شود و سپس به شیء دیگری **دوباره متصل** می‌شود + +```{code-cell} python3 +x = 'foo' +id(x) +x = 'bar' +id(x) +``` + +در این مورد، پس از اینکه `x` را دوباره به `'bar'` متصل می‌کنیم، دیگر هیچ نامی به شیء اول یعنی `'foo'` متصل نیست. + +این وضعیت باعث می‌شود که `'foo'` وارد فرآیند جمع‌آوری زباله (garbage collection) شود. + +به عبارت دیگر، جای حافظه‌ای که آن شیء را ذخیره می‌کند، آزاد می‌شود و به سیستم عامل بازگردانده می‌شود. + +جمع‌آوری زباله در واقع یکی از حوزه های فعال پژوهشی در علوم کامپیوتر است. + +اگر علاقه‌مند هستید، می‌توانید [بیشتر درباره جمع‌آوری زباله بخوانید](https://rushter.com/blog/python-garbage-collector/). + +## فضاهای نام + +```{index} single: Python; Namespaces +``` + +در بحث قبلی دستور زیر را نوشتیم: + +```{code-cell} python3 +x = 42 +``` + +این دستور نام `x` را به عدد صحیح در سمت راست متصل می‌کند. + +همچنین ذکر کردیم که این فرآیند اتصال `x` به شیء صحیح به عنوان یک دیکشنری پیاده‌سازی می‌شود. + +این دیکشنری، فضای نام نامیده می‌شود. + +```{admonition} تعریف +یک **فضای نام** یک جدول از نمادها است که به پایتون می‌گوید هر اسم مربوط به کدام شیء در حافظه است. +``` + +پایتون از چندین فضای نام استفاده می‌کند و در صورت لزوم آن‌ها را به صورت پویا ایجاد می‌کند. + +به عنوان مثال، هر بار که ماژولی را import می‌کنیم، پایتون یک فضای نام برای آن ماژول ایجاد می‌کند. + +برای مشاهده این موضوع در عمل، فرض کنید اسکریپتی به نام `mathfoo.py` می‌نویسیم که فقط یک خط دارد: + +```{code-cell} python3 +%%file mathfoo.py +pi = 'foobar' +``` + +حالا مفسر پایتون را شروع می‌کنیم و آن را import می‌کنیم: + +```{code-cell} python3 +import mathfoo +``` + +بعد بیایید ماژول `math` را از کتابخانه استاندارد import کنیم: + +```{code-cell} python3 +import math +``` + +هر دوی این ماژول‌ها یک صفت به نام `pi` دارند: + +```{code-cell} python3 +math.pi +``` + +```{code-cell} python3 +mathfoo.pi +``` + +این دو اتصال متفاوت `pi` در دو فضای نام جداگانه وجود دارند و هر کدام به صورت یک دیکشنری پیاده‌سازی شده‌اند. + +اگر بخواهید، می‌توانید این دیکشنری را مستقیما با استفاده از `module_name.__dict__` نگاه کنید: + +```{code-cell} python3 +import math + +math.__dict__.items() +``` + +```{code-cell} python3 +import mathfoo + +mathfoo.__dict__ +``` + +همان‌طور که می‌دانید، ما به عناصر موجود در فضای نام، با استفاده از نقطه‌گذاری صفات دسترسی پیدا می‌کنیم. + +```{code-cell} python3 +math.pi +``` + +این دستور کاملاً معادل `math.__dict__['pi']` است: + +```{code-cell} python3 +math.__dict__['pi'] +``` + +## مشاهده فضاهای نام + +همان‌طور که در بالا دیدیم، فضای نام `math` را می‌توان با تایپ کردن `math.__dict__` چاپ (print)کرد. + +راه دیگر برای دیدن محتویات آن تایپ کردن `vars(math)` است: + +```{code-cell} python3 +vars(math).items() +``` + +اگر فقط می‌خواهید نام‌ها را ببینید، می‌توانید دستور زیر را تایپ کنید: + +```{code-cell} python3 +# Show the first 10 names +dir(math)[0:10] +``` + +به نام‌های ویژه `__doc__` و `__name__` توجه کنید. + +این‌ نام ها به محض import شدن هر ماژول، به طور خودکار در فضای نام ایجاد و مقداردهی می‌شوند: + +* `__doc__` رشته مستندسازی ماژول است +* `__name__` نام ماژول است + +```{code-cell} python3 +print(math.__doc__) +``` + +```{code-cell} python3 +math.__name__ +``` + +## محیط تعاملی + +```{index} single: Python; Interpreter +``` + +در پایتون، **تمام** کدهایی که توسط مفسر اجرا می‌شوند داخل یک ماژول اجرا می‌شوند. + +اما دستوراتی که مستقیما در محیط تعاملی (prompt) تایپ می‌کنیم چطور؟ + +این‌ دستورها هم به عنوان کدی در نظر گرفته می‌شوند که داخل یک ماژول اجرا می‌شود؛ در این حالت، آن ماژول `__main__` نام دارد. + +برای بررسی این موضوع، می‌توانیم مقدار `__name__`را درprompt چاپ کنیم تا نام ماژول فعلی را ببینیم. + +```{code-cell} python3 +print(__name__) +``` + +وقتی یک اسکریپت را با استفاده از دستور `run` در IPython اجرا می‌کنیم، محتویات فایل نیز به عنوان بخشی از `__main__` اجرا می‌شوند. + +برای مشاهده این موضوع، بیایید یک فایل `mod.py` ایجاد کنیم که صفت `__name__` خودش را چاپ کند: + +```{code-cell} ipython +%%file mod.py +print(__name__) +``` + +حالا بیایید دو روش متفاوت اجرای آن را در IPython ببینیم: + +```{code-cell} python3 +import mod # Standard import +``` + +```{code-cell} ipython +%run mod.py # Run interactively +``` + +در حالت دوم، کد به عنوان بخشی از `__main__` اجرا می‌شود، بنابراین `__name__` برابر با `__main__` است. + +برای دیدن محتویات فضای نام `__main__` از `()vars` به جای `vars(__main__)` استفاده می‌کنیم. + +اگر این کار را در IPython انجام دهید، تعداد زیادی متغیر خواهید دید که IPython به آن‌ها نیاز دارد و هنگام شروع جلسه مقداردهی اولیه را انجام داده است. + +اگر ترجیح می‌دهید فقط متغیرهایی که شما مقداردهی اولیه کرده‌اید را ببینید، از `whos%` استفاده کنید: + +```{code-cell} ipython +x = 2 +y = 3 + +import numpy as np + +%whos +``` + +## فضای نام سراسری یا جهانی(The Global Namespace) + +```{index} single: Python; Namespace (Global) +``` + +مستندات پایتون اغلب به "فضای نام سراسری" اشاره می‌کند. + +فضای نام سراسری *فضای نام ماژولی است که در حال حاضر در حال اجرا است*. + +به عنوان مثال، فرض کنید که مفسر را باز می‌کنیم و شروع به انجام تخصیص ها(assignments) می‌کنیم. + +اکنون ما در ماژول `__main__` کار می‌کنیم، و از این رو فضای نام برای `__main__` همان فضای نام سراسری است. + +سپس، ماژولی به نام `amodule` را import می‌کنیم: + +```{code-block} python3 +:class: no-execute + +import amodule +``` + +در این نقطه، مفسر یک فضای نام برای ماژول `amodule` ایجاد می‌کند و شروع به اجرای دستورات در ماژول می‌کند. + +در حالی که این اتفاق می‌افتد، فضای نام `amodule.__dict__` فضای نام سراسری است. + +پس از اتمام اجرای ماژول، مفسر به ماژولی که دستور import از آن صادر شده بود بازمی‌گردد. + +در این مثال، این ماژول `__main__` است، بنابراین دوباره فضای نام مربوط به `__main__` به عنوان فضای نام سراسری درنظر گرفته می‌شود. + +## فضای نام محلی(Local Namespaces) + +```{index} single: Python; Namespace (Local) +``` + +نکته مهم: وقتی یک تابع را فراخوانی می‌کنیم، مفسر یک *فضای نام محلی* برای آن تابع ایجاد می‌کند و متغیرها را در آن فضای نام ثبت می‌کند. + +دلیل این امر در همین لحظه توضیح داده خواهد شد. + +متغیرهای موجود در فضای نام محلی *متغیرهای محلی* نامیده می‌شوند. + +پس از بازگشت تابع، فضای نام آزاد می‌شود و از بین می‌رود. + +در حالی که تابع در حال اجرا است، می‌توانیم محتویات فضای نام محلی را با `()locals` مشاهده کنیم. + +به عنوان مثال، دستور زیر را در نظر بگیرید: + +```{code-cell} python3 +def f(x): + a = 2 + print(locals()) + return a * x +``` + +حالا بیایید تابع را فراخوانی کنیم: + +```{code-cell} python3 +f(1) +``` + +می‌توانید فضای نام محلی `f` را قبل از نابود شدن ببینید. + +## فضای نام داخلی (The `__builtins__` Namespace ) + +```{index} single: Python; Namespace (__builtins__) +``` + +ما از توابع داخلی مختلفی مانند `()max` و `()dir` و `()str`و `()list` و `()len` و `()range` و `()type`و غیره، استفاده کرده‌ایم. + +اما دسترسی به این نام‌ها چگونه امکان پذیر است؟ + +* این تعاریف در ماژولی به نام `__builtin__` ذخیره شده‌اند. +* آن‌ها فضای نام مخصوص به خود را دارند که به آن `__builtins__` گفته می‌شود. + +```{code-cell} python3 +# Show the first 10 names in `__main__` +dir()[0:10] +``` + +```{code-cell} python3 +# Show the first 10 names in `__builtins__` +dir(__builtins__)[0:10] +``` + +می‌توانیم به عناصر فضای نام به صورت زیر دسترسی پیدا کنیم: + +```{code-cell} python3 +__builtins__.max +``` + +اما `__builtins__` خاص است، چون همیشه می‌توانیم مستقیماً به آن‌ها هم دسترسی داشته باشیم. + +```{code-cell} python3 +max +``` + +```{code-cell} python3 +__builtins__.max == max +``` + +بخش بعدی توضیح می‌دهد که این کار چگونه امکان پذیر است ... + +## Name Resolution + +```{index} single: Python; Namespace (Resolution) +``` + +فضاهای نام عالی هستند زیرا به ما کمک می‌کنند تا نام‌های متغیر را سازماندهی کنیم. + +(در prompt عبارت `import this` را تایپ کنید و به آخرین موردی که چاپ می‌شود نگاه کنید) + +با این حال، ما باید بفهمیم که مفسر پایتون چگونه با فضاهای نام متعدد کار می‌کند. + +درک جریان اجرا به ما کمک می‌کند تا بررسی کنیم که کدام متغیرها در دسترس هستند و چگونه می‌توانیم هنگام نوشتن و اشکال‌زدایی برنامه‌ها با آن‌ها کار کنیم. + +در هر نقطه از اجرای برنامه، در واقع حداقل دو فضای نام وجود دارند که می‌توان به طور مستقیم به آن‌ها دسترسی داشت. + +("دسترسی مستقیم" به معنای بدون استفاده از نقطه است، مانند `pi` به جای `math.pi`) + +این فضاهای نام عبارتند از: + +* فضای نام سراسری (ماژولی که در حال اجرا است) +* فضای نام داخلی یا builtin + +اگر مفسر در حال اجرای یک تابع باشد، آنگاه فضاهای نامی که مستقیما قابل دسترسی هستند، عبارتند از: + +* فضای نام محلی تابع +* فضای نام سراسری (ماژولی که در حال اجرا است) +* فضای نام داخلی یا builtin + +گاهی اوقات توابع در داخل توابع دیگر تعریف می‌شوند، مانند مثال زیر: + +```{code-cell} python3 +def f(): + a = 2 + def g(): + b = 4 + print(a * b) + g() +``` + +در اینجا `f` تابع *بستار یا دربرگیرنده* برای `g` است و هر تابع فضای نام خود را دارد. + +حالا می‌توانیم قانون نحوه کار Name Resolution را در فضای نام توضیح دهیم: + +ترتیبی که مفسر برای جستجوی نام‌ها دنبال می‌کند عبارت است از: + +1. فضای نام محلی (اگر وجود داشته باشد) +1. زنجیره فضاهای نام توابع بستار (اگر وجود داشته باشند) +1. فضای نام سراسری +1. فضای نام داخلی یا builtin + +اگر نام در هیچ یک از این فضاهای نام نباشد، مفسر یک `NameError` ایجاد می‌کند. + +این قانون **LEGB** نامیده می‌شود (local, enclosing, global, builtin). + +در ادامه یک مثال برای روشن شدن موضوع آورده شده است. + +تصاویر و نمودارهای این مثال توسط [nbtutor](https://github.com/lgpage/nbtutor) در یک ژوپیترنوت‌بوک ایجاد شده‌اند. + +آن‌ها می‌توانند به شما کمک کنند تا برنامه خود را بهتر درک کنید، مخصوصا زمانی که در حال یادگیری یک زبان برنامه نویسی جدید هستید. + +فرض کنید اسکریپتی به نام `test.py` داریم که به صورت زیر است: + +```{code-cell} python3 +%%file test.py +def g(x): + a = 1 + x = x + a + return x + +a = 0 +y = g(10) +print("a = ", a, "y = ", y) +``` + +وقتی این اسکریپت را اجرا می‌کنیم، چه اتفاقی می‌افتد؟ + +```{code-cell} ipython +%run test.py +``` + +ابتدا: + +* فضای نام سراسری با مقدار `{}` ایجاد می‌شود. + +```{figure} /_static/lecture_specific/oop_intro/global.png +``` + +* یک تابع ساخته می‌شود و نام `g` در فضای نام سراسری به آن متصل می‌شود. +* نام `a`هم در فضای سراسری به مقدار `0` متصل می‌شود. + +```{figure} /_static/lecture_specific/oop_intro/global2.png +``` + +سپس `g` از طریق `y = g(10)` فراخوانی می‌شود که منجر به دنباله ای از اقدامات زیر می‌شود: + +* فضای نام محلی برای تابع ایجاد می‌شود. +* نام‌های محلی `x` و `a` متصل می‌شوند، به طوری که فضای نام محلی به `{'x': 10, 'a': 1}` تبدیل می‌شود. + +توجه کنید که فضای نام سراسری `a` تحت تأثیر فضای نام محلی `a` قرار نگرفت. + +```{figure} /_static/lecture_specific/oop_intro/local1.png +``` + +* دستور `x = x + a` از نام های محلی `a` و `x` برای محاسبه `x + a` استفاده می‌کند و نام محلی `x` به نتیجه آن متصل می‌کند. +* این مقدار بازگردانده می‌شود و نام `y` در فضای نام سراسری به آن متصل می‌شود. +* `x` و `a` محلی حذف می‌شوند (و فضای نام محلی آزاد می‌شود). + +```{figure} /_static/lecture_specific/oop_intro/local_return.png +``` + +(mutable_vs_immutable)= +### پارامترهای {index}`تغییرپذیر ` در مقابل {index}`تغییرناپذیر ` + +این زمان خوبی است که کمی بیشتر درباره اشیاء تغییرپذیر و تغییرناپذیر صحبت کنیم. + +فرض کنید قطعه کد زیر را داریم: + +```{code-cell} python3 +def f(x): + x = x + 1 + return x + +x = 1 +print(f(x), x) +``` + +اکنون می‌فهمیم که اینجا چه اتفاقی می‌افتد: کد مقدار `2` را به عنوان خروجی `f(x)` و مقدار `1` را به عنوان مقدار `x` چاپ می‌کند. + +ابتدا `f` و `x` در فضای نام سراسری ثبت می‌شوند. + +فراخوانی `f(x)` یک فضای نام محلی ایجاد می‌کند و `x` را به آن اضافه می‌کند که به `1` متصل است. + +سپس، این `x` محلی دوباره به شیء عدد صحیح جدید `2` متصل می‌شود و این مقدار بازگردانده می‌شود. + +هیچ یک از تغییرات، بر `x` سراسری تأثیر نمی‌گذارد. + +با این حال، وقتی از یک نوع داده **تغییرپذیر** مانند لیست استفاده می‌کنیم، داستان متفاوت است: + +```{code-cell} python3 +def f(x): + x[0] = x[0] + 1 + return x + +x = [1] +print(f(x), x) +``` + +این کد `[2]` را به عنوان مقدار `f(x)` چاپ می‌کند و *همان* مقدار برای `x` سراسری هم نمایش داده می‌شود. + +در اینجا چه اتفاقی می‌افتد: + +* `f` به عنوان یک تابع در فضای نام سراسری ثبت می‌شود + +```{figure} /_static/lecture_specific/oop_intro/mutable1.png +``` + +* `x` به `[1]` در فضای نام سراسری متصل می‌شود + +```{figure} /_static/lecture_specific/oop_intro/mutable2.png +``` + +* فراخوانی `f(x)`: + * یک فضای نام محلی ایجاد می‌کند + * `x` را به فضای نام محلی اضافه می‌کند که به `[1]` متصل است + +```{figure} /_static/lecture_specific/oop_intro/mutable3.png +``` + +```{note} +`x` سراسری و `x` محلی به همان لیست `[1]` اشاره می‌کنند. +``` + +می‌توانیم ببینیم که شناسه `x` محلی و `x` سراسری یکسان است. + +```{code-cell} python3 +def f(x): + x[0] = x[0] + 1 + print(f'the identity of local x is {id(x)}') + return x + +x = [1] +print(f'the identity of global x is {id(x)}') +print(f(x), x) +``` + +* در داخل `f(x)`: + * لیست `[1]` به `[2]` تغییر می‌کند + * لیست `[2]` بازگردانده می‌شود. + +```{figure} /_static/lecture_specific/oop_intro/mutable4.png +``` +* فضای نام محلی آزاد می‌شود و `x` محلی از بین می‌رود + +```{figure} /_static/lecture_specific/oop_intro/mutable5.png +``` + +اگر می‌خواهید `x` محلی و `x` سراسری را به طور جداگانه تغییر دهید، می‌توانید یک نسخه [*کپی*](https://docs.python.org/3/library/copy.html) از لیست بسازید و آن نسخه کپی را به `x` محلی اختصاص دهید. + +این مورد را به عنوان تمرین به شما می‌سپاریم تا بررسی کنید. \ No newline at end of file diff --git a/lectures/oop_intro.md b/lectures/oop_intro.md new file mode 100644 index 0000000..9eb761a --- /dev/null +++ b/lectures/oop_intro.md @@ -0,0 +1,400 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +heading-map: + overview: مقدمه + objects: اشیاء + type: نوع + identity: شناسه + object-content-data-and-attributes: 'محتوای شیء: داده‌ها و ویژگی‌ها' + methods: متدها + inspection-using-rich: بازرسی با استفاده از Rich + a-little-mystery: یک معمای کوچک + summary: خلاصه + exercises: تمرین‌ها +--- + +(oop_intro)= +```{raw} jupyter + +``` + +# برنامه‌نویسی شی‌گرا قسمت اول : اشیاء و متدها + +## مقدمه + +در گذشته، بیشتر زبان های برنامه‌نویسی مانند (Fortran، C، MATLAB و غیره) بر اساس پارادایمی به نام «برنامه‌نویسی [رویه‌ای](https://en.wikipedia.org/wiki/Procedural_programming)» طراحی شده بودند. در این پارادایم، برنامه به صورت مجموعه‌ای از دستورالعمل ها و توابع نوشته می‌شود که به ترتیب اجرا شده و داده‌ها را پردازش می‌کند. + +این پارادایم به شرح زیر عمل می‌کند: + +* برنامه دارای یک وضعیت است که متناظر با مقادیر متغیرهای خود است. +* توابع فراخوانی می‌شوند تا بر روی این وضعیت عمل کرده و آن را تغییر دهند. +* خروجی‌های نهایی از طریق دنباله‌ای از فراخوانی‌های توابع تولید می‌شوند. + +دو پارادایم مهم دیگر، [برنامه‌نویسی شی‌گرا](https://en.wikipedia.org/wiki/Object-oriented_programming) (OOP) و [برنامه‌نویسی تابعی](https://en.wikipedia.org/wiki/Functional_programming) هستند. + +در پارادایم OOP، داده‌ها و توابع در قالب "اشیاء" با یکدیگر ترکیب می‌شوند، و توابع در این زمینه به عنوان **متدها** شناخته می‌شوند. + +متدها برای تغییر داده‌های موجود در شیء فراخوانی می‌شوند. + +* به یک لیست پایتون فکر کنید که حاوی داده است و متدهایی مانند `()append` و `()pop` دارد که داده را تغییر می‌دهند. + +زبان‌های برنامه‌نویسی تابعی، بر اساس ایده ترکیب توابع ساخته شده‌اند. + +* نمونه‌های معروف این دسته شامل [Lisp](https://en.wikipedia.org/wiki/Common_Lisp)، [Haskell](https://en.wikipedia.org/wiki/Haskell) و [Elixir](https://en.wikipedia.org/wiki/Elixir_(programming_language)) هستند. + +پس پایتون در کدام یک از این دسته‌ها قرار می‌گیرد؟ + +در واقع پایتون یک زبان عمل‌گرا است که به جای پیروی سخت گیرانه از یک رویکرد خالص، سبک‌های شی‌گرا، تابعی و رویه‌ای را با یکدیگر ترکیب می‌کند. + +از یک طرف، این ویژگی به پایتون و کاربرانش اجازه می‌دهد تا جنبه‌های خوب پارادایم‌های مختلف را انتخاب کنند. + +و از طرف دیگر، همین نبود خلوص پارادایمی ممکن است گاهی اوقات منجر به برخی سردرگمی‌ها شود. + +خوشبختانه اگر بدانیم که در سطح بنیادی، پایتون یک زبان شی‌گرا *است*، این سردرگمی تا حد زیادی به حداقل می‌رسد. + +منظور ما این است که، در پایتون، *همه چیز یک شیء است*. + +در این درس، توضیح می‌دهیم که این گزاره به چه معناست و چرا اهمیت دارد. + +ما از کتابخانه خارجی زیر استفاده خواهیم کرد: + +```{code-cell} python3 +:tags: [hide-output] +!pip install rich +``` + +## اشیاء + +```{index} single: Python; Objects +``` + +در پایتون، یک *شیء* مجموعه‌ای از داده‌ها و دستورالعمل‌های نگهداری‌شده در حافظه کامپیوتر است که شامل موارد زیر می‌شود: + +1. یک نوع(Type) +1. یک شناسه منحصر به فرد(Unique Identity) +1. داده (یعنی محتوا) +1. متدها(Methods) + +این مفاهیم به ترتیب تعریف و بررسی می‌شوند. + +(type)= +### نوع (Type) + +```{index} single: Python; Type +``` + +پایتون انواع مختلفی از اشیاء را فراهم می‌کند تا بتواند دسته‌های مختلفی از داده ها را پشتیبانی کند. + +به عنوان مثال: + +```{code-cell} python3 +s = 'This is a string' +type(s) +``` + +```{code-cell} python3 +x = 42 # حالا بیایید یک عدد صحیح ایجاد کنیم +type(x) +``` + +نوع یک شیء برای بسیاری از عبارات مهم است. + +به عنوان مثال، عملگر جمع بین دو رشته متنی به معنای به هم پیوند دادن آنها است: + +```{code-cell} python3 +'300' + 'cc' +``` + +از طرف دیگر، بین دو عدد به معنای جمع معمولی است: + +```{code-cell} python3 +300 + 400 +``` + +عبارت زیر را در نظر بگیرید: + +```{code-cell} python3 +--- +tags: [raises-exception] +--- +'300' + 400 +``` + +در اینجا ما در حال ترکیب انواع متفاوتی از مقادیر هستیم، و برای پایتون مشخص نیست که آیا کاربر می‌خواهد: + +* `'300'` را به عدد صحیح تبدیل کند و سپس آن را به `400` اضافه کند، یا +* `400` را به رشته تبدیل کند و سپس آن را به `'300'` متصل کند. + +برخی زبان‌ها ممکن است سعی کنند حدس بزنند، اما پایتون یک زبان *تایپ قوی (strongly typed)* است. + +* نوع داده اهمیت زیادی دارد و تبدیل ضمنی نوع در پایتون به ندرت انجام می‌شود. +* در نتیجه پایتون به جای حدس زدن، با نشان دادن یک خطای `TypeError` پاسخ می‌دهد. + +برای جلوگیری از این خطا، باید با تغییر نوع داده مربوطه، منظور خود را به صورت صریح وشفاف مشخص کنید. + +به عنوان مثال: + +```{code-cell} python3 +int('300') + 400 # برای جمع به عنوان اعداد، رشته را به عدد صحیح تبدیل کنید +``` + +(identity)= +### شناسه (Unique Identity) + +```{index} single: Python; Identity +``` + +در پایتون، هر شیء یک شناسه منحصر به فرد دارد که به پایتون (و ما) کمک می‌کند تا شیء را ردیابی و تشخیص دهیم. + +شناسه یک شیء را می‌توان از طریق تابع `()id` به دست آورد: + +```{code-cell} python3 +y = 2.5 +z = 2.5 +id(y) +``` + +```{code-cell} python3 +id(z) +``` + +در این مثال، `y` و `z` با اینکه مقدار یکسانی (یعنی `2.5`) دارند، اما آن‌ها یک شیء نیستند. + +شناسه یک شیء در واقع فقط آدرس شیء در حافظه است. + +### محتوای شیء: داده‌ها و ویژگی‌ها + +```{index} single: Python; Content +``` + +اگر `x = 42` را قرار دهیم، یک شیء از نوع `int` ایجاد می‌کنیم که حاوی داده `42` است. + +در واقع، همانطور که مثال زیر نشان می‌دهد، حاوی اطلاعات بیشتری نیز هست: +```{code-cell} python3 +x = 42 +x +``` + +```{code-cell} python3 +x.imag +``` + +```{code-cell} python3 +x.__class__ +``` + +وقتی پایتون این شیء که از نوع عدد صحیح است را ایجاد می‌کند، اطلاعات کمکی مختلفی مانند قسمت موهومی(imaginary) و نوع(type) شیء را با آن ذخیره می‌کند. + +هر نامی که بعد از نقطه بیاید *ویژگی(attribute)* شیء سمت چپ نقطه نامیده می‌شود. + +* به عنوان مثال، `imag` و `__class__` ویژگی‌های `x` هستند. + +از این مثال می‌بینیم که اشیاء دارای ویژگی‌هایی هستند که حاوی اطلاعات کمکی هستند. + + همچنین، ویژگی‌هایی وجود دارند که مانند توابع عمل می‌کنند و *متد(methods)* نامیده می‌شوند + +این ویژگی‌ها مهم هستند، بنابراین بیایید آن‌ها را به طور عمیق بررسی کنیم. + +(methods)= +### متدها + +```{index} single: Python; Methods +``` + +متدها *توابعی هستند که به طور مستقیم با یک شیء مرتبط شده و جزئی از آن شیء محسوب می‌شوند*. + +به طور رسمی، متدها ویژگی‌های اشیاء هستند که **قابل فراخوانی** هستند، یعنی ویژگی‌هایی که می‌توان آن ها را مانند توابع فراخوانی کرد. + +```{code-cell} python3 +x = ['foo', 'bar'] +callable(x.append) +``` + +```{code-cell} python3 +callable(x.__doc__) +``` + +متدها معمولاً بر روی داده‌های موجود در شیئی که به آن تعلق دارند عمل می‌کنند، یا آن داده را با داده‌های دیگر ترکیب می‌کنند: + +```{code-cell} python3 +x = ['a', 'b'] +x.append('c') +s = 'This is a string' +s.upper() +``` + +```{code-cell} python3 +s.lower() +``` + +```{code-cell} python3 +s.replace('This', 'That') +``` + +بخش بزرگی از قابلیت‌های پایتون حول فراخوانی‌های متدها سازماندهی شده است. + +به عنوان مثال، کد زیر را در نظر بگیرید: + +```{code-cell} python3 +x = ['a', 'b'] +x[0] = 'aa' # تغییر آیتم با استفاده از براکت +x +``` + +با اینکه به نظر می‌رسد که در اینجا از هیچ متدی استفاده نشده باشد، اما در واقع استفاده از براکت ها برای تغییر دادن مقادیر فقط یک رابط ساده و راحت برای فراخوانی یک متد است. + +آنچه در واقع اتفاق می‌افتد این است که پایتون متد `__setitem__` را به شرح زیر فراخوانی می‌کند: + +```{code-cell} python3 +x = ['a', 'b'] +x.__setitem__(0, 'aa') # معادل x[0] = 'aa' +x +``` + +(اگر بخواهید می‌توانید متد `__setitem__` را تغییر دهید، به طوری که استفاده از براکت ها برای مقادیر کار کاملاً متفاوتی انجام دهد) + +## بازرسی با استفاده از Rich + +یک بسته بسیار کاربردی به نام [rich](https://github.com/Textualize/rich) وجود دارد که به ما کمک می‌کند محتویات یک شیء را به صورت خوانا مشاهده کنیم. + +به عنوان مثال: + +```{code-cell} python3 +from rich import inspect +x = 10 +inspect(10) +``` +برای دیدن متدها هم میتوانیم از این روش استفاده کنیم: +```{code-cell} python3 +inspect(10, methods=True) +``` + +در واقع متدهای بیشتری هم وجود دارند، و شما می‌توانید با اجرای `inspect(10, all=True)` آنها را مشاهده کنید. + +## یک معمای کوچک + +در این سخنرانی ادعا کردیم که پایتون، در ذات خود یک زبان شی‌گرا است. + +اما در اینجا مثالی وجود دارد که بیشتر رویه‌ای به نظر می‌رسد: + +```{code-cell} python3 +x = ['a', 'b'] +m = len(x) +m +``` + +اگر پایتون شی‌گرا است، چرا از `()x.len` استفاده نمی‌کنیم؟ + +پاسخ به این نکته مربوط می‌شود به اینکه پایتون به قابلیت خوانایی و سبک ثابت و یکسان اهمیت می‌دهد. + +در پایتون، معمولا کاربران اشیاء سفارشی می‌سازند، ما نیز نحوه انجام این کار را {doc}`بعداً ` بررسی خواهیم. + +معمولا کاربران متدهایی به اشیاء خود اضافه می‌کنند که طول شیء را، به طوریکه برای شیء مناسب باشد، اندازه‌گیری کنند. + +هنگام نام‌گذاری چنین متدی، انتخاب‌های طبیعی `()len` و `()length` هستند. + +اگر برخی از کاربران `()len` و برخی دیگر `()length` را انتخاب کنند، سبک ناسازگار و ناهماهنگ شده و به خاطر سپردن آن سخت تر خواهد بود. + +برای جلوگیری از این امر، سازنده پایتون تصمیم گرفت `()len` را به عنوان یک تابع داخلی اضافه کند، تا تأکید کند بر اینکه استفاده از `()len` قراردادی است. + +حالا، با گفتن همه این‌ها، پایتون همچنان در پشت صحنه شی‌گرا *است*. + +در واقع، لیست `x` که پیشتر بررسی شد، دارای متدی به نام `()__len__` است. + +و تنها کاری که تابع `()len` انجام می‌دهد فراخوانی این متد است. + +به عبارت دیگر، معادل کد زیر است: + +```{code-cell} python3 +x = ['a', 'b'] +len(x) +``` +و + +```{code-cell} python3 +x = ['a', 'b'] +x.__len__() +``` + +## خلاصه + +نکته اصلی این درس این است: + +* در پایتون، *همه چیز در حافظه به عنوان یک شیء در نظر گرفته می‌شود*. + +و این فقط شامل لیست‌ها، رشته‌ها و غیره نیست، بلکه مواردی که کمتر مورد توجه قرار میگیرد را نیز شامل می‌شود، مانند: + +* توابع (پس از بارگذاری در حافظه) +* ماژول‌ها +* فایل‌های باز شده برای خواندن یا نوشتن +* اعداد صحیح و غیره + +به یاد داشتن اینکه همه چیز یک شیء است به شما کمک می‌کند تا با برنامه‌های خود تعامل داشته باشید و کد پایتونی واضحی بنویسید. + +## تمرین‌ها + +```{exercise-start} +:label: oop_intro_ex1 +``` + +ما قبلاً با {any}`نوع داده بولین ` آشنا شدیم. + +با استفاده از آنچه در این درس آموختیم، لیستی از متدهای شیء بولین `True` را چاپ کنید. + +```{hint} +:class: dropdown + +می‌توانید از `callable()` برای آزمایش اینکه آیا یک ویژگی از یک شیء می‌تواند به عنوان تابع فراخوانی شود، استفاده کنید +``` + +```{exercise-end} +``` + +```{solution-start} oop_intro_ex1 +:class: dropdown +``` + +ابتدا، باید همه ویژگی‌های `True` را پیدا کنیم، که با کد زیر میتوانید پیدا کنید: + +```{code-cell} python3 +print(sorted(True.__dir__())) +``` + +یا + +```{code-cell} python3 +print(sorted(dir(True))) +``` + +از آنجایی که نوع داده بولین یک نوع داده اولیه است، می‌توانید آن را در مجموعه پیش فرض توابع و نام ها (built-in) نیز پیدا کنید: + +```{code-cell} python3 +print(dir(__builtins__.bool)) +``` + +در اینجا از یک حلقه `for` برای فیلتر کردن ویژگی‌هایی که قابل فراخوانی هستند استفاده می‌کنیم: + +```{code-cell} python3 +attributes = dir(__builtins__.bool) +callablels = [] + +for attribute in attributes: + # از eval() برای ارزیابی یک رشته به عنوان یک عبارت استفاده کنید + if callable(eval(f'True.{attribute}')): + callablels.append(attribute) +print(callablels) +``` + +```{solution-end} +``` \ No newline at end of file diff --git a/lectures/python_essentials.md b/lectures/python_essentials.md new file mode 100644 index 0000000..9708581 --- /dev/null +++ b/lectures/python_essentials.md @@ -0,0 +1,1086 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +heading-map: + overview: مقدمه + data-types: انواع داده + primitive-data-types: انواع داده ابتدایی + boolean-values: مقادیر Boolean + numeric-types: انواع داده عددی + containers: ظرف ها + slice-notation: نشانه‌گذاری Slice + sets-and-dictionaries: مجموعه‌ها و دیکشنری ها + input-and-output: ورودی و خروجی + paths: مسیرها + iterating: تکرار + looping-over-different-objects: حلقه تکرار اشیاء مختلف + looping-without-indices: حلقه تکرار بدون ایندکس‌ها + list-comprehensions: خلاصه لیست‌ها + comparisons-and-logical-operators: عملگرهای مقایسه‌ای و منطقی + comparisons: عملگرهای مقایسه‌ای + combining-expressions: ترکیب عبارات + coding-style-and-documentation: سبک کدنویسی و مستندسازی + python-style-guidelines-pep8: راهنمای سبک کدنویسی پایتون: PEP8 + docstrings: داک‌استرینگ‌ها (docstring) + exercises: تمرین‌ها +--- + +(python_done_right)= +```{raw} jupyter + +``` + +# اصول برنامه‌نویسی پایتون + +## مقدمه + +ما تاکنون مطالب زیادی را با تمرکز بر مثال‌ها، به‌سرعت پوشش داده‌ایم. + +اکنون بیایید برخی ویژگی‌های اصلی پایتون را به‌صورت نظام‌مندتر بررسی کنیم. + +این روش شاید کمتر هیجان‌انگیز باشد، اما به درک بهتر جزئیات کمک می‌کند. + +## انواع داده + +```{index} single: Python; Data Types +``` + +برنامه‌های کامپیوتری مجموعه‌ای از انواع داده را مدیریت می‌کنند. +برای مثال، `1.5` یک عدد اعشاری (float) است، در حالی که `1` یک عدد صحیح (integer) محسوب می‌شود. در برنامه نویسی باید بین این دو نوع تفاوت قائل شوید، زیرا در حافظه به شکل متفاوتی ذخیره می‌شوند و عملیات ریاضی روی آنها فرق دارد. + + * به عنوان نمونه، در بیشتر رایانه‌ها محاسبات اعشاری توسط یک واحد خاص به نام **واحد ممیز شناور (FPU)** انجام می‌شود. + + +به طور کلی، اعداد اعشاری اطلاعات بیشتری دارند اما عملیات ریاضی روی اعداد صحیح سریع‌تر و دقیق‌تر است. + + + * پایتون چندین نوع دادهٔ داخلی دیگر نیز دارد، مثل رشته‌ها (strings) و فهرست‌ها (lists). + +بیایید بیشتر با آن‌ها آشنا شویم. + +### انواع داده اولیه (Primitive Data Types) + +(boolean)= +#### مقادیر بولی (Boolean) + +یک نوع از داده های اولیه، مقادیر **بولی** هستند که می‌توانند فقط `True` (درست) یا `False` (نادرست) باشند. + +```{code-cell} python3 +x = True +x +``` + +می‌توانیم نوع هر شیء در حافظه را با استفاده از تابع `()type` بررسی کنیم: + +```{code-cell} python3 +type(x) +``` + +مثلاً در مثال زیر، پایتون مقدار عبارت سمت راست را محاسبه کرده و آن را به متغیر `y` نسبت می‌دهد: + +```{code-cell} python3 +y = 100 < 10 +y +``` + +```{code-cell} python3 +type(y) +``` + +در عبارات ریاضی، `True` معادل `1` و `False` معادل `0` در نظر گرفته می‌شود. + +این ویژگی را **محاسبات بولی (Boolean arithmetic)** می‌نامند و اغلب در برنامه نویسی کاربردی است. + +مثال های زیر نمونه هایی از این دست هستند: + +```{code-cell} python3 +x + y +``` + +```{code-cell} python3 +x * y +``` + +```{code-cell} python3 +True + True +``` + +```{code-cell} python3 +bools = [True, True, False, True] # List of Boolean values + +sum(bools) +``` + +#### انواع داده‌های عددی + +داده های عددی از دیگر داده‌های اولیه هستند. + +همانطور که پیشتر با داده های صحیح (integer) و اعشاری (float) یا به اختصار `int`و `float` آشنا شدید؛ نوعی دیگر از داده های عددی **اعداد مختلط (complex)** نام دارند. + +همانند مثال زیر: + +```{code-cell} python3 +x = complex(1, 2) +y = complex(2, 1) +print(x * y) + +type(x) +``` + +### ظرف ها یا ساختارهای نگه دارنده(Containers) + +پایتون چندین نوع پایه برای ذخیره سازی مجموعه هایی از داده ها(احتمالا ناهمگن) دارد. + + +پییش‌تر با {ref}`لیست‌ها ` آشنا شدیم. + +```{index} single: Python; Tuples +``` + +نوعی دیگر از آن، **تاپل (tuple)** است که «غیرقابل تغییر» (immutable) می باشد. + +```{code-cell} python3 +x = ('a', 'b') # Parentheses instead of the square brackets +x = 'a', 'b' # Or no brackets --- the meaning is identical +x +``` + +```{code-cell} python3 +type(x) +``` + +اگر شیئی پس از ساخته شدن قابل تغییر نباشد، **immutable** است. +در مقابل، اگر قابل ویرایش باشد، **mutable** نام دارد. + +در پایتون لیست ها قابل تغییر هستند: + +```{code-cell} python3 +x = [1, 2] +x[0] = 10 +x +``` + +اما tuple‌ها قابل تغییر نیستند: + +```{code-cell} python3 +--- +tags: [raises-exception] +--- +x = (1, 2) +x[0] = 10 +``` + +کمی بعدتر درباره نقش داده‌های قابل تغییر و تغییرناپذیر بیشتر صحبت خواهیم کرد. + +می‌توان تاپل‌ها (و لیست‌ها) را به صورت زیر «بازکرد» (unpack) کرد: + +```{code-cell} python3 +integers = (10, 20, 30) +x, y, z = integers +x +``` + +```{code-cell} python3 +y +``` + +در واقع شما قبلاً {ref}`نمونه‌ای از این را دیده‌اید `. + +باز کردن tuple راحت است و ما اغلب از آن استفاده خواهیم کرد. + +#### نشانه گذاری برش (Slice Notation) +```{index} single: Python; Slicing +``` + +برای دسترسی به چندین عنصر از یک دنباله (یک لیست، یک tuple یا یک رشته)، می‌توانید از نشانه‌گذاری slice در پایتون استفاده کنید. + +برای مثال: + +```{code-cell} python3 +a = ["a", "b", "c", "d", "e"] +a[1:] +``` + +```{code-cell} python3 +a[1:3] +``` + +قانون کلی این است که `a[m:n]` تعداد `n - m` عنصر را بازمی‌گرداند، که از `a[m]` شروع می‌شود. + +اعداد منفی نیز مجاز هستند: + +```{code-cell} python3 +a[-2:] # Last two elements of the list +``` + +همچنین می‌توانید از قالب `[start:end:step]` برای مشخص کردن گام برش استفاده کنید: + +```{code-cell} python3 +a[::2] +``` + +با استفاده از یک گام منفی، می‌توانید دنباله را به صورت معکوس برگردانید: + +```{code-cell} python3 +a[-2::-1] # Walk backwards from the second last element to the first element +``` + +همان نشانه‌گذاری slice روی tuple‌ها و رشته‌ها نیز کار می‌کند: + +```{code-cell} python3 +s = 'foobar' +s[-3:] # Select the last three elements +``` + +#### مجموعه‌ها و دیکشنری‌ها (Sets and Dictionaries) + +```{index} single: Python; Sets +``` + +```{index} single: Python; Dictionaries +``` + +دو نوع دیگر از ظرف ها در پایتون،[مجموعه ها (sets)](https://docs.python.org/3/tutorial/datastructures.html#sets) و [دیکشنری ها (dictionaries)](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) هستند. + +دیکشنری‌ها بسیار شبیه لیست‌ها هستند، با این تفاوت که آیتم‌ها به جای شماره‌گذاری، نام‌گذاری می‌شوند: + +```{code-cell} python3 +d = {'name': 'Frodo', 'age': 33} +type(d) +``` + +```{code-cell} python3 +d['age'] +``` + +در این مثال از دیکشنری، نام های `'name'` و`'age'` *کلیدها(keys)* نامیده می‌شوند. + +اشیایی که کلیدها به آن‌ها ارجاع می‌دهند (`'Frodo'` و `33`) `مقادیر(Values)` نامیده می‌شوند. + +**مجموعه‌ها(sets)** دسته های بدون ترتیب و بدون عنصر تکراری هستند و متدهای مجموعه،عملیات معمول مربوط به مجموعه ها را فراهم می کنند: + +```{code-cell} python3 +s1 = {'a', 'b'} +type(s1) +``` + +```{code-cell} python3 +s2 = {'b', 'c'} +s1.issubset(s2) +``` + +```{code-cell} python3 +s1.intersection(s2) +``` + +تابع `()set` مجموعه ها را از دنباله ها می سازد: + +```{code-cell} python3 +s3 = set(('foo', 'bar', 'foo')) +s3 +``` + +## ورودی و خروجی (Input and Output) + +```{index} single: Python; IO +``` + +بیایید به طور خلاصه خواندن و نوشتن در فایل‌های متنی را مرور کنیم. + +با نوشتن شروع می‌کنیم: + +```{code-cell} python3 +f = open('newfile.txt', 'w') # Open 'newfile.txt' for writing +f.write('Testing\n') # Here '\n' means new line +f.write('Testing again') +f.close() +``` + +در اینجا: + +* تابع سازنده داخلی `()open`یک فایل برای نوشتن ایجاد می کند. +* هم `()write` و هم `()close` متدهای فایل های ایجادشده هستند. + +این فایلی که ساخته‌ایم کجاست؟ + +به خاطر داشته باشید که پایتون مفهوم دایرکتوری فعلی (present working directory یا pwd) شما را حفظ می کند، که در Jupyter یا IPython می توان آن را از طریق دستور زیر پیدا کرد: + + +```{code-cell} ipython +%pwd +``` + +اگر مسیری مشخص نشده باشد، پایتون در همین مکان(پوشه جاری)فایل را می نویسد. + +ما همچنین میتوانیم با پایتون محتوای فایل `newline.txt` را به صورت زیر بخوانیم: + +```{code-cell} python3 +f = open('newfile.txt', 'r') +out = f.read() +out +``` + +```{code-cell} python3 +print(out) +``` + +در واقع، رویکرد پیشنهادی در پایتون مدرن، استفاده از یک دستور `with` است تا اطمینان حاصل شود که فایل‌ها به درستی دریافت و منتشر می‌شوند. + +محدود کردن عملیات در همان بلوک نیز وضوح کد شما را بهبود می‌بخشد. + +```{note} +این نوع بلوک به طور رسمی به عنوان یک [*context*](https://realpython.com/python-with-statement/#the-with-statement-approach) شناخته می‌شود. +``` + +بیایید سعی کنیم دو مثال بالا را به عبارت `with` تبدیل کنیم. + +ابتدا مثال نوشتن را تغییر می‌دهیم: +```{code-cell} python3 + +with open('newfile.txt', 'w') as f: + f.write('Testing\n') + f.write('Testing again') +``` + +توجه کنید که نیازی به فراخوانی متد `()close` نداریم زیرا بلوک `with` تضمین می‌کند که جریان(stream) در پایان بلوک بسته شود. + +با کمی تغییر، می‌توانیم فایل‌ها را با استفاده از دستور `with` نیز بخوانیم: + +```{code-cell} python3 +with open('newfile.txt', 'r') as fo: + out = fo.read() + print(out) +``` +اکنون فرض کنید که می‌خواهیم ورودی را از یک فایل بخوانیم و خروجی را در فایل دیگری بنویسیم. +در اینجا نحوه انجام این کار در حالی که به درستی منابع را دریافت و به سیستم عامل بازمی‌گردانیم با استفاده از عبارات `with` آمده است: + +```{code-cell} python3 +with open("newfile.txt", "r") as f: + file = f.readlines() + with open("output.txt", "w") as fo: + for i, line in enumerate(file): + fo.write(f'Line {i}: {line} \n') +``` + +فایل خروجی به صورت زیر خواهد بود: + +```{code-cell} python3 +with open('output.txt', 'r') as fo: + print(fo.read()) +``` + +می‌توانیم مثال بالا را ساده کنیم با این کار که دو دستور `with` را در یک خط قرار دهیم: + +```{code-cell} python3 +with open("newfile.txt", "r") as f, open("output2.txt", "w") as fo: + for i, line in enumerate(f): + fo.write(f'Line {i}: {line} \n') +``` + +فایل خروجی یکسان خواهد بود: + +```{code-cell} python3 +with open('output2.txt', 'r') as fo: + print(fo.read()) +``` + +فرض کنید می‌خواهیم به جای بازنویسی فایل موجود به نوشتن در آن ادامه دهیم. + +می‌توانیم حالت را به `a` تغییر دهیم که مخفف حالت append است: + +```{code-cell} python3 +with open('output2.txt', 'a') as fo: + fo.write('\nThis is the end of the file') +``` + +```{code-cell} python3 +with open('output2.txt', 'r') as fo: + print(fo.read()) +``` + +```{note} +توجه کنید که ما فقط حالت‌های `r`، `w` و `a` را در اینجا پوشش دادیم که رایج‌ترین حالت‌های مورد استفاده هستند. +پایتون [حالت‌های متنوعی](https://www.geeksforgeeks.org/python/reading-writing-text-files-python/) ارائه می‌دهد که می‌توانید با آن‌ها آزمایش کنید. +``` + +### مسیرها (Paths) + +```{index} single: Python; Paths +``` + +توجه کنید که اگر `newfile.txt` در دایرکتوری کاری فعلی نباشد، فراخوانی `()open` با خطا مواجه می‌شود. + +در این حالت، می‌توانید فایل را به pwd منتقل کنید یا [مسیر کامل](https://en.wikipedia.org/wiki/Path_%28computing%29) فایل را مشخص کنید: + +```{code-block} python3 +:class: no-execute + +f = open('insert_full_path_to_file/newfile.txt', 'r') +``` + +(iterating_version_1)= +## تکرار(Iteration) + +```{index} single: Python; Iteration +``` + +یکی از مهم‌ترین وظایف در علوم کامپیوتر، عبور گام‌به‌گام از یک دنباله از داده‌ها و انجام یک عمل مشخص است. + +یکی از نقاط قوت پایتون رابط کاربری ساده و انعطاف‌پذیر آن برای این نوع تکرار از طریق حلقه `for` است. + +### حلقه تکرار روی اشیاء مختلف + +بسیاری از اشیاء پایتون "قابل تکرار" هستند، به این معنی که می‌توان روی آن‌ها حلقه(loop) زد. + +برای ارائه یک مثال، بیایید فایل us_cities.txt را بنویسیم که شهرهای ایالات متحده و جمعیت آن‌ها را فهرست می‌کندو در پوشه کاری فعلی ذخیره می گردد: + +(us_cities_data)= +```{code-cell} ipython +%%writefile us_cities.txt +new york: 8244910 +los angeles: 3819702 +chicago: 2707120 +houston: 2145146 +philadelphia: 1536471 +phoenix: 1469471 +san antonio: 1359758 +san diego: 1326179 +dallas: 1223229 +``` + +در اینجا `writefile%%` یک [IPython cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#cell-magics) است. + +فرض کنید می‌خواهیم اطلاعات را با بزرگ کردن نام‌ها و اضافه کردن ویرگول برای نشان دادن هزارگان خواناتر کنیم. + +برنامه زیر داده‌ها را می‌خواند و تبدیل را انجام می‌دهد: + +```{code-cell} python3 +data_file = open('us_cities.txt', 'r') +for line in data_file: + city, population = line.split(':') # Tuple unpacking + city = city.title() # Capitalize city names + population = f'{int(population):,}' # Add commas to numbers + print(city.ljust(15) + population) +data_file.close() +``` + +در اینجا `'f` یک f-string است [که برای درج متغیرها در رشته‌ها استفاده می‌شود](https://docs.python.org/3/library/string.html#formatspec). + +قالب‌بندی مجدد هر خط نتیجه سه متد مختلف رشته است که بررسی جزئیات آن‌ها را می‌توان برای بعد گذاشت. + +بخش جالب این برنامه برای ما خط 2 است که نشان می‌دهد: + +1. شیء فایل `data_file` قابل تکرار است، به این معنی که می‌توان آن را در سمت راست `in` در یک حلقه `for` قرار داد. +1. تکرار به صورت خط به خط از فایل عبور می‌کند. + +این همان چیزی است که منجر به سینتکس ساده و کاربردی نمایش داده شده در برنامه ما می شود. + +بسیاری از انواع دیگر اشیاء قابل تکرار هستند و ما بعداً درباره برخی از آن‌ها بحث خواهیم کرد. + +### حلقه تکرار بدون ایندکس‌ها + +یکی از چیزهایی که ممکن است متوجه شده باشید این است که پایتون تمایل دارد مستقیما روی آیتم ها حلقه بزنیم، بدون اینکه شماره موقعیت(یا همان ایندکس) هر آیتم را به صورت صریح استفاده کنیم. + +برای مثال: + +```{code-cell} python3 +x_values = [1, 2, 3] # Some iterable x +for x in x_values: + print(x * x) +``` + +به این ترجیح داده می‌شود: + +```{code-cell} python3 +for i in range(len(x_values)): + print(x_values[i] * x_values[i]) +``` + +وقتی این دو گزینه را مقایسه می‌کنید، می‌توانید ببینید چرا اولی ترجیح داده می‌شود. + +پایتون امکاناتی دارد که کار با حلقه ها را بدون ایندکس ساده می‌کند. + +یکی از این امکانات `()zip` است که به شما اجازه می‌دهد دو لیست را هم زمان با هم طی کنید و هر بار یک جفت از عناصر آنها را بگیرید. + +برای مثال، کد زیر را اجرا کنید: + +```{code-cell} python3 +countries = ('Japan', 'Korea', 'China') +cities = ('Tokyo', 'Seoul', 'Beijing') +for country, city in zip(countries, cities): + print(f'The capital of {country} is {city}') +``` + +تابع `()zip` همچنین برای ایجاد دیکشنری‌ها کاربردی است، برای مثال: + +```{code-cell} python3 +names = ['Tom', 'John'] +marks = ['E', 'F'] +dict(zip(names, marks)) +``` + +اگر واقعاً به شماره موقعیت یا ایندکس از یک لیست نیاز داریم، یکی از گزینه‌ها استفاده از `()enumerate` است. + +برای درک اینکه `()enumerate` چه کاری انجام می‌دهد، مثال زیر را در نظر بگیرید: + +```{code-cell} python3 +letter_list = ['a', 'b', 'c'] +for index, letter in enumerate(letter_list): + print(f"letter_list[{index}] = '{letter}'") +``` +(list_comprehensions)= +### ‌خلاصه لیست (List Comprehension) + +```{index} single: Python; List comprehension +``` + +همچنین می‌توانیم کد تولید لیست مقادیر تصادفی را با استفاده از چیزی به نام *خلاصه لیست(list comprehension)* به طور قابل توجهی ساده کنیم. + +[خلاصه لیست‌ها](https://en.wikipedia.org/wiki/List_comprehension) یک ابزار زیبای پایتون برای ایجاد لیست هستند. + +مثال زیر را در نظر بگیرید، که خلاصه لیست در سمت راست خط دوم است: + +```{code-cell} python3 +animals = ['dog', 'cat', 'bird'] +plurals = [animal + 's' for animal in animals] +plurals +``` + +این یک مثال دیگر است: + +```{code-cell} python3 +range(8) +``` + +```{code-cell} python3 +doubles = [2 * x for x in range(8)] +doubles +``` + +## عملگرهای مقایسه‌ای و منطقی + +### عملگرهای مقایسه‌ای + +```{index} single: Python; Comparison +``` + +بسیاری از انواع مختلف عبارات وجود دارند که نتیجه‌شان با یکی از مقادیر Boolean (یعنی `True` یا `False`) نشان داده می‌شوند. + +یکی از رایج ترین این عبارت ها، عملگرهای مقایسه‌ای هستند، مانند: + +```{code-cell} python3 +x, y = 1, 2 +x < y +``` + +```{code-cell} python3 +x > y +``` + +یکی از ویژگی‌های خوب پایتون این است که می‌توان نامساوی‌ها را به صورت *پشت سر هم (Chain)* در یک عبارت نوشت. + +```{code-cell} python3 +1 < 2 < 3 +``` + +```{code-cell} python3 +1 <= 2 <= 3 +``` + +همانطور که قبلاً دیدیم، هنگام آزمایش برابری از `==` استفاده می‌کنیم: + +```{code-cell} python3 +x = 1 # Assignment +x == 2 # Comparison +``` + +برای "نابرابر" از `!=` استفاده کنید: + +```{code-cell} python3 +1 != 2 +``` + +توجه کنید که هنگام آزمایش شرایط، می‌توانیم از **هر** عبارت معتبر پایتون استفاده کنیم: + +```{code-cell} python3 +x = 'yes' if 42 else 'no' +x +``` + +```{code-cell} python3 +x = 'yes' if [] else 'no' +x +``` + +اینجا چه اتفاقی می‌افتد؟ + +قانون این است: + +* عباراتی که به صفر، دنباله‌ها یا ظرف های خالی (رشته‌ها، لیست‌ها و غیره) و `None` تشخیص داده می‌شوند، همه معادل `False` هستند. + * به عنوان مثال، `[]` و `()` در یک شرط `if` معادل `False` هستند +* همه مقادیر دیگر معادل `True` هستند. + * به عنوان مثال، `42` در یک شرط `if` معادل `True` است + +### ترکیب عبارات + +```{index} single: Python; Logical Expressions +``` + +می‌توانیم عبارات را با استفاده از `and`، `or` و `not` ترکیب کنیم. + +این‌ها ربط‌دهنده‌های منطقی استاندارد (عطف، فصل و نفی) هستند. + +```{code-cell} python3 +1 < 2 and 'f' in 'foo' +``` + +```{code-cell} python3 +1 < 2 and 'g' in 'foo' +``` + +```{code-cell} python3 +1 < 2 or 'g' in 'foo' +``` + +```{code-cell} python3 +not True +``` + +```{code-cell} python3 +not not True +``` + +به خاطر بسپارید: + +* `P and Q` زمانی `True` است که هر دو `True` باشند، در غیر این صورت `False` +* `P or Q` زمانی `False` است که هر دو `False` باشند، در غیر این صورت `True` + +همچنین می‌توانیم از `()all` و `()any` برای آزمایش دنباله‌ای از عبارات استفاده کنیم: + +```{code-cell} python3 +all([1 <= 2 <= 3, 5 <= 6 <= 7]) +``` +```{code-cell} python3 +all([1 <= 2 <= 3, "a" in "letter"]) +``` +```{code-cell} python3 +any([1 <= 2 <= 3, "a" in "letter"]) +``` + +```{note} +* `()all` زمانی `True` برمی‌گرداند که *همه* مقادیر/عبارات boolean در دنباله `True` باشند +* `()any` زمانی `True` برمی‌گرداند که *هر* مقدار/عبارت boolean در دنباله `True` باشد +``` + +## سبک کدنویسی و مستندسازی + +داشتن یک سبک کدنویسی سازگار و استفاده از مستندسازی می‌تواند کد را خواناتر و نگهداری و توسعه آن را آسان تر کند. + +### راهنمای سبک کدنویسی پایتون: PEP8 + +```{index} single: Python; PEP8 +``` + +می‌توانید با تایپ کردن `import this` در خط فرمان با فلسفه برنامه‌نویسی پایتون آشنا شوید. + +پایتون در کنار موارد دیگر، تأکید زیادی بر یکدست بودن و ثبات در سبک برنامه نویسی دارد. + +همه ما ضرب‌المثل « سازگاری و ذهن‌های کوچک » را شنیده‌ایم. + +در برنامه‌نویسی، همانند ریاضیات، کاملا برعکس است. + +* یک مقاله ریاضی که در آن نمادهای $\cup$ و $\cap$ معکوس شده باشند، خواندن آن بسیار سخت خواهد بود، حتی اگر نویسنده در صفحه اول به شما بگوید. + +در پایتون، سبک استاندارد در [PEP8](https://peps.python.org/pep-0008/) بیان شده است. + +(گاهی اوقات در این درس ها از PEP8 منحرف خواهیم شد تا نمادگذاری ریاضی را بهتر رعایت کنیم) + +(Docstrings)= +### داک‌استرینگ‌ها (docstring) + +```{index} single: Python; Docstrings +``` + +پایتون سیستمی برای اضافه کردن توضیحات به ماژول‌ها، کلاس‌ها، توابع و غیره دارد که *داک‌استرینگ‌(docstring)* نامیده می‌شود. + +نکته خوب درباره داک‌استرینگ‌‌ها این است که در زمان اجرا در دسترس هستند. + +اجرای این کد را امتحان کنید: + +```{code-cell} python3 +def f(x): + """ + This function squares its argument + """ + return x**2 +``` + +پس از اجرای این کد، داک‌استرینگ‌ در دسترس است: + +```{code-cell} ipython +f? +``` + +```{code-block} ipython +:class: no-execute + +Type: function +String Form: +File: /home/john/temp/temp.py +Definition: f(x) +Docstring: This function squares its argument +``` + +```{code-cell} ipython +f?? +``` + +```{code-block} ipython +:class: no-execute + +Type: function +String Form: +File: /home/john/temp/temp.py +Definition: f(x) +Source: +def f(x): + """ + This function squares its argument + """ + return x**2 +``` + +با یک علامت سوال داک‌استرینگ‌ را می‌آوریم، و با دو علامت سوال کد منبع را نیز دریافت می‌کنیم. + +می‌توانید قراردادهای داک‌استرینگ‌ را در [PEP257](https://peps.python.org/pep-0257/) پیدا کنید. + +## تمرین‌ها + +تمرین‌های زیر را حل کنید. + +(برای برخی از تمرینات، تابع داخلی `()sum` کاربردی است). + +```{exercise-start} +:label: pyess_ex1 +``` +قسمت 1: با توجه به دو لیست یا tuple عددی `x_vals` و `y_vals` با طول برابر، ضرب داخلی آن‌ها را با استفاده از `()zip` محاسبه کنید. + +قسمت 2: در یک خط، تعداد اعداد زوج در بازه 0,...,99 را بشمارید. + +قسمت 3: با توجه به `pairs = ((2, 5), (4, 2), (9, 8), (12, 10))`، تعداد جفت‌های `(a, b)` را بشمارید که در آن هم `a` و هم `b` زوج باشند. + +```{hint} +:class: dropdown + +`x % 2` اگر `x` زوج باشد 0 برمی‌گرداند، در غیر این صورت 1. + +``` + +```{exercise-end} +``` + + +```{solution-start} pyess_ex1 +:class: dropdown +``` + +**راه‌حل قسمت 1:** + +یک راه‌حل ممکن میتواند این باشد: + +```{code-cell} python3 +x_vals = [1, 2, 3] +y_vals = [1, 1, 1] +sum([x * y for x, y in zip(x_vals, y_vals)]) +``` + +این روش هم برای این سوال کار می‌کند: + +```{code-cell} python3 +sum(x * y for x, y in zip(x_vals, y_vals)) +``` + +**راه‌حل قسمت 2:** + +یک راه‌حل ممکن میتواند این باشد: + +```{code-cell} python3 +sum([x % 2 == 0 for x in range(100)]) +``` + +این روش هم برای این سوال کار می‌کند: + +```{code-cell} python3 +sum(x % 2 == 0 for x in range(100)) +``` + +برخی از جایگزین‌های کمتر طبیعی که با این حال به نشان دادن انعطاف‌پذیری خلاصه لیست‌ها کمک می‌کنند عبارتند از: + +```{code-cell} python3 +len([x for x in range(100) if x % 2 == 0]) +``` + +و + +```{code-cell} python3 +sum([1 for x in range(100) if x % 2 == 0]) +``` + +**راه‌حل قسمت 3:** + +یک راه حل این است: + +```{code-cell} python3 +pairs = ((2, 5), (4, 2), (9, 8), (12, 10)) +sum([x % 2 == 0 and y % 2 == 0 for x, y in pairs]) +``` + +```{solution-end} +``` + +```{exercise-start} +:label: pyess_ex2 +``` + +چندجمله‌ای زیر را در نظر بگیرید: + +```{math} +:label: polynom0 + +p(x) += a_0 + a_1 x + a_2 x^2 + \cdots a_n x^n += \sum_{i=0}^n a_i x^i +``` + +تابع `p` را بنویسید به طوری که `p(x, coeff)` مقدار را در {eq}`polynom0` با توجه به نقطه `x` و لیست ضرایب `coeff` ($a_1, a_2, \cdots a_n$) محاسبه کند. + +سعی کنید از `()enumerate` در حلقه خود استفاده کنید. + +```{exercise-end} +``` + +```{solution-start} pyess_ex2 +:class: dropdown +``` +یک راه‌حل این است: + +```{code-cell} python3 +def p(x, coeff): + return sum(a * x**i for i, a in enumerate(coeff)) +``` + +```{code-cell} python3 +p(1, (2, 4)) +``` + +```{solution-end} +``` + + +```{exercise-start} +:label: pyess_ex3 +``` + +تابعی بنویسید که یک رشته را به عنوان آرگومان می‌گیرد و تعداد حروف بزرگ در رشته را برمی‌گرداند. + +```{hint} +:class: dropdown + +`'foo'.upper()` مقدار `'FOO'` را برمی‌گرداند. + +``` + +```{exercise-end} +``` + +```{solution-start} pyess_ex3 +:class: dropdown +``` + +یک راه‌حل این است: + +```{code-cell} python3 +def f(string): + count = 0 + for letter in string: + if letter == letter.upper() and letter.isalpha(): + count += 1 + return count + +f('The Rain in Spain') +``` + +یک راه حل جایگزین‌ و پایتونی تر: + +```{code-cell} python3 +def count_uppercase_chars(s): + return sum([c.isupper() for c in s]) + +count_uppercase_chars('The Rain in Spain') +``` + +```{solution-end} +``` + + + +```{exercise} +:label: pyess_ex4 + +تابعی بنویسید که دو دنباله `seq_a` و `seq_b` را به عنوان آرگومان می‌گیرد و `True` را برمی‌گرداند اگر هر عنصر در `seq_a` همچنین عنصری از `seq_b` باشد، در غیر این صورت `False`. + +* منظور از "دنباله" یک لیست، یک tuple یا یک رشته است. +* تمرین را بدون استفاده از [set‌ها](https://docs.python.org/3/tutorial/datastructures.html#sets) و متدهای set انجام دهید. +``` + +```{solution-start} pyess_ex4 +:class: dropdown +``` + +یک راه‌حل این است: + +```{code-cell} python3 +def f(seq_a, seq_b): + for a in seq_a: + if a not in seq_b: + return False + return True + +# == test == # +print(f("ab", "cadb")) +print(f("ab", "cjdb")) +print(f([1, 2], [1, 2, 3])) +print(f([1, 2, 3], [1, 2])) +``` + +یک راه حل جایگزین، پایتونی‌تر با استفاده از `()all`: + +```{code-cell} python3 +def f(seq_a, seq_b): + return all([i in seq_b for i in seq_a]) + +# == test == # +print(f("ab", "cadb")) +print(f("ab", "cjdb")) +print(f([1, 2], [1, 2, 3])) +print(f([1, 2, 3], [1, 2])) +``` + +البته، اگر از نوع داده `sets` استفاده کنیم، راه‌حل ساده‌تر است: + +```{code-cell} python3 +def f(seq_a, seq_b): + return set(seq_a).issubset(set(seq_b)) +``` + +```{solution-end} +``` + + +```{exercise} +:label: pyess_ex5 + +وقتی کتابخانه‌های عددی را پوشش دهیم، خواهیم دید که آن‌ها شامل جایگزین‌های زیادی برای درونیابی و تقریب تابع هستند. + +با این حال، بیایید روتین تقریب تابع خودمان را به عنوان یک تمرین بنویسیم. + +به طور خاص، بدون استفاده از هیچ import، تابعی `linapprox` بنویسید که به عنوان آرگومان‌ها می‌گیرد + +* یک تابع `f` که بازه‌ای $[a, b]$ را به $\mathbb R$ نگاشت می‌کند. +* دو اسکالر `a` و `b` که حدود این بازه را مشخص می‌کنند. +* یک عدد صحیح `n` که تعداد نقاط شبکه را تعیین می‌کند. +* یک عدد `x` که `a <= x <= b` را برآورده می‌کند. + +و [درونیابی خطی تکه‌ای](https://en.wikipedia.org/wiki/Linear_interpolation) `f` را در `x`، بر اساس `n` نقطه شبکه با فاصله یکسان `a = point[0] < point[1] < ... < point[n-1] = b` برمی‌گرداند. + +برای وضوح تلاش کنید، نه کارایی. +``` + +```{solution-start} pyess_ex5 +:class: dropdown +``` +یک راه‌حل این است: + +```{code-cell} python3 +def linapprox(f, a, b, n, x): + """ + Evaluates the piecewise linear interpolant of f at x on the interval + [a, b], with n evenly spaced grid points. + + Parameters + ========== + f : function + The function to approximate + + x, a, b : scalars (floats or integers) + Evaluation point and endpoints, with a <= x <= b + + n : integer + Number of grid points + + Returns + ======= + A float. The interpolant evaluated at x + + """ + length_of_interval = b - a + num_subintervals = n - 1 + step = length_of_interval / num_subintervals + + # === find first grid point larger than x === # + point = a + while point <= x: + point += step + + # === x must lie between the gridpoints (point - step) and point === # + u, v = point - step, point + + return f(u) + (x - u) * (f(v) - f(u)) / (v - u) +``` + +```{solution-end} +``` + + +```{exercise-start} +:label: pyess_ex6 +``` + +با استفاده از سینتکس خلاصه لیست، می‌توانیم حلقه در کد زیر را ساده کنیم. + +```{code-cell} python3 +import numpy as np + +n = 100 +ϵ_values = [] +for i in range(n): + e = np.random.randn() + ϵ_values.append(e) +``` + +```{exercise-end} +``` + +```{solution-start} pyess_ex6 +:class: dropdown +``` + +یک راه‌حل این است: + +```{code-cell} python3 +n = 100 +ϵ_values = [np.random.randn() for i in range(n)] +``` + +```{solution-end} +``` \ No newline at end of file