From 4d8e193d654c5ad90df0e65eca276b3434230c18 Mon Sep 17 00:00:00 2001 From: Mohamad Reza Date: Tue, 11 Mar 2025 00:41:07 +0330 Subject: [PATCH] update farsi translation - section 1 --- translations/fa-farsi/section1-temp.md | 298 +++++++++++++++++++++++++ 1 file changed, 298 insertions(+) diff --git a/translations/fa-farsi/section1-temp.md b/translations/fa-farsi/section1-temp.md index eca01fa..9d70bb4 100644 --- a/translations/fa-farsi/section1-temp.md +++ b/translations/fa-farsi/section1-temp.md @@ -490,3 +490,301 @@ True همونطور که مشاهده می‌کنید، ترتیب حذف شدن شیءها باعث تفاوت میشه. --- + + +### ▶ بی‌نظمی در خود نظم * + +```py +from collections import OrderedDict + +dictionary = dict() +dictionary[1] = 'a'; dictionary[2] = 'b'; + +ordered_dict = OrderedDict() +ordered_dict[1] = 'a'; ordered_dict[2] = 'b'; + +another_ordered_dict = OrderedDict() +another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a'; + +class DictWithHash(dict): + """ + یک dict که تابع جادویی __hash__ هم توش پیاده شده. + """ + __hash__ = lambda self: 0 + +class OrderedDictWithHash(OrderedDict): + """ + یک OrderedDict که تابع جادویی __hash__ هم توش پیاده شده. + """ + __hash__ = lambda self: 0 +``` + +**Output** +```py +>>> dictionary == ordered_dict # اگر مقدار اولی با دومی برابره +True +>>> dictionary == another_ordered_dict # و مقدار اولی با سومی برابره +True +>>> ordered_dict == another_ordered_dict # پس چرا مقدار دومی با سومی برابر نیست؟ +False + +# ما همه‌مون میدونیم که یک مجموعه فقط شامل عناصر منحصربه‌فرد و غیرتکراریه. +# بیاید یک مجموعه از این دیکشنری‌ها بسازیم ببینیم چه اتفاقی میافته... + +>>> len({dictionary, ordered_dict, another_ordered_dict}) +Traceback (most recent call last): + File "", line 1, in +TypeError: unhashable type: 'dict' + +# منطقیه چون dict ها __hash__ توشون پیاده‌سازی نشده. پس بیاید از +# کلاس‌هایی که خودمون درست کردیم استفاده کنیم. +>>> dictionary = DictWithHash() +>>> dictionary[1] = 'a'; dictionary[2] = 'b'; +>>> ordered_dict = OrderedDictWithHash() +>>> ordered_dict[1] = 'a'; ordered_dict[2] = 'b'; +>>> another_ordered_dict = OrderedDictWithHash() +>>> another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a'; +>>> len({dictionary, ordered_dict, another_ordered_dict}) +1 +>>> len({ordered_dict, another_ordered_dict, dictionary}) # ترتیب رو عوض می‌کنیم +2 +``` + +چی شد؟ + +#### 💡 توضیحات: + +- دلیل اینکه این مقایسه بین متغیرهای `dictionary`، `ordered_dict` و `another_ordered_dict` به درستی اجرا نمیشه به خاطر نحوه پیاده‌سازی تابع `__eq__` در کلاس `OrderedDict` هست. طبق [مستندات](https://docs.python.org/3/library/collections.html#ordereddict-objects) + > مقایسه برابری بین شیءهایی از نوع OrderedDict به ترتیب اعضای آن‌ها هم بستگی دارد و به صورت `list(od1.items())==list(od2.items())` پیاده سازی شده است. مقایسه برابری بین شیءهای `OrderedDict` و شیءهای قابل نگاشت دیگر به ترتیب اعضای آن‌ها بستگی ندارد و مقایسه همانند دیکشنری‌های عادی انجام می‌شود. +- این رفتار باعث میشه که بتونیم `OrderedDict` ها رو هرجایی که یک دیکشنری عادی کاربرد داره، جایگزین کنیم و استفاده کنیم. +- خب، حالا چرا تغییر ترتیب روی طول مجموعه‌ای که از دیکشنری‌ها ساختیم، تاثیر گذاشت؟ جوابش همین رفتار مقایسه‌ای غیرانتقالی بین این شیءهاست. از اونجایی که `set` ها مجموعه‌ای از عناصر غیرتکراری و بدون نظم هستند، ترتیبی که عناصر تو این مجموعه‌ها درج میشن نباید مهم باشه. ولی در این مورد، مهم هست. بیاید کمی تجزیه و تحلیلش کنیم. + ```py + >>> some_set = set() + >>> some_set.add(dictionary) # این شیء‌ها از قطعه‌کدهای بالا هستند. + >>> ordered_dict in some_set + True + >>> some_set.add(ordered_dict) + >>> len(some_set) + 1 + >>> another_ordered_dict in some_set + True + >>> some_set.add(another_ordered_dict) + >>> len(some_set) + 1 + + >>> another_set = set() + >>> another_set.add(ordered_dict) + >>> another_ordered_dict in another_set + False + >>> another_set.add(another_ordered_dict) + >>> len(another_set) + 2 + >>> dictionary in another_set + True + >>> another_set.add(another_ordered_dict) + >>> len(another_set) + 2 + ``` + پس بی‌ثباتی تو این رفتار به خاطر اینه که مقدار `another_ordered_dict in another_set` برابر با `False` هست چون `ordered_dict` از قبل داخل `another_set` هست و همونطور که قبلا مشاهده کردید، مقدار `ordered_dict == another_ordered_dict` برابر با `False` هست. + +--- + + +### ▶ تلاش کن... * + +```py +def some_func(): + try: + return 'from_try' + finally: + return 'from_finally' + +def another_func(): + for _ in range(3): + try: + continue + finally: + print("Finally!") + +def one_more_func(): + try: + for i in range(3): + try: + 1 / i + except ZeroDivisionError: + # بذارید اینجا ارور بدیم و بیرون حلقه بهش + # رسیدگی کنیم + raise ZeroDivisionError("A trivial divide by zero error") + finally: + print("Iteration", i) + break + except ZeroDivisionError as e: + print("Zero division error occurred", e) +``` + +**خروجی:** + +```py +>>> some_func() +'from_finally' + +>>> another_func() +Finally! +Finally! +Finally! + +>>> 1 / 0 +Traceback (most recent call last): + File "", line 1, in +ZeroDivisionError: division by zero + +>>> one_more_func() +Iteration 0 + +``` + +#### 💡 Explanation: + +- وقتی یک عبارت `return`، `break` یا `continue` داخل بخش `try` از یک عبارت "try...finally" اجرا میشه، بخش `fianlly` هم هنگام خارج شدن اجرا میشه. +- مقدار بازگشتی یک تابع از طریق آخرین عبارت `return` که داخل تابع اجرا میشه، مشخص میشه. از اونجایی که بخش `finally` همیشه اجرا میشه، عبارت `return` که داخل بخش `finally` هست آخرین عبارتیه که اجرا میشه. +- نکته اینجاست که اگه بخش داخل بخش `finally` یک عبارت `return` یا `break` اجرا بشه، `exception` موقتی که ذخیره شده، رها میشه. + +--- + + +### ▶ برای چی? + +```py +some_string = "wtf" +some_dict = {} +for i, some_dict[i] in enumerate(some_string): + i = 10 +``` + +**Output:** +```py +>>> some_dict # یک دیکشنری مرتب‌شده نمایان میشه. +{0: 'w', 1: 't', 2: 'f'} +``` + +#### 💡 توضیحات: +* یک حلقه `for` در [گرامر پایتون](https://docs.python.org/3/reference/grammar.html) این طور تعریف میشه: + ``` + for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] + ``` + به طوری که `exprlist` یک هدف برای مقداردهیه. این یعنی، معادل عبارت `{exprlist} = {next_value}` **برای هر شیء داخل `testlist` اجرا می‌شود**. + یک مثال جالب برای نشون دادن این تعریف: + ```py + for i in range(4): + print(i) + i = 10 + ``` + + **خروجی:** + ``` + 0 + 1 + 2 + 3 + ``` + + آیا انتظار داشتید که حلقه فقط یک بار اجرا بشه؟ + + **💡 توضیحات:** + + - عبارت مقداردهی `i = 10` به خاطر نحوه کار کردن حلقه‌ها، هیچوقت باعث تغییر در تکرار حلقه نمیشه. قبل از شروع هر تکرار، مقدار بعدی که توسط شیء قابل تکرار (که در اینجا `range(4)` است) ارائه میشه، از بسته خارج میشه و به متغیرهای لیست هدف (که در اینجا `i` است) مقداردهی میشه. + +* تابع `enumerate(some_string)`، یک متغیر `i` (که یک شمارنده اقزایشی است) و یک حرف از حروف رشته `some_string` رو در هر تکرار برمیگردونه. و بعدش برای کلید `i` (تازه مقداردهی‌شده) در دیکشنری `some_dict`، مقدار اون حرف رو تنظیم می‌کنه. بازشده این حلقه می‌تونه مانند مثال زیر ساده بشه: + ```py + >>> i, some_dict[i] = (0, 'w') + >>> i, some_dict[i] = (1, 't') + >>> i, some_dict[i] = (2, 'f') + >>> some_dict + ``` + +--- + +### ▶ اختلاف زمانی در محاسبه + +1\. +```py +array = [1, 8, 15] +# یک عبارت تولیدکننده عادی +gen = (x for x in array if array.count(x) > 0) +array = [2, 8, 22] +``` + +**خروجی:** + +```py +>>> print(list(gen)) # پس بقیه مقدارها کجا رفتن؟ +[8] +``` + +2\. + +```py +array_1 = [1,2,3,4] +gen_1 = (x for x in array_1) +array_1 = [1,2,3,4,5] + +array_2 = [1,2,3,4] +gen_2 = (x for x in array_2) +array_2[:] = [1,2,3,4,5] +``` + +**خروجی:** +```py +>>> print(list(gen_1)) +[1, 2, 3, 4] + +>>> print(list(gen_2)) +[1, 2, 3, 4, 5] +``` + +3\. + +```py +array_3 = [1, 2, 3] +array_4 = [10, 20, 30] +gen = (i + j for i in array_3 for j in array_4) + +array_3 = [4, 5, 6] +array_4 = [400, 500, 600] +``` + +**خروجی:** +```py +>>> print(list(gen)) +[401, 501, 601, 402, 502, 602, 403, 503, 603] +``` + +#### 💡 توضیحات + +- در یک عبارت [تولیدکننده](https://wiki.python.org/moin/Generators)، عبارت بند `in` در هنگام تعریف محاسبه میشه ولی عبارت شرطی در زمان اجرا محاسبه میشه. +- پس قبل از زمان اجرا، `array` دوباره با لیست `[2, 8, 22]` مقداردهی میشه و از آن‌جایی که در مقدار جدید `array`، بین `1`، `8` و `15`، فقط تعداد `8` بزرگتر از `0` است، تولیدکننده فقط مقدار `8` رو برمیگردونه +- تفاوت در مقدار `gen_1` و `gen_2` در بخش دوم به خاطر نحوه مقداردهی دوباره `array_1` و `array_2` است. +- در مورد اول، متغیر `array_1` به شیء جدید `[1,2,3,4,5]` وصله و از اون جایی که عبارت بند `in` در هنگام تعریف محاسبه میشه، `array_1` داخل تولیدکننده هنوز به شیء قدیمی `[1,2,3,4]` (که هنوز حذف نشده) +- در مورد دوم، مقداردهی برشی به `array_2` باعث به‌روز شدن شیء قدیمی این متغیر از `[1,2,3,4]` به `[1,2,3,4,5]` میشه و هر دو متغیر `gen_2` و `array_2` به یک شیء اشاره میکنند که حالا به‌روز شده. +- خیلی‌خب، حالا طبق منطقی که تا الان گفتیم، نباید مقدار `list(gen)` در قطعه‌کد سوم، `[11, 21, 31, 12, 22, 32, 13, 23, 33]` باشه؟ (چون `array_3` و `array_4` قراره درست مثل `array_1` رفتار کنن). دلیل این که چرا (فقط) مقادیر `array_4` به‌روز شدن، توی [PEP-289](https://www.python.org/dev/peps/pep-0289/#the-details) توضیح داده شده. + + > فقط بیرونی‌ترین عبارت حلقه `for` بلافاصله محاسبه میشه و باقی عبارت‌ها به تعویق انداخته میشن تا زمانی که تولیدکننده اجرا بشه. + +--- + + +### ▶ هر گردی، گردو نیست + +```py +>>> 'something' is not None +True +>>> 'something' is (not None) +False +``` + +#### 💡 توضیحات +- عملگر `is not` یک عملگر باینری واحده و رفتارش متفاوت تر از استفاده `is` و `not` به صورت جداگانه‌ست. +- عملگر `is not` مقدار `False` رو برمیگردونه اگر متغیرها در هردو سمت این عملگر به شیء یکسانی اشاره کنند و درغیر این صورت، مقدار `True` برمیگردونه +- در مثال بالا، عبارت `(not None)` برابره با مقدار `True` از اونجایی که مقدار `None` در زمینه boolean به `False` تبدیل میشه. پس کل عبارت معادل عبارت `'something' is True` میشه. + +---