[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"post-data-tamrin-python-pishrafte":3,"comments-41":57},{"post":4,"course":40,"related":41},{"id":5,"slug":6,"title":7,"content":8,"excerpt":9,"category":10,"date":11,"readTime":12,"wordCount":13,"image":14,"author":15,"authorRole":16,"authorImg":17,"reviewedBy":18,"tags":19,"relatedCourseSlug":21,"categoryKey":22,"summaryPoints":23,"faq":27,"seoTitle":37,"seoDesc":38,"createdAtIso":39,"updatedAtIso":39,"updatedDate":40},41,"tamrin-python-pishrafte","تمرین پایتون پیشرفته: ۱۵ تمرین با جواب، از شی‌گرایی تا دکوراتور و ژنراتور","\n\u003Cp>بعد از پایتون مقدماتی، بیشتر آدم‌ها جایی گیر می‌کنند که مفهوم‌ها را خوانده‌اند ولی وقتی باید خودشان کلاس، دکوراتور یا ژنراتور بنویسند، نمی‌دانند از کجا شروع کنند. راه عبور از این مرحله فقط تمرین است. این صفحه ۱۵ تمرین پایتون پیشرفته دارد که به ترتیب سختی چیده شده‌اند و هر کدام جواب کامل، خروجی واقعی و توضیح دارد.\u003C\u002Fp>\n\n\u003Cp>این تمرین‌ها برای کسی است که پایتون مقدماتی (متغیر، شرط، حلقه، تابع، لیست و دیکشنری) را بلد است و می‌خواهد سطحش را بالا ببرد؛ برای آمادگی امتحان درس پایتون پیشرفته، مصاحبه‌ی شغلی یا قبل از شروع یک دوره‌ی پیشرفته. اگر هنوز نمی‌دانید پایتون پیشرفته دقیقاً شامل چه مباحثی است، اول \u003Ca href=\"\u002Flearning\u002Fpython-pishrafte\">آموزش پایتون پیشرفته: بعد از مقدماتی چه یاد بگیرم؟\u003C\u002Fa> را بخوانید.\u003C\u002Fp>\n\n\u003Ch2>چطور از این تمرین‌ها استفاده کنید؟\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>اول خودتان بنویسید.\u003C\u002Fstrong> صورت مسئله را بخوانید و حداقل ۱۵ دقیقه روی آن کار کنید، بعد جواب را ببینید. خواندن جواب بدون تلاش، تقریباً هیچ چیز یاد نمی‌دهد.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>جواب را اجرا کنید و تغییر دهید.\u003C\u002Fstrong> همه‌ی کدها با پایتون ۳ اجرا و خروجی‌شان همین‌جا آمده است. یک خط را عوض کنید و پیش‌بینی کنید چه می‌شود.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>جواب شما لازم نیست شبیه جواب ما باشد.\u003C\u002Fstrong> در پایتون معمولاً چند راه درست هست؛ مهم این است که کد شما خوانا باشد و درست کار کند.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>شی‌گرایی\u003C\u002Fh2>\n\n\u003Ch3>تمرین ۱: حساب بانکی\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>BankAccount\u003C\u002Fcode> بسازید که موجودی را نگه دارد و متدهای \u003Ccode>deposit\u003C\u002Fcode> و \u003Ccode>withdraw\u003C\u002Fcode> داشته باشد. واریز مبلغ منفی و برداشت بیشتر از موجودی باید با \u003Ccode>ValueError\u003C\u002Fcode> رد شود.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>class BankAccount:\n    def __init__(self, owner, balance=0):\n        self.owner = owner\n        self.balance = balance\n\n    def deposit(self, amount):\n        if amount &lt;= 0:\n            raise ValueError(\"amount must be positive\")\n        self.balance += amount\n\n    def withdraw(self, amount):\n        if amount &gt; self.balance:\n            raise ValueError(\"insufficient funds\")\n        self.balance -= amount\n\n\nacc = BankAccount(\"Sara\", 100)\nacc.deposit(50)\nacc.withdraw(30)\nprint(acc.balance)\n\ntry:\n    acc.withdraw(500)\nexcept ValueError as err:\n    print(\"Error:\", err)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">120\nError: insufficient funds\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>نکته‌ی اصلی این تمرین این است که کلاس از داده‌ی خودش محافظت می‌کند: به‌جای اینکه هر جای برنامه موجودی را مستقیم کم کند، همه‌ی تغییرها از متدها می‌گذرد و قانون‌ها یک جا نوشته می‌شوند.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۲: شکل‌ها با ارث‌بری و چندریختی\u003C\u002Fh3>\n\u003Cp>کلاس پایه‌ی \u003Ccode>Shape\u003C\u002Fcode> با متد \u003Ccode>area\u003C\u002Fcode> بسازید و دو کلاس \u003Ccode>Rectangle\u003C\u002Fcode> و \u003Ccode>Circle\u003C\u002Fcode> از آن ارث ببرند. بعد فهرستی از شکل‌های مختلف بسازید و مساحت همه را با یک حلقه چاپ کنید.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import math\n\n\nclass Shape:\n    def area(self):\n        raise NotImplementedError\n\n\nclass Rectangle(Shape):\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    def area(self):\n        return self.width * self.height\n\n\nclass Circle(Shape):\n    def __init__(self, radius):\n        self.radius = radius\n\n    def area(self):\n        return math.pi * self.radius ** 2\n\n\nshapes = [Rectangle(3, 4), Circle(1), Rectangle(2, 5)]\nfor shape in shapes:\n    print(type(shape).__name__, round(shape.area(), 2))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">Rectangle 12\nCircle 3.14\nRectangle 10\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>حلقه نمی‌داند هر شکل چیست و لازم هم نیست بداند؛ فقط \u003Ccode>area\u003C\u002Fcode> را صدا می‌زند و هر کلاس جواب خودش را می‌دهد. به این چندریختی (Polymorphism) می‌گویند. اگر فردا کلاس \u003Ccode>Triangle\u003C\u002Fcode> اضافه شود، حلقه هیچ تغییری لازم ندارد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۳: بردار با متدهای ویژه\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>Vector\u003C\u002Fcode> دوبعدی بسازید که با عملگر \u003Ccode>+\u003C\u002Fcode> جمع شود، با \u003Ccode>==\u003C\u002Fcode> مقایسه شود، با \u003Ccode>abs\u003C\u002Fcode> طولش محاسبه شود و هنگام چاپ به شکل خوانا نمایش داده شود.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import math\n\n\nclass Vector:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    def __add__(self, other):\n        return Vector(self.x + other.x, self.y + other.y)\n\n    def __eq__(self, other):\n        return self.x == other.x and self.y == other.y\n\n    def __abs__(self):\n        return math.hypot(self.x, self.y)\n\n    def __repr__(self):\n        return f\"Vector({self.x}, {self.y})\"\n\n\nv = Vector(3, 4) + Vector(1, 2)\nprint(v)\nprint(v == Vector(4, 6))\nprint(abs(Vector(3, 4)))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">Vector(4, 6)\nTrue\n5.0\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>متدهایی که با دو زیرخط شروع و تمام می‌شوند (Dunder Methods) به کلاس اجازه می‌دهند مثل نوع‌های داخلی پایتون رفتار کند. \u003Ccode>__repr__\u003C\u002Fcode> را همیشه بنویسید؛ عیب‌یابی را خیلی راحت‌تر می‌کند.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۴: دما با property\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>Temperature\u003C\u002Fcode> بسازید که دما را به سلسیوس نگه دارد. دمای کمتر از صفر مطلق (۲۷۳٫۱۵- درجه) نباید پذیرفته شود و فارنهایت باید بدون ذخیره‌ی جداگانه محاسبه شود.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>class Temperature:\n    def __init__(self, celsius):\n        self.celsius = celsius\n\n    @property\n    def celsius(self):\n        return self._celsius\n\n    @celsius.setter\n    def celsius(self, value):\n        if value &lt; -273.15:\n            raise ValueError(\"below absolute zero\")\n        self._celsius = value\n\n    @property\n    def fahrenheit(self):\n        return self._celsius * 9 \u002F 5 + 32\n\n\nt = Temperature(25)\nprint(t.fahrenheit)\nt.celsius = 100\nprint(t.fahrenheit)\ntry:\n    t.celsius = -300\nexcept ValueError as err:\n    print(\"Error:\", err)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">77.0\n212.0\nError: below absolute zero\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>با \u003Ccode>property\u003C\u002Fcode> بیرون کلاس هنوز می‌نویسیم \u003Ccode>t.celsius = 100\u003C\u002Fcode>، ولی پشت صحنه اعتبارسنجی اجرا می‌شود. حتی مقداری که در \u003Ccode>__init__\u003C\u002Fcode> داده می‌شود هم از همان setter می‌گذرد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۵: سازنده‌ی جایگزین با classmethod\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>Date\u003C\u002Fcode> سه مقدار سال، ماه و روز دارد. یک \u003Ccode>classmethod\u003C\u002Fcode> بنویسید که تاریخ را از رشته‌ای مثل \u003Ccode>1405-07-10\u003C\u002Fcode> بسازد، و یک \u003Ccode>staticmethod\u003C\u002Fcode> که بگوید ماه معتبر است یا نه.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>class Date:\n    def __init__(self, year, month, day):\n        self.year = year\n        self.month = month\n        self.day = day\n\n    @classmethod\n    def from_string(cls, text):\n        year, month, day = (int(part) for part in text.split(\"-\"))\n        return cls(year, month, day)\n\n    @staticmethod\n    def is_valid_month(month):\n        return 1 &lt;= month &lt;= 12\n\n    def __repr__(self):\n        return f\"Date({self.year}, {self.month}, {self.day})\"\n\n\nd = Date.from_string(\"1405-07-10\")\nprint(d)\nprint(Date.is_valid_month(13))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">Date(1405, 7, 10)\nFalse\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>classmethod\u003C\u002Fcode> کلاس را به‌عنوان اولین ورودی می‌گیرد و برای «راه دوم ساختن شیء» استفاده می‌شود. \u003Ccode>staticmethod\u003C\u002Fcode> هیچ‌کدام را نمی‌گیرد و فقط تابعی است که از نظر معنی به کلاس تعلق دارد.\u003C\u002Fp>\n\n\u003Ch2>توابع پیشرفته\u003C\u002Fh2>\n\n\u003Ch3>تمرین ۶: ورودی‌های متغیر با args و kwargs\u003C\u002Fh3>\n\u003Cp>تابع \u003Ccode>report\u003C\u002Fcode> بنویسید که هر تعداد نمره را بگیرد و میانگین را چاپ کند، و هر تعداد اطلاعات اضافه‌ی نام‌دار (مثل نام و کلاس) را هم کنار آن نشان دهد.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>def report(*scores, **info):\n    average = sum(scores) \u002F len(scores)\n    details = \", \".join(f\"{key}={value}\" for key, value in info.items())\n    print(f\"average={average:.2f} ({details})\")\n\n\nreport(18, 17, 19.5, name=\"Ali\", grade=11)\nreport(15, 20, name=\"Mina\")\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">average=18.17 (name=Ali, grade=11)\naverage=17.50 (name=Mina)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>*scores\u003C\u002Fcode> همه‌ی ورودی‌های بی‌نام را در یک تاپل جمع می‌کند و \u003Ccode>**info\u003C\u002Fcode> ورودی‌های نام‌دار را در یک دیکشنری. اسم‌ها قراردادی‌اند؛ چیزی که اهمیت دارد ستاره‌هاست.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۷: شمارنده با closure\u003C\u002Fh3>\n\u003Cp>تابع \u003Ccode>make_counter\u003C\u002Fcode> بنویسید که یک تابع برگرداند؛ هر بار که تابع برگشتی صدا زده شود، عدد بعدی را بدهد. دو شمارنده‌ی مستقل بسازید.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>def make_counter():\n    count = 0\n\n    def counter():\n        nonlocal count\n        count += 1\n        return count\n\n    return counter\n\n\nfirst = make_counter()\nsecond = make_counter()\nprint(first(), first(), first())\nprint(second())\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">1 2 3\n1\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>تابع داخلی متغیر \u003Ccode>count\u003C\u002Fcode> را حتی بعد از تمام شدن \u003Ccode>make_counter\u003C\u002Fcode> به خاطر دارد؛ به این closure می‌گویند. بدون \u003Ccode>nonlocal\u003C\u002Fcode> پایتون فکر می‌کند \u003Ccode>count\u003C\u002Fcode> یک متغیر محلی تازه است و خطا می‌دهد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۸: دکوراتور شمارش فراخوانی\u003C\u002Fh3>\n\u003Cp>دکوراتور \u003Ccode>count_calls\u003C\u002Fcode> بنویسید که بشمارد هر تابع چند بار صدا زده شده، بدون اینکه نام و توضیح تابع اصلی از بین برود.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import functools\n\n\ndef count_calls(func):\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        wrapper.calls += 1\n        return func(*args, **kwargs)\n\n    wrapper.calls = 0\n    return wrapper\n\n\n@count_calls\ndef greet(name):\n    \"\"\"Say hello.\"\"\"\n    return f\"Hello {name}\"\n\n\ngreet(\"Reza\")\ngreet(\"Neda\")\nprint(greet.calls)\nprint(greet.__name__, \"-\", greet.__doc__)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">2\ngreet - Say hello.\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>دکوراتور تابعی است که تابع می‌گیرد و تابع تازه‌ای برمی‌گرداند. \u003Ccode>functools.wraps\u003C\u002Fcode> نام و توضیح تابع اصلی را روی تابع تازه کپی می‌کند؛ بدون آن، \u003Ccode>greet.__name__\u003C\u002Fcode> می‌شد \u003Ccode>wrapper\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۹: دکوراتور با ورودی\u003C\u002Fh3>\n\u003Cp>دکوراتور \u003Ccode>repeat(n)\u003C\u002Fcode> بنویسید که تابع را n بار اجرا کند.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import functools\n\n\ndef repeat(times):\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            for _ in range(times):\n                func(*args, **kwargs)\n        return wrapper\n    return decorator\n\n\n@repeat(3)\ndef beep():\n    print(\"beep\")\n\n\nbeep()\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">beep\nbeep\nbeep\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>دکوراتوری که ورودی می‌گیرد سه لایه دارد: لایه‌ی بیرونی ورودی را می‌گیرد، لایه‌ی میانی خود تابع را و لایه‌ی داخلی جای تابع اجرا می‌شود. \u003Ccode>@repeat(3)\u003C\u002Fcode> اول \u003Ccode>repeat(3)\u003C\u002Fcode> را اجرا می‌کند و نتیجه‌اش را روی تابع می‌گذارد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۱۰: مرتب‌سازی با چند معیار\u003C\u002Fh3>\n\u003Cp>فهرستی از دانش‌آموزان (نام و نمره) را اول بر اساس نمره از زیاد به کم، و در نمره‌های برابر بر اساس نام به ترتیب الفبا مرتب کنید.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>students = [\n    {\"name\": \"Sara\", \"score\": 18},\n    {\"name\": \"Ali\", \"score\": 19},\n    {\"name\": \"Amir\", \"score\": 18},\n    {\"name\": \"Neda\", \"score\": 20},\n]\n\nranked = sorted(students, key=lambda s: (-s[\"score\"], s[\"name\"]))\nfor position, s in enumerate(ranked, start=1):\n    print(position, s[\"name\"], s[\"score\"])\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">1 Neda 20\n2 Ali 19\n3 Amir 18\n4 Sara 18\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>کلید مرتب‌سازی می‌تواند یک تاپل باشد؛ پایتون اول عضو اول را مقایسه می‌کند و در تساوی سراغ عضو دوم می‌رود. منفی کردن نمره، ترتیب نزولی را بدون \u003Ccode>reverse=True\u003C\u002Fcode> می‌سازد، که اگر به کار می‌رفت ترتیب نام‌ها را هم برعکس می‌کرد.\u003C\u002Fp>\n\n\u003Ch2>ژنراتور و پیمایشگر\u003C\u002Fh2>\n\n\u003Ch3>تمرین ۱۱: دنباله‌ی فیبوناچی با ژنراتور\u003C\u002Fh3>\n\u003Cp>ژنراتوری بنویسید که اعداد فیبوناچی را بی‌پایان تولید کند. بعد ده عدد اول و اولین عدد بزرگ‌تر از هزار را چاپ کنید.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import itertools\n\n\ndef fibonacci():\n    a, b = 0, 1\n    while True:\n        yield a\n        a, b = b, a + b\n\n\nprint(list(itertools.islice(fibonacci(), 10)))\nprint(next(n for n in fibonacci() if n &gt; 1000))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]\n1597\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ژنراتور با \u003Ccode>yield\u003C\u002Fcode> هر بار فقط یک عدد می‌سازد و حافظه‌ای برای کل دنباله اشغال نمی‌کند؛ برای همین می‌تواند بی‌پایان باشد. \u003Ccode>islice\u003C\u002Fcode> فقط به تعداد لازم از آن برمی‌دارد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۱۲: پیمایشگر شمارش معکوس\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>Countdown\u003C\u002Fcode> بسازید که بشود با \u003Ccode>for\u003C\u002Fcode> روی آن حلقه زد و از عدد داده‌شده تا یک شمارش معکوس کند.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>class Countdown:\n    def __init__(self, start):\n        self.current = start\n\n    def __iter__(self):\n        return self\n\n    def __next__(self):\n        if self.current &lt; 1:\n            raise StopIteration\n        value = self.current\n        self.current -= 1\n        return value\n\n\nfor n in Countdown(5):\n    print(n, end=\" \")\nprint()\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">5 4 3 2 1\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>هر چیزی که \u003Ccode>__iter__\u003C\u002Fcode> و \u003Ccode>__next__\u003C\u002Fcode> داشته باشد در حلقه‌ی \u003Ccode>for\u003C\u002Fcode> قابل استفاده است. پایان پیمایش با \u003Ccode>StopIteration\u003C\u002Fcode> اعلام می‌شود. همین کار را با یک ژنراتور کوتاه‌تر هم می‌شود نوشت؛ نسخه‌ی کلاسی برای فهمیدن سازوکار حلقه‌ی for است.\u003C\u002Fp>\n\n\u003Ch2>خطا، context manager و داده\u003C\u002Fh2>\n\n\u003Ch3>تمرین ۱۳: زمان‌سنج با context manager\u003C\u002Fh3>\n\u003Cp>کلاس \u003Ccode>Timer\u003C\u002Fcode> بسازید که با \u003Ccode>with\u003C\u002Fcode> استفاده شود و زمان اجرای کد داخل بلوک را اندازه بگیرد.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import time\n\n\nclass Timer:\n    def __enter__(self):\n        self.start = time.perf_counter()\n        return self\n\n    def __exit__(self, exc_type, exc, tb):\n        self.elapsed = time.perf_counter() - self.start\n        return False\n\n\nwith Timer() as timer:\n    total = sum(range(1_000_000))\n\nprint(total)\nprint(timer.elapsed &lt; 5)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">499999500000\nTrue\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>__enter__\u003C\u002Fcode> هنگام ورود به \u003Ccode>with\u003C\u002Fcode> و \u003Ccode>__exit__\u003C\u002Fcode> هنگام خروج اجرا می‌شود، حتی اگر داخل بلوک خطا رخ دهد. برگرداندن \u003Ccode>False\u003C\u002Fcode> یعنی خطا (اگر بود) پنهان نشود. باز کردن فایل با \u003Ccode>with open(...)\u003C\u002Fcode> هم دقیقاً همین سازوکار را دارد.\u003C\u002Fp>\n\n\u003Ch3>تمرین ۱۴: آمار کلمه‌ها با Counter و defaultdict\u003C\u002Fh3>\n\u003Cp>در یک متن، سه کلمه‌ی پرتکرار را پیدا کنید و کلمه‌ها را بر اساس حرف اولشان گروه‌بندی کنید.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>from collections import Counter, defaultdict\n\ntext = \"python is fun and python is powerful and fast\"\nwords = text.split()\n\nfor word, count in Counter(words).most_common(3):\n    print(word, count)\n\ngroups = defaultdict(list)\nfor word in sorted(set(words)):\n    groups[word[0]].append(word)\nfor letter, items in groups.items():\n    print(letter, \"-&gt;\", \", \".join(items))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">python 2\nis 2\nand 2\na -&gt; and\nf -&gt; fast, fun\ni -&gt; is\np -&gt; powerful, python\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>Counter\u003C\u002Fcode> شمارش را در یک خط انجام می‌دهد و \u003Ccode>defaultdict(list)\u003C\u002Fcode> لازم نمی‌داند قبل از افزودن، وجود کلید را بررسی کنید. هر دو در ماژول استاندارد \u003Ccode>collections\u003C\u002Fcode> هستند و در مصاحبه‌ها زیاد پرسیده می‌شوند.\u003C\u002Fp>\n\n\u003Ch2>پروژه‌ی پایانی\u003C\u002Fh2>\n\n\u003Ch3>تمرین ۱۵: کتابخانه‌ی کوچک با dataclass و JSON\u003C\u002Fh3>\n\u003Cp>سیستم کوچک کتابخانه بسازید: کتاب‌ها عنوان، نویسنده و وضعیت امانت دارند. امکان افزودن، امانت دادن و برگرداندن کتاب باشد و همه‌ی کتاب‌ها در فایل JSON ذخیره و دوباره خوانده شوند. امانت دادن کتابی که قبلاً امانت رفته باید خطا بدهد.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>جواب:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import json\nimport os\nimport tempfile\nfrom dataclasses import asdict, dataclass\n\n\n@dataclass\nclass Book:\n    title: str\n    author: str\n    borrowed: bool = False\n\n\nclass Library:\n    def __init__(self):\n        self.books = {}\n\n    def add(self, book):\n        self.books[book.title] = book\n\n    def borrow(self, title):\n        book = self.books[title]\n        if book.borrowed:\n            raise ValueError(f\"{title} is already borrowed\")\n        book.borrowed = True\n\n    def give_back(self, title):\n        self.books[title].borrowed = False\n\n    def save(self, path):\n        with open(path, \"w\", encoding=\"utf-8\") as f:\n            json.dump([asdict(b) for b in self.books.values()], f, ensure_ascii=False)\n\n    @classmethod\n    def load(cls, path):\n        library = cls()\n        with open(path, encoding=\"utf-8\") as f:\n            for item in json.load(f):\n                library.add(Book(**item))\n        return library\n\n\nlibrary = Library()\nlibrary.add(Book(\"Dune\", \"Frank Herbert\"))\nlibrary.add(Book(\"Clean Code\", \"Robert Martin\"))\nlibrary.borrow(\"Dune\")\n\npath = os.path.join(tempfile.gettempdir(), \"library.json\")\nlibrary.save(path)\n\nrestored = Library.load(path)\nprint(\"Borrowed:\", \", \".join(b.title for b in restored.books.values() if b.borrowed))\ntry:\n    restored.borrow(\"Dune\")\nexcept ValueError as err:\n    print(\"Error:\", err)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>خروجی:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-text\">Borrowed: Dune\nError: Dune is already borrowed\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>این پروژه تقریباً همه‌ی مباحث بالا را کنار هم می‌گذارد: کلاس، \u003Ccode>classmethod\u003C\u002Fcode> برای ساختن کتابخانه از فایل، \u003Ccode>with\u003C\u002Fcode> برای کار امن با فایل و مدیریت خطا. \u003Ccode>dataclass\u003C\u002Fcode> متدهای \u003Ccode>__init__\u003C\u002Fcode> و \u003Ccode>__repr__\u003C\u002Fcode> را خودکار می‌سازد و \u003Ccode>asdict\u003C\u002Fcode> شیء را برای ذخیره در JSON به دیکشنری تبدیل می‌کند. برای تمرین بیشتر، تاریخ امانت و جریمه‌ی دیرکرد را اضافه کنید.\u003C\u002Fp>\n\n\u003Ch2>بعد از این ۱۵ تمرین\u003C\u002Fh2>\n\n\u003Cp>اگر بیشتر این تمرین‌ها را خودتان حل کردید، پایه‌ی پایتون پیشرفته را دارید و وقت آن است که این مهارت‌ها را در یک پروژه‌ی واقعی به کار ببرید. چند پیشنهاد:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>پروژه‌ی کتابخانه‌ی تمرین آخر را با دیتابیس SQLite به‌جای فایل JSON بازنویسی کنید.\u003C\u002Fli>\n\u003Cli>برای کلاس‌های تمرین‌های ۱ تا ۴ با ماژول \u003Ccode>unittest\u003C\u002Fcode> یا کتابخانه‌ی pytest تست بنویسید.\u003C\u002Fli>\n\u003Cli>یکی از \u003Ca href=\"\u002Flearning\u002Fproje-python-mobtadi\">۱۰ پروژه‌ی پایتون برای مبتدی‌ها\u003C\u002Fa> را با شی‌گرایی و مدیریت خطا از نو بنویسید؛ تفاوت کد قبل و بعد را خواهید دید.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>بعد از پایتون پیشرفته معمولاً یکی از مسیرهای تخصصی انتخاب می‌شود: توسعه‌ی وب، تحلیل داده یا هوش مصنوعی. اگر هوش مصنوعی برایتان جذاب است، \u003Ca href=\"\u002Flearning\u002Fpython-baraye-hoosh-masnooi\">آموزش پایتون برای هوش مصنوعی\u003C\u002Fa> را ببینید.\u003C\u002Fp>\n\n\u003Cp>در \u003Ca href=\"\u002Fcourses\u002Fpython-advanced\">دوره‌ی پایتون پیشرفته‌ی داناورس\u003C\u002Fa> همین مباحث، یعنی شی‌گرایی، ساختمان داده، کار با API و پروژه‌های حرفه‌ای، در کلاس زنده و آنلاین با بازبینی کد توسط منتور تمرین می‌شود. بازبینی کد همان چیزی است که در خودآموزی بیشتر از همه کم است: کسی که بگوید کد شما کار می‌کند ولی چرا خوانا نیست.\u003C\u002Fp>\n\n\u003Ch2>سوالات رایج\u003C\u002Fh2>\n\n\u003Ch3>این تمرین‌ها برای امتحان پایتون پیشرفته مناسب است؟\u003C\u002Fh3>\n\u003Cp>مباحث این صفحه، یعنی شی‌گرایی، دکوراتور، ژنراتور، مدیریت خطا و کار با فایل، در بیشتر سرفصل‌های پایتون پیشرفته هست. ولی شکل سوال‌های هر امتحان به منبع درس بستگی دارد؛ برای امتحان کلاسی، سوال‌های نمونه‌ی خود استاد یا منبع درس را هم حتماً تمرین کنید.\u003C\u002Fp>\n\n\u003Ch3>با کدام نسخه‌ی پایتون اجرا کنم؟\u003C\u002Fh3>\n\u003Cp>همه‌ی کدها با پایتون ۳ و فقط با کتابخانه‌های استاندارد نوشته شده‌اند و نصب چیز اضافه‌ای لازم ندارند.\u003C\u002Fp>\n\n\u003Ch3>اگر نتوانستم بیشتر تمرین‌ها را حل کنم چه کنم؟\u003C\u002Fh3>\n\u003Cp>نگران نباشید؛ یعنی هنوز به مرور مباحث مقدماتی، به‌خصوص تابع و دیکشنری، نیاز دارید. بعد از مرور، تمرین‌ها را از اول شروع کنید. تمرین ۱ تا ۵ پایه‌ی بقیه است.\u003C\u002Fp>\n  ","بعد از پایتون مقدماتی، بیشتر آدم‌ها جایی گیر می‌کنند که مفهوم‌ها را خوانده‌اند ولی وقتی باید خودشان کلاس، دکوراتور یا ژنراتور بنویسند، نمی‌دانند از کجا شروع کنند. راه عبور از این م...","آموزش برنامه‌نویسی","۱۱ مهر ۱۴۰۵","۱۲ دقیقه",2317,"\u002Fimages\u002Fblog\u002Ftamrin-python-pishrafte.jpg","تیم تولید محتوای داناورس","مدرس و کارشناس آکادمی","\u002Fimages\u002Flogo-light.png","مجید مهربخش",[10,20],"داناورس","python-advanced","tutorial",[24,25,26],"۱۵ تمرین پایتون پیشرفته به ترتیب سختی، هر کدام با جواب کامل، خروجی واقعی و توضیح.","مباحث: شی‌گرایی، توابع پیشرفته (args و kwargs و دکوراتور)، ژنراتور و پیمایشگر، context manager و داده، و پروژه‌ی پایانی کتابخانه با dataclass و JSON.","برای کسی است که پایتون مقدماتی را بلد است؛ قبل از دیدن جواب، حداقل ۱۵ دقیقه خودتان روی هر تمرین کار کنید.",[28,31,34],{"q":29,"a":30},"این تمرین‌ها برای امتحان پایتون پیشرفته مناسب است؟","مباحث این صفحه، یعنی شی‌گرایی، دکوراتور، ژنراتور، مدیریت خطا و کار با فایل، در بیشتر سرفصل‌های پایتون پیشرفته هست. ولی شکل سوال‌های هر امتحان به منبع درس بستگی دارد؛ برای امتحان کلاسی، سوال‌های نمونه‌ی خود استاد یا منبع درس را هم حتماً تمرین کنید.",{"q":32,"a":33},"با کدام نسخه‌ی پایتون اجرا کنم؟","همه‌ی کدها با پایتون ۳ و فقط با کتابخانه‌های استاندارد نوشته شده‌اند و نصب چیز اضافه‌ای لازم ندارند.",{"q":35,"a":36},"اگر نتوانستم بیشتر تمرین‌ها را حل کنم چه کنم؟","نگران نباشید؛ یعنی هنوز به مرور مباحث مقدماتی، به‌خصوص تابع و دیکشنری، نیاز دارید. بعد از مرور، تمرین‌ها را از اول شروع کنید. تمرین ۱ تا ۵ پایه‌ی بقیه است.","تمرین پایتون پیشرفته؛ ۱۵ تمرین با جواب و توضیح","۱۵ تمرین پایتون پیشرفته با جواب و خروجی: کلاس، ارث‌بری، متدهای ویژه، property، دکوراتور، closure، ژنراتور، context manager، Counter و پروژه‌ی کتابخانه با JSON.","2026-10-03T08:38:39.77264+00:00",null,[42,47,52],{"path":43,"title":44,"desc":45,"image":46},"\u002Flearning\u002Fpython-pishrafte","آموزش پایتون پیشرفته: بعد از پایتون مقدماتی دقیقاً چه چیزهایی یاد بگیرم؟","آموزش پایتون پیشرفته قدم‌به‌قدم: شی‌گرایی، مدیریت خطا، دکوراتور، ژنراتور، JSON، دیتابیس SQL، محیط مجازی و تست، با مثال کد و مسیرهای تخصصی بعد از آن.","\u002Fimages\u002Fblog\u002Fpython-pishrafte.jpg",{"path":48,"title":49,"desc":50,"image":51},"\u002Flearning\u002Famoozesh-barnamenevisi-online","آموزش برنامه‌نویسی آنلاین: کلاس زنده، دوره‌ی ضبط‌شده یا منابع رایگان؟ راهنمای انتخاب","مقایسه‌ی سه راه یادگیری برنامه‌نویسی آنلاین: کلاس زنده، دوره ضبط‌شده و منابع رایگان؛ مزایا و معایب، وسایل لازم، ۷ سوال قبل از ثبت‌نام، برای بزرگسال و کودک.","\u002Fimages\u002Fblog\u002Famoozesh-barnamenevisi-online.jpg",{"path":53,"title":54,"desc":55,"image":56},"\u002Flearning\u002Famoozesh-arduino-az-sefr","آموزش آردوینو از صفر: از نصب نرم‌افزار تا اولین ربات، به زبان ساده","آموزش آردوینو از صفر: انتخاب برد اونو یا نانو، نصب نرم‌افزار و درایور، اولین برنامه، کار با سنسور و سروو و ۵ پروژه‌ی مبتدی، حتی بدون برد با شبیه‌ساز.","\u002Fimages\u002Fblog\u002Famoozesh-arduino-az-sefr.jpg",[]]