🐍 آموزش جامع Python — پست ۱۰۷
مقایسه اشیاء با
در پست قبل یاد گرفتیم با
حالا میخواهیم رفتار عملگر
برای این کار از:
استفاده میکنیم.
🔹
یعنی:
مثلاً میتوانیم محصولات را بر اساس قیمت مقایسه کنیم:
حالا:
خروجی:
چون:
🔹 مقایسه برعکس
خروجی:
چون:
صحیح نیست.
🔹
وقتی مینویسیم:
Python تقریباً این متد را فراخوانی میکند:
بنابراین:
به
به
⚠️ نکته مهم
یعنی با تعریف آن، لزوماً تمام عملگرهای مقایسهای دیگر (
هرکدام از این رفتارها متدهای ویژه مربوط به خودشان را دارند.
🔹 مثال کاربردی
میتوانیم دانشآموزها را بر اساس نمره مقایسه کنیم:
خروجی:
چون نمره
🎯 تمرین
کلاس
خروجی مورد انتظار:
پست ۱۰۷ از دوره جامع Python 🐍
مقایسه اشیاء با
__lt__در پست قبل یاد گرفتیم با
__eq__ مشخص کنیم دو شیء چه زمانی برابر هستند.حالا میخواهیم رفتار عملگر
< را برای اشیای کلاس خودمان تعریف کنیم.برای این کار از:
__lt__
استفاده میکنیم.
🔹
__lt__ چیست؟__lt__ مخفف Less Than است.یعنی:
<
مثلاً میتوانیم محصولات را بر اساس قیمت مقایسه کنیم:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __lt__(self, other):
return self.price < other.price
حالا:
product1 = Product("Mouse", 20)
product2 = Product("Keyboard", 50)
print(product1 < product2)خروجی:
True
چون:
20 < 50
🔹 مقایسه برعکس
print(product2 < product1)
خروجی:
False
چون:
50 < 20
صحیح نیست.
🔹
__lt__ چگونه اجرا میشود؟وقتی مینویسیم:
product1 < product2
Python تقریباً این متد را فراخوانی میکند:
product1.__lt__(product2)
بنابراین:
self
به
product1 و:other
به
product2 اشاره میکند.⚠️ نکته مهم
__lt__ فقط رفتار < را تعریف میکند.یعنی با تعریف آن، لزوماً تمام عملگرهای مقایسهای دیگر (
>, <=, >=) را برای کلاس تعریف نکردهایم.هرکدام از این رفتارها متدهای ویژه مربوط به خودشان را دارند.
🔹 مثال کاربردی
میتوانیم دانشآموزها را بر اساس نمره مقایسه کنیم:
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __lt__(self, other):
return self.score < other.score
student1 = Student("Ali", 15)
student2 = Student("Sara", 18)
print(student1 < student2)
خروجی:
True
چون نمره
Ali کمتر از نمره Sara است.🎯 تمرین
کلاس
Book را کامل کن تا دو کتاب بر اساس قیمت با < مقایسه شوند:class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __lt__(self, other):
# کد اینجا
book1 = Book("Python", 30)
book2 = Book("Django", 45)
print(book1 < book2)
print(book2 < book1)
خروجی مورد انتظار:
True
False
پست ۱۰۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۰۸
مقایسه اشیاء با
در پست قبل با
حالا نوبت عملگر بزرگتر است:
برای تعریف این رفتار از متد جادویی:
استفاده میکنیم.
🔹
مثلاً میتوانیم محصولات را بر اساس قیمت مقایسه کنیم:
حالا:
خروجی:
چون:
🔹 مقایسه برعکس
خروجی:
چون:
صحیح نیست.
🔹
وقتی مینویسیم:
Python از متد مقایسهای مربوط به این عملگر استفاده میکند؛ در حالت معمول:
بنابراین:
به شیء سمت چپ و:
به شیء سمت راست اشاره میکند.
🔹 مثال با دانشآموز
خروجی:
چون نمره
⚠️ نکته مهم
را برای کلاس تعریف میکند.
برای عملگرهایی مانند:
متدهای مقایسهای دیگری وجود دارند.
در ادامه با آنها آشنا میشویم.
🎯 تمرین
کلاس
خروجی مورد انتظار:
پست ۱۰۸ از دوره جامع Python 🐍
مقایسه اشیاء با
__gt__در پست قبل با
__lt__ یاد گرفتیم رفتار عملگر < را برای اشیای کلاس خودمان تعریف کنیم.حالا نوبت عملگر بزرگتر است:
>
برای تعریف این رفتار از متد جادویی:
__gt__
استفاده میکنیم.
🔹
__gt__ چیست؟__gt__ مخفف Greater Than است.مثلاً میتوانیم محصولات را بر اساس قیمت مقایسه کنیم:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __gt__(self, other):
return self.price > other.price
حالا:
product1 = Product("Laptop", 1200)
product2 = Product("Mouse", 50)
print(product1 > product2)خروجی:
True
چون:
1200 > 50
🔹 مقایسه برعکس
print(product2 > product1)
خروجی:
False
چون:
50 > 1200
صحیح نیست.
🔹
__gt__ چگونه اجرا میشود؟وقتی مینویسیم:
product1 > product2
Python از متد مقایسهای مربوط به این عملگر استفاده میکند؛ در حالت معمول:
product1.__gt__(product2)
بنابراین:
self
به شیء سمت چپ و:
other
به شیء سمت راست اشاره میکند.
🔹 مثال با دانشآموز
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __gt__(self, other):
return self.score > other.score
student1 = Student("Ali", 18)
student2 = Student("Sara", 15)
print(student1 > student2)
خروجی:
True
چون نمره
18 از 15 بیشتر است.⚠️ نکته مهم
__gt__ فقط رفتار عملگر:>
را برای کلاس تعریف میکند.
برای عملگرهایی مانند:
< <= >=
متدهای مقایسهای دیگری وجود دارند.
در ادامه با آنها آشنا میشویم.
🎯 تمرین
کلاس
Car را کامل کن تا خودروها بر اساس قیمت با > مقایسه شوند:class Car:
def __init__(self, model, price):
self.model = model
self.price = price
def __gt__(self, other):
# کد اینجا
car1 = Car("BMW", 50000)
car2 = Car("Toyota", 30000)
print(car1 > car2)
print(car2 > car1)
خروجی مورد انتظار:
True
False
پست ۱۰۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۰۹
مقایسه اشیاء با
در دو پست قبل با:
رفتار
حالا دو عملگر دیگر را یاد میگیریم:
🔹
یعنی:
مثلاً:
حالا:
خروجی:
چون:
صحیح است.
🔹
یعنی:
مثلاً:
حالا:
خروجی:
چون:
صحیح است.
🔹 یک مثال کامل
میتوانیم هر دو متد را در یک کلاس داشته باشیم:
خروجی:
چون نمره هر دو دانشآموز
🧠 متدهای مقایسهای مهم
تا اینجا با این متدها آشنا شدیم:
این متدها به Python میگویند عملگرهای مقایسهای با اشیای کلاس ما چگونه رفتار کنند.
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در این قسمت:
قیمت کتاب سمت چپ با قیمت کتاب سمت راست مقایسه میشود.
و در:
برعکس، بررسی میکنیم که قیمت کتاب سمت چپ بزرگتر یا مساوی قیمت کتاب سمت راست باشد.
پست ۱۰۹ از دوره جامع Python 🐍
مقایسه اشیاء با
__le__ و __ge__در دو پست قبل با:
__lt__
__gt__
رفتار
< و > را برای اشیای کلاس تعریف کردیم.حالا دو عملگر دیگر را یاد میگیریم:
<=
>=
🔹
__le____le__ مخفف Less Than or Equal است.یعنی:
کوچکتر یا مساوی
مثلاً:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __le__(self, other):
return self.price <= other.price
حالا:
product1 = Product("Mouse", 50)
product2 = Product("Keyboard", 50)
print(product1 <= product2)خروجی:
True
چون:
50 <= 50
صحیح است.
🔹
__ge____ge__ مخفف Greater Than or Equal است.یعنی:
بزرگتر یا مساوی
مثلاً:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __ge__(self, other):
return self.price >= other.price
حالا:
product1 = Product("Laptop", 1200)
product2 = Product("Mouse", 50)
print(product1 >= product2)خروجی:
True
چون:
1200 >= 50
صحیح است.
🔹 یک مثال کامل
میتوانیم هر دو متد را در یک کلاس داشته باشیم:
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __le__(self, other):
return self.score <= other.score
def __ge__(self, other):
return self.score >= other.score
student1 = Student("Ali", 18)
student2 = Student("Sara", 18)
print(student1 <= student2)
print(student1 >= student2)
خروجی:
True
True
چون نمره هر دو دانشآموز
18 است.🧠 متدهای مقایسهای مهم
تا اینجا با این متدها آشنا شدیم:
__eq__ → ==
__lt__ → <
__gt__ → >
__le__ → <=
__ge__ → >=
این متدها به Python میگویند عملگرهای مقایسهای با اشیای کلاس ما چگونه رفتار کنند.
🎯 تمرین
کلاس
Book را کامل کن تا دو کتاب بر اساس قیمت با <= و >= مقایسه شوند:class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __le__(self, other):
# کد اینجا
def __ge__(self, other):
# کد اینجا
book1 = Book("Python", 30)
book2 = Book("Django", 45)
print(book1 <= book2)
print(book1 >= book2)
خروجی مورد انتظار:
True
False
✅ پاسخ کامل تمرین
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __le__(self, other):
return self.price <= other.price
def __ge__(self, other):
return self.price >= other.price
book1 = Book("Python", 30)
book2 = Book("Django", 45)
print(book1 <= book2)
print(book1 >= book2)
خروجی:
True
False
در این قسمت:
return self.price <= other.price
قیمت کتاب سمت چپ با قیمت کتاب سمت راست مقایسه میشود.
و در:
return self.price >= other.price
برعکس، بررسی میکنیم که قیمت کتاب سمت چپ بزرگتر یا مساوی قیمت کتاب سمت راست باشد.
پست ۱۰۹ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۰
بررسی نابرابری اشیاء با
در پستهای قبل با متدهای مقایسهای مختلف آشنا شدیم.
حالا میخواهیم رفتار عملگر:
را برای اشیای کلاس خودمان تعریف کنیم.
برای این کار از متد جادویی:
استفاده میکنیم.
🔹
یعنی بررسی میکند آیا دو شیء با هم متفاوت هستند یا نه.
مثلاً:
حالا:
خروجی:
چون قیمت دو محصول متفاوت است.
🔹 وقتی هر دو مقدار یکسان باشند
خروجی:
چون دو محصول از نظر
🔹
وقتی مینویسیم:
Python میتواند متد زیر را فراخوانی کند:
بنابراین:
⚠️ نکته مهم
در مثال بالا از
چون اگر حتی یکی از این دو مقدار متفاوت باشد، دو محصول متفاوت محسوب میشوند.
مثلاً:
🧠 متدهای مقایسهای تا اینجا
با این متدها میتوانیم رفتار اصلی عملگرهای مقایسهای را برای کلاس خودمان تعریف کنیم.
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در این قسمت:
نام کاربری دو شیء مقایسه میشود.
و:
سن دو شیء مقایسه میشود.
از
پست ۱۱۰ از دوره جامع Python 🐍
بررسی نابرابری اشیاء با
__ne__در پستهای قبل با متدهای مقایسهای مختلف آشنا شدیم.
حالا میخواهیم رفتار عملگر:
!=
را برای اشیای کلاس خودمان تعریف کنیم.
برای این کار از متد جادویی:
__ne__
استفاده میکنیم.
🔹
__ne__ چیست؟__ne__ مخفف Not Equal است.یعنی بررسی میکند آیا دو شیء با هم متفاوت هستند یا نه.
مثلاً:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __ne__(self, other):
return (
self.name != other.name
or self.price != other.price
)
حالا:
product1 = Product("Laptop", 1200)
product2 = Product("Laptop", 1500)
print(product1 != product2)خروجی:
True
چون قیمت دو محصول متفاوت است.
🔹 وقتی هر دو مقدار یکسان باشند
product1 = Product("Laptop", 1200)
product2 = Product("Laptop", 1200)
print(product1 != product2)خروجی:
False
چون دو محصول از نظر
name و price یکسان هستند.🔹
__ne__ چگونه اجرا میشود؟وقتی مینویسیم:
product1 != product2
Python میتواند متد زیر را فراخوانی کند:
product1.__ne__(product2)
بنابراین:
self → شیء سمت چپ
other → شیء سمت راست
⚠️ نکته مهم
در مثال بالا از
or استفاده کردیم:self.name != other.name or self.price != other.price
چون اگر حتی یکی از این دو مقدار متفاوت باشد، دو محصول متفاوت محسوب میشوند.
مثلاً:
name یکسان + price متفاوت → True
name متفاوت + price یکسان → True
name متفاوت + price متفاوت → True
name یکسان + price یکسان → False
🧠 متدهای مقایسهای تا اینجا
__eq__ → ==
__ne__ → !=
__lt__ → <
__gt__ → >
__le__ → <=
__ge__ → >=
با این متدها میتوانیم رفتار اصلی عملگرهای مقایسهای را برای کلاس خودمان تعریف کنیم.
🎯 تمرین
کلاس
User را کامل کن تا دو کاربر وقتی username یا age متفاوت دارند، متفاوت محسوب شوند:class User:
def __init__(self, username, age):
self.username = username
self.age = age
def __ne__(self, other):
# کد اینجا
user1 = User("ali", 25)
user2 = User("ali", 25)
user3 = User("sara", 25)
print(user1 != user2)
print(user1 != user3)
خروجی مورد انتظار:
False
True
✅ پاسخ کامل تمرین
class User:
def __init__(self, username, age):
self.username = username
self.age = age
def __ne__(self, other):
if not isinstance(other, User):
return NotImplemented
return (
self.username != other.username
or self.age != other.age
)
user1 = User("ali", 25)
user2 = User("ali", 25)
user3 = User("sara", 25)
print(user1 != user2)
print(user1 != user3)
خروجی:
False
True
در این قسمت:
self.username != other.username
نام کاربری دو شیء مقایسه میشود.
و:
self.age != other.age
سن دو شیء مقایسه میشود.
از
or استفاده میکنیم، چون متفاوت بودن حتی یکی از ویژگیها کافی است تا دو کاربر متفاوت محسوب شوند.پست ۱۱۰ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۱۱
کنترل عملگر
تا اینجا چندین Magic Method برای کنترل رفتار اشیاء یاد گرفتیم.
حالا میخواهیم مشخص کنیم وقتی از عملگر:
برای یک شیء استفاده میکنیم، Python چگونه بررسی کند که یک مقدار داخل آن شیء وجود دارد یا نه.
برای این کار از:
استفاده میکنیم.
🔹
این متد رفتار عملگر
مثلاً یک کلاس
حالا:
خروجی:
🔹
وقتی
خروجی:
🔹
وقتی مینویسیم:
Python از رفتار تعریفشده در:
استفاده میکند.
در این متد:
پارامتر
🔹 مثال با
میتوانیم بررسی کنیم آیا یک نام کاربری در گروه وجود دارد یا نه:
خروجی:
⚠️ نکته مهم
یا:
در واقع میتوانیم منطق بررسی را کاملاً مطابق نیاز کلاس خودمان تعریف کنیم.
🧠 چند Magic Method مهم تا اینجا
این متدها به ما اجازه میدهند رفتار طبیعی Python را برای کلاسهای خودمان شخصیسازی کنیم.
🎯 تمرین
کلاس
خروجی مورد انتظار:
پست ۱۱۱ از دوره جامع Python 🐍
کنترل عملگر
in با __contains__تا اینجا چندین Magic Method برای کنترل رفتار اشیاء یاد گرفتیم.
حالا میخواهیم مشخص کنیم وقتی از عملگر:
in
برای یک شیء استفاده میکنیم، Python چگونه بررسی کند که یک مقدار داخل آن شیء وجود دارد یا نه.
برای این کار از:
__contains__
استفاده میکنیم.
🔹
__contains__ چیست؟این متد رفتار عملگر
in را برای شیء مشخص میکند.مثلاً یک کلاس
Playlist میسازیم:class Playlist:
def __init__(self, songs):
self.songs = songs
def __contains__(self, song):
return song in self.songs
حالا:
playlist = Playlist([
"Song A",
"Song B",
"Song C"
])
print("Song B" in playlist)
print("Song X" in playlist)
خروجی:
True
False
🔹
not inوقتی
__contains__ را تعریف کنیم، میتوانیم از not in هم استفاده کنیم:print("Song X" not in playlist)خروجی:
True
🔹
__contains__ چگونه اجرا میشود؟وقتی مینویسیم:
"Song B" in playlist
Python از رفتار تعریفشده در:
playlist.__contains__("Song B")استفاده میکند.
در این متد:
def __contains__(self, song):
پارامتر
song همان مقداری است که میخواهیم وجود آن را بررسی کنیم.🔹 مثال با
UserGroupمیتوانیم بررسی کنیم آیا یک نام کاربری در گروه وجود دارد یا نه:
class UserGroup:
def __init__(self, users):
self.users = users
def __contains__(self, username):
return username in self.users
group = UserGroup(["ali", "sara", "reza"])
print("ali" in group)
print("mina" in group)
خروجی:
True
False
⚠️ نکته مهم
__contains__ باید نتیجهای مناسب برای بررسی وجود داشتن برگرداند؛ معمولاً:True
یا:
False
در واقع میتوانیم منطق بررسی را کاملاً مطابق نیاز کلاس خودمان تعریف کنیم.
🧠 چند Magic Method مهم تا اینجا
__str__ → print()
__repr__ → repr()
__len__ → len()
__eq__ → ==
__ne__ → !=
__lt__ → <
__gt__ → >
__le__ → <=
__ge__ → >=
__contains__ → in
این متدها به ما اجازه میدهند رفتار طبیعی Python را برای کلاسهای خودمان شخصیسازی کنیم.
🎯 تمرین
کلاس
Library را کامل کن تا بتوانیم با in بررسی کنیم آیا یک کتاب در کتابخانه وجود دارد یا نه:class Library:
def __init__(self, books):
self.books = books
def __contains__(self, title):
# کد اینجا
library = Library([
"Python",
"Django",
"Flask"
])
print("Python" in library)
print("Java" in library)
خروجی مورد انتظار:
True
False
پست ۱۱۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۲
دسترسی به عناصر شیء با
تا اینجا یاد گرفتیم با
حالا میخواهیم کاری کنیم که بتوانیم با استفاده از
برای این کار از
🔹
وقتی Python چنین کدی میبیند:
در واقع میتواند متد زیر را صدا بزند:
🔹 مثال ساده
خروجی:
در اینجا:
باعث میشود Python از
🔹 پشتیبانی از Index منفی
چون در مثال بالا دسترسی نهایی به یک
خروجی:
🔹 پشتیبانی از Slice
خروجی:
چون مقدار
⚠️ نکته مهم
در مثال ما:
دادهها را نگهداری میکند و
🧠 خلاصه
مثلاً:
تقریباً معادل:
است.
🎯 تمرین
کلاس
خروجی مورد انتظار:
پست ۱۱۲ از دوره جامع Python 🐍
دسترسی به عناصر شیء با
__getitem__تا اینجا یاد گرفتیم با
__contains__ رفتار in را برای یک شیء کنترل کنیم.حالا میخواهیم کاری کنیم که بتوانیم با استفاده از
[] به عناصر یک شیء دسترسی داشته باشیم.برای این کار از
__getitem__ استفاده میکنیم.🔹
__getitem__وقتی Python چنین کدی میبیند:
object[index]
در واقع میتواند متد زیر را صدا بزند:
object.__getitem__(index)
🔹 مثال ساده
class Playlist:
def __init__(self, songs):
self.songs = songs
def __getitem__(self, index):
return self.songs[index]
playlist = Playlist([
"Song A",
"Song B",
"Song C"
])
print(playlist[0])
print(playlist[1])
خروجی:
Song A
Song B
در اینجا:
playlist[0]
باعث میشود Python از
__getitem__ استفاده کند.🔹 پشتیبانی از Index منفی
چون در مثال بالا دسترسی نهایی به یک
list انجام میشود، Index منفی هم کار میکند:print(playlist[-1])
خروجی:
Song C
🔹 پشتیبانی از Slice
__getitem__ فقط برای یک Index نیست؛ میتوانیم Slice هم دریافت کنیم:print(playlist[0:2])
خروجی:
['Song A', 'Song B']
چون مقدار
0:2 به عنوان index به __getitem__ ارسال میشود.⚠️ نکته مهم
__getitem__ خودش الزاماً دادهها را نگهداری نمیکند.در مثال ما:
self.songs
دادهها را نگهداری میکند و
__getitem__ فقط نحوه دسترسی به آنها را مشخص میکند.🧠 خلاصه
obj[index]
↓
__getitem__(index)
مثلاً:
playlist[1]
تقریباً معادل:
playlist.__getitem__(1)
است.
🎯 تمرین
کلاس
Library را کامل کن تا بتوانیم با Index به کتابها دسترسی داشته باشیم:class Library:
def __init__(self, books):
self.books = books
def __getitem__(self, index):
# code
library = Library([
"Python",
"Django",
"Flask"
])
print(library[0])
print(library[2])
خروجی مورد انتظار:
Python
Flask
پست ۱۱۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۳
تغییر عناصر شیء با
در پست قبل یاد گرفتیم با
حالا میخواهیم با همان
برای این کار از
🔹
وقتی Python چنین کدی میبیند:
میتواند متد زیر را اجرا کند:
🔹 مثال ساده
خروجی:
در اینجا:
باعث میشود Python از
🔹 چه اتفاقی میافتد؟
این کد:
تقریباً به این شکل عمل میکند:
و داخل متد:
مقدار داخل لیست تغییر میکند.
🔹 ترکیب
با داشتن هر دو متد، شیء ما میتواند هم خوانده و هم تغییر داده شود:
خروجی:
⚠️ نکته مهم
اما
پس:
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
پست ۱۱۳ از دوره جامع Python 🐍
تغییر عناصر شیء با
__setitem__در پست قبل یاد گرفتیم با
__getitem__ کاری کنیم که بتوانیم از [] برای خواندن عناصر یک شیء استفاده کنیم.حالا میخواهیم با همان
[] بتوانیم مقدار یک عنصر را تغییر دهیم.برای این کار از
__setitem__ استفاده میکنیم.🔹
__setitem__وقتی Python چنین کدی میبیند:
object[index] = value
میتواند متد زیر را اجرا کند:
object.__setitem__(index, value)
🔹 مثال ساده
class Playlist:
def __init__(self, songs):
self.songs = songs
def __getitem__(self, index):
return self.songs[index]
def __setitem__(self, index, value):
self.songs[index] = value
playlist = Playlist([
"Song A",
"Song B",
"Song C"
])
playlist[1] = "New Song"
print(playlist[1])
خروجی:
New Song
در اینجا:
playlist[1] = "New Song"
باعث میشود Python از
__setitem__ استفاده کند.🔹 چه اتفاقی میافتد؟
این کد:
playlist[1] = "New Song"
تقریباً به این شکل عمل میکند:
playlist.__setitem__(1, "New Song")
و داخل متد:
self.songs[index] = value
مقدار داخل لیست تغییر میکند.
🔹 ترکیب
__getitem__ و __setitem__با داشتن هر دو متد، شیء ما میتواند هم خوانده و هم تغییر داده شود:
playlist = Playlist([
"Song A",
"Song B",
"Song C"
])
print(playlist[0])
playlist[0] = "Updated Song"
print(playlist[0])
خروجی:
Song A
Updated Song
⚠️ نکته مهم
__getitem__ برای دریافت مقدار است:playlist[0]
اما
__setitem__ برای قرار دادن یا تغییر مقدار است:playlist[0] = "New Song"
پس:
__getitem__ → obj[index]
__setitem__ → obj[index] = value
🎯 تمرین
کلاس
Inventory را کامل کن:class Inventory:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
# code
def __setitem__(self, index, value):
# code
inventory = Inventory([
"Keyboard",
"Mouse",
"Monitor"
])
print(inventory[1])
inventory[1] = "Gaming Mouse"
print(inventory[1])
خروجی مورد انتظار:
Mouse
Gaming Mouse
✅ پاسخ کامل تمرین
class Inventory:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
inventory = Inventory([
"Keyboard",
"Mouse",
"Monitor"
])
print(inventory[1])
inventory[1] = "Gaming Mouse"
print(inventory[1])
خروجی:
Mouse
Gaming Mouse
پست ۱۱۳ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۴
پیمایش روی شیء با
تا اینجا یاد گرفتیم با
حالا میخواهیم کاری کنیم که بتوانیم مستقیماً روی شیء با
برای این کار از
🔹
وقتی مینویسیم:
Python باید بداند چگونه روی
یکی از روشهای اصلی این کار استفاده از:
است.
🔹 مثال ساده
خروجی:
🔹 چرا
در اینجا:
تابع
سپس
🔹 استفاده با
یک شیء قابل پیمایش را میتوان به لیست هم تبدیل کرد:
خروجی:
🔹 یک مثال دیگر
خروجی:
⚠️ نکته مهم
در مثال ما:
این کار را برای لیست داخلی انجام میدهد.
بنابراین:
🧠 مقایسه تا اینجا
هرکدام رفتار متفاوتی از شیء را کنترل میکنند.
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
پست ۱۱۴ از دوره جامع Python 🐍
پیمایش روی شیء با
__iter__تا اینجا یاد گرفتیم با
__getitem__ به عناصر یک شیء دسترسی داشته باشیم.حالا میخواهیم کاری کنیم که بتوانیم مستقیماً روی شیء با
for پیمایش کنیم.برای این کار از
__iter__ استفاده میکنیم.🔹
__iter__وقتی مینویسیم:
for item in object:
print(item)
Python باید بداند چگونه روی
object پیمایش کند.یکی از روشهای اصلی این کار استفاده از:
__iter__()
است.
🔹 مثال ساده
class Playlist:
def __init__(self, songs):
self.songs = songs
def __iter__(self):
return iter(self.songs)
playlist = Playlist([
"Song A",
"Song B",
"Song C"
])
for song in playlist:
print(song)
خروجی:
Song A
Song B
Song C
🔹 چرا
iter()؟در اینجا:
return iter(self.songs)
تابع
iter() از لیست یک iterator میسازد.سپس
for از آن برای گرفتن عناصر یکییکی استفاده میکند.🔹 استفاده با
list()یک شیء قابل پیمایش را میتوان به لیست هم تبدیل کرد:
print(list(playlist))
خروجی:
['Song A', 'Song B', 'Song C']
🔹 یک مثال دیگر
class Team:
def __init__(self, members):
self.members = members
def __iter__(self):
return iter(self.members)
team = Team([
"Ali",
"Sara",
"Reza"
])
for member in team:
print(member)
خروجی:
Ali
Sara
Reza
⚠️ نکته مهم
__iter__ باید یک iterator برگرداند.در مثال ما:
return iter(self.members)
این کار را برای لیست داخلی انجام میدهد.
بنابراین:
__iter__ → امکان پیمایش شیء با for
🧠 مقایسه تا اینجا
__contains__ → in
__getitem__ → obj[index]
__setitem__ → obj[index] = value
__iter__ → for item in obj
هرکدام رفتار متفاوتی از شیء را کنترل میکنند.
🎯 تمرین
کلاس
Library را طوری کامل کن که بتوانیم روی کتابهای آن با for پیمایش کنیم:class Library:
def __init__(self, books):
self.books = books
def __iter__(self):
# code
library = Library([
"Python",
"Django",
"Flask"
])
for book in library:
print(book)
خروجی مورد انتظار:
Python
Django
Flask
✅ پاسخ کامل تمرین
class Library:
def __init__(self, books):
self.books = books
def __iter__(self):
return iter(self.books)
library = Library([
"Python",
"Django",
"Flask"
])
for book in library:
print(book)
خروجی:
Python
Django
Flask
پست ۱۱۴ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۱۵
ساخت Iterator با
در پست قبل با
حالا میخواهیم ببینیم یک Iterator چگونه میتواند عناصر را یکییکی در اختیار Python قرار دهد.
برای این کار از
🔹
هر بار که
خروجی:
بعد از تمام شدن عناصر،
🔹 ساخت Iterator خودمان
میتوانیم کلاسی بسازیم که خودش Iterator باشد:
حالا:
خروجی:
🔹 استفاده با
چون کلاس ما
خروجی:
🔹 چرا
در این مثال:
خود شیء
پس Python همان شیء را برای دریافت مقادیر بعدی استفاده میکند.
🔹 نقش
وقتی دیگر مقداری باقی نمانده باشد:
به Python میگوییم:
🧠 تفاوت
برای ساخت یک Iterator معمولاً هر دو متد را داریم:
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در اینجا:
در هر مرحله عدد را یک واحد کاهش میدهد.
وقتی مقدار به
اجرا میشود و حلقه
پست ۱۱۵ از دوره جامع Python 🐍
ساخت Iterator با
__next__در پست قبل با
__iter__ آشنا شدیم.حالا میخواهیم ببینیم یک Iterator چگونه میتواند عناصر را یکییکی در اختیار Python قرار دهد.
برای این کار از
__next__ استفاده میکنیم.🔹
__next__هر بار که
next() را روی یک Iterator اجرا کنیم، عنصر بعدی را برمیگرداند:numbers = iter([10, 20, 30])
print(next(numbers))
print(next(numbers))
print(next(numbers))
خروجی:
10
20
30
بعد از تمام شدن عناصر،
next() با خطای StopIteration مواجه میشود.🔹 ساخت Iterator خودمان
میتوانیم کلاسی بسازیم که خودش Iterator باشد:
class Counter:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current > self.end:
raise StopIteration
value = self.current
self.current += 1
return value
حالا:
counter = Counter(1, 3)
print(next(counter))
print(next(counter))
print(next(counter))
خروجی:
1
2
3
🔹 استفاده با
forچون کلاس ما
__iter__ و __next__ دارد، میتوانیم از آن با for هم استفاده کنیم:counter = Counter(1, 3)
for number in counter:
print(number)
خروجی:
1
2
3
🔹 چرا
return self؟در این مثال:
def __iter__(self):
return self
خود شیء
Counter همان Iterator است.پس Python همان شیء را برای دریافت مقادیر بعدی استفاده میکند.
🔹 نقش
StopIterationوقتی دیگر مقداری باقی نمانده باشد:
if self.current > self.end:
raise StopIteration
به Python میگوییم:
دیگر عنصری برای پیمایش وجود ندارد.
for این وضعیت را مدیریت میکند و حلقه را متوقف میکند.🧠 تفاوت
__iter__ و __next____iter__ → Iterator را برمیگرداند
__next__ → عنصر بعدی را برمیگرداند
برای ساخت یک Iterator معمولاً هر دو متد را داریم:
__iter__()
__next__()
🎯 تمرین
کلاس
Countdown را کامل کن تا از عدد 3 تا 1 را یکییکی برگرداند:class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
# code
def __next__(self):
# code
countdown = Countdown(3)
for number in countdown:
print(number)
خروجی مورد انتظار:
3
2
1
✅ پاسخ کامل تمرین
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < 1:
raise StopIteration
value = self.current
self.current -= 1
return value
countdown = Countdown(3)
for number in countdown:
print(number)
خروجی:
3
2
1
در اینجا:
self.current -= 1
در هر مرحله عدد را یک واحد کاهش میدهد.
وقتی مقدار به
0 برسد:raise StopIteration
اجرا میشود و حلقه
for متوقف میشود.پست ۱۱۵ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۱۶
ساخت Generator با
در پستهای قبل با Iterator و
Python راه سادهتری برای ساخت Iteratorها دارد: Generator.
برای ساخت Generator معمولاً از
🔹
مثلاً:
حالا:
خروجی:
🔹 Generator Object
تابعی که
🔹 اجرای مرحلهای
Generator مقدارها را یکییکی تولید میکند:
خروجی:
بعد از تمام شدن Generator،
🔹 تفاوت
با
فقط اولین
اما با
مقادیر میتوانند یکییکی تولید شوند.
🔹 چرا Generator مهم است؟
فرض کن میخواهیم اعداد
ساختن یک لیست کامل:
همه مقادیر را در حافظه نگه میدارد.
اما Generator میتواند مقدارها را در زمان نیاز تولید کند:
در این حالت لازم نیست همه مقادیر از ابتدا در یک لیست ذخیره شوند.
این ویژگی برای کار با دادههای بزرگ بسیار مهم است.
🧠 خلاصه
و یک نکته مهم:
🎯 تمرین
یک Generator به نام
خروجی مورد انتظار:
پست ۱۱۶ از دوره جامع Python 🐍
ساخت Generator با
yieldدر پستهای قبل با Iterator و
__next__ آشنا شدیم.Python راه سادهتری برای ساخت Iteratorها دارد: Generator.
برای ساخت Generator معمولاً از
yield استفاده میکنیم.🔹
yield چیست؟yield شبیه return است، اما یک تفاوت مهم دارد:return اجرای تابع را تمام میکند.yield مقدار را برمیگرداند و وضعیت اجرای تابع را حفظ میکند.مثلاً:
def numbers():
yield 1
yield 2
yield 3
حالا:
for number in numbers():
print(number)
خروجی:
1
2
3
🔹 Generator Object
تابعی که
yield دارد، هنگام فراخوانی معمولی، نتیجه را یکجا اجرا نمیکند؛ یک Generator Object برمیگرداند:def numbers():
yield 1
yield 2
yield 3
result = numbers()
print(result)
result یک Generator است.🔹 اجرای مرحلهای
Generator مقدارها را یکییکی تولید میکند:
def numbers():
yield 1
yield 2
yield 3
numbers_generator = numbers()
print(next(numbers_generator))
print(next(numbers_generator))
print(next(numbers_generator))
خروجی:
1
2
3
بعد از تمام شدن Generator،
next() باعث StopIteration میشود.🔹 تفاوت
return و yieldبا
return:def get_number():
return 1
return 2
فقط اولین
return قابل اجراست.اما با
yield:def get_numbers():
yield 1
yield 2
مقادیر میتوانند یکییکی تولید شوند.
🔹 چرا Generator مهم است؟
فرض کن میخواهیم اعداد
1 تا 1,000,000 را تولید کنیم.ساختن یک لیست کامل:
numbers = list(range(1, 1_000_001))
همه مقادیر را در حافظه نگه میدارد.
اما Generator میتواند مقدارها را در زمان نیاز تولید کند:
def numbers():
for i in range(1, 1_000_001):
yield i
در این حالت لازم نیست همه مقادیر از ابتدا در یک لیست ذخیره شوند.
این ویژگی برای کار با دادههای بزرگ بسیار مهم است.
🧠 خلاصه
yield → تولید مقدار بهصورت مرحلهای
Generator → تولیدکننده مقدارها
next() → دریافت مقدار بعدی
StopIteration → پایان Generator
و یک نکته مهم:
هر تابعی که
yieldداشته باشد، یک Generator Function است.
🎯 تمرین
یک Generator به نام
count_up بنویس که اعداد 1 تا 5 را یکییکی تولید کند:def count_up():
# code
for number in count_up():
print(number)
خروجی مورد انتظار:
1
2
3
4
5
پست ۱۱۶ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۷
Generator با
در پست قبل دیدیم که
حالا یک مرحله جلوتر میرویم و یاد میگیریم Generator چگونه میتواند با حلقه و دادههای پویا کار کند.
🔹 تولید مقدار با حلقه
بهجای اینکه چند
حالا:
خروجی:
🔹 چرا
در
این اعداد را تولید میکند:
پس برای اینکه
🔹 Generator فقط تا زمانی که لازم است
میتوانیم فقط چند مقدار اول را دریافت کنیم:
خروجی:
در اینجا Generator لازم نیست همه اعداد را از ابتدا تولید کند.
هر بار که
🔹 یک مثال کاربردیتر
فرض کنیم میخواهیم فقط اعداد زوج را تولید کنیم:
حالا:
خروجی:
⚠️ نکته مهم
Generator با
به همین دلیل برای دادههای زیاد میتواند از نظر مصرف حافظه مناسبتر باشد.
🧠 خلاصه
🎯 تمرین
یک Generator به نام
خروجی مورد انتظار:
پست ۱۱۷ از دوره جامع Python 🐍
Generator با
yield و ورودی پویادر پست قبل دیدیم که
yield میتواند مقدارها را یکییکی تولید کند.حالا یک مرحله جلوتر میرویم و یاد میگیریم Generator چگونه میتواند با حلقه و دادههای پویا کار کند.
🔹 تولید مقدار با حلقه
بهجای اینکه چند
yield پشت سر هم بنویسیم، میتوانیم از حلقه استفاده کنیم:def count_up(limit):
for number in range(1, limit + 1):
yield number
حالا:
for number in count_up(5):
print(number)
خروجی:
1
2
3
4
5
🔹 چرا
limit + 1؟در
range() مقدار انتهایی شامل نمیشود:range(1, 5)
این اعداد را تولید میکند:
1
2
3
4
پس برای اینکه
5 هم تولید شود، مینویسیم:range(1, limit + 1)
🔹 Generator فقط تا زمانی که لازم است
میتوانیم فقط چند مقدار اول را دریافت کنیم:
numbers = count_up(100)
print(next(numbers))
print(next(numbers))
print(next(numbers))
خروجی:
1
2
3
در اینجا Generator لازم نیست همه اعداد را از ابتدا تولید کند.
هر بار که
next() اجرا میشود، Generator تا yield بعدی ادامه پیدا میکند.🔹 یک مثال کاربردیتر
فرض کنیم میخواهیم فقط اعداد زوج را تولید کنیم:
def even_numbers(limit):
for number in range(2, limit + 1, 2):
yield number
حالا:
for number in even_numbers(10):
print(number)
خروجی:
2
4
6
8
10
⚠️ نکته مهم
Generator با
yield مقدارها را یکییکی تولید میکند، نه اینکه همه را از ابتدا در یک لیست قرار دهد.به همین دلیل برای دادههای زیاد میتواند از نظر مصرف حافظه مناسبتر باشد.
🧠 خلاصه
yield → تولید مرحلهای مقدار
range → تولید دنباله عددی
next() → دریافت مقدار بعدی
Generator → تولید مقدار در زمان نیاز
🎯 تمرین
یک Generator به نام
odd_numbers بنویس که اعداد فرد از 1 تا 9 را تولید کند:def odd_numbers():
# code
for number in odd_numbers():
print(number)
خروجی مورد انتظار:
1
3
5
7
9
پست ۱۱۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۸
ارسال مقدار به Generator با
در پستهای قبل یاد گرفتیم Generator با
اما
🔹
مثال ساده:
برای شروع Generator ابتدا باید آن را اجرا کنیم:
حالا میتوانیم مقدار ارسال کنیم:
خروجی:
🔹
در این کد:
حالا:
خروجی:
🔹
میتوانیم هم مقدار دریافت کنیم و هم مقدار تولید کنیم:
حالا:
خروجی:
در اینجا:
مقدار
⚠️ نکته مهم
قبل از اولین
تا اولین
این کار Generator را آماده دریافت مقدار میکند.
🧠 خلاصه
Generator فقط یک تولیدکننده ساده نیست؛ میتواند بین کد بیرونی و خودش داده رد و بدل کند.
🎯 تمرین
Generator زیر را کامل کن تا عدد دریافتشده را دو برابر کند:
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در اینجا ابتدا:
Generator را تا اولین
سپس:
عدد
بنابراین:
و در نهایت:
مقدار
پست ۱۱۸ از دوره جامع Python 🐍
ارسال مقدار به Generator با
send()در پستهای قبل یاد گرفتیم Generator با
yield مقدار تولید میکند.اما
yield فقط برای خروجی گرفتن نیست؛ میتوانیم با send() یک مقدار را به Generator برگردانیم.🔹
send()مثال ساده:
def calculator():
number = yield
print(number)
برای شروع Generator ابتدا باید آن را اجرا کنیم:
calc = calculator()
next(calc)
حالا میتوانیم مقدار ارسال کنیم:
calc.send(10)
خروجی:
10
🔹
yield میتواند مقدار دریافت کنددر این کد:
def calculator():
number = yield
result = number * 2
print(result)
حالا:
calc = calculator()
next(calc)
calc.send(5)
خروجی:
10
🔹
yield هم میتواند مقدار برگرداندمیتوانیم هم مقدار دریافت کنیم و هم مقدار تولید کنیم:
def calculator():
number = yield
result = number * 2
yield result
حالا:
calc = calculator()
next(calc)
result = calc.send(5)
print(result)
خروجی:
10
در اینجا:
calc.send(5)
مقدار
5 را به Generator میفرستد و Generator به yield result میرسد.⚠️ نکته مهم
قبل از اولین
send() معمولاً باید Generator را با:next(generator)
تا اولین
yield جلو ببریم.این کار Generator را آماده دریافت مقدار میکند.
🧠 خلاصه
yield → تولید مقدار و توقف موقت
next() → ادامه Generator تا yield بعدی
send(value) → ارسال مقدار به yield
Generator فقط یک تولیدکننده ساده نیست؛ میتواند بین کد بیرونی و خودش داده رد و بدل کند.
🎯 تمرین
Generator زیر را کامل کن تا عدد دریافتشده را دو برابر کند:
def double():
# code
generator = double()
next(generator)
result = generator.send(7)
print(result)
خروجی مورد انتظار:
14
✅ پاسخ کامل تمرین
def double():
number = yield
result = number * 2
yield result
generator = double()
next(generator)
result = generator.send(7)
print(result)
خروجی:
14
در اینجا ابتدا:
next(generator)
Generator را تا اولین
yield جلو میبرد.سپس:
generator.send(7)
عدد
7 را به این قسمت میرساند:number = yield
بنابراین:
number = 7
7 * 2 = 14
و در نهایت:
yield result
مقدار
14 را تولید میکند.پست ۱۱۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۱۹
مدیریت پایان Generator با
در پستهای قبل با
حالا میخواهیم ببینیم اگر یک Generator با
🔹 استفاده از
یک Generator میتواند هم
حالا:
خروجی:
وقتی
🔹
در Generator میتوانیم بنویسیم:
اما این مقدار مثل
اگر با
خروجی:
🔹 چرا
حلقه
بنابراین:
فقط مقادیری را که با
🔹 تفاوت
مثلاً:
ترتیب اتفاقها:
⚠️ نکته مهم
در Generator:
در نهایت باعث ایجاد
قرار میگیرد.
این موضوع بیشتر در پیادهسازیها و کار با Generatorها به شکل مستقیم اهمیت دارد.
🧠 خلاصه
🎯 تمرین
Generator زیر را طوری کامل کن که:
عددهای
سپس با
مقدار
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در اینجا:
اولین مقدار را تولید میکند.
سپس:
دومین مقدار را تولید میکند.
در نهایت:
Generator را تمام میکند و
پست ۱۱۹ از دوره جامع Python 🐍
مدیریت پایان Generator با
returnدر پستهای قبل با
yield، next() و send() آشنا شدیم.حالا میخواهیم ببینیم اگر یک Generator با
return تمام شود، چه اتفاقی میافتد.🔹 استفاده از
return در Generatorیک Generator میتواند هم
yield داشته باشد و هم return:def numbers():
yield 1
yield 2
return
حالا:
for number in numbers():
print(number)
خروجی:
1
2
وقتی
return اجرا شود، Generator تمام میشود.🔹
return میتواند یک مقدار هم داشته باشددر Generator میتوانیم بنویسیم:
def numbers():
yield 1
yield 2
return "Done"
اما این مقدار مثل
yield مستقیماً توسط for چاپ نمیشود.اگر با
next() ادامه دهیم، مقدار بازگشتی داخل StopIteration قرار میگیرد:generator = numbers()
print(next(generator))
print(next(generator))
try:
next(generator)
except StopIteration as error:
print(error.value)
خروجی:
1
2
Done
🔹 چرا
for مقدار return را نشان نمیدهد؟حلقه
for وقتی به StopIteration برسد، آن را برای پایان پیمایش مدیریت میکند.بنابراین:
for number in numbers():
print(number)
فقط مقادیری را که با
yield تولید شدهاند دریافت میکند.🔹 تفاوت
yield و returnyield → یک مقدار تولید میکند و Generator را موقتاً متوقف میکند
return → Generator را کاملاً تمام میکند
مثلاً:
def example():
yield 10
yield 20
return "Finished"
ترتیب اتفاقها:
yield 10
↓
yield 20
↓
return "Finished"
↓
StopIteration
⚠️ نکته مهم
در Generator:
return value
در نهایت باعث ایجاد
StopIteration میشود و مقدار value در:StopIteration.value
قرار میگیرد.
این موضوع بیشتر در پیادهسازیها و کار با Generatorها به شکل مستقیم اهمیت دارد.
🧠 خلاصه
yield → تولید مقدار
return → پایان Generator
StopIteration → اعلام پایان
🎯 تمرین
Generator زیر را طوری کامل کن که:
عددهای
1 و 2 را تولید کند.سپس با
return پیام "Finished" را برگرداند.مقدار
return را با StopIteration.value دریافت کن.def process():
# code
generator = process()
print(next(generator))
print(next(generator))
try:
next(generator)
except StopIteration as error:
print(error.value)
خروجی مورد انتظار:
1
2
Finished
✅ پاسخ کامل تمرین
def process():
yield 1
yield 2
return "Finished"
generator = process()
print(next(generator))
print(next(generator))
try:
next(generator)
except StopIteration as error:
print(error.value)
خروجی:
1
2
Finished
در اینجا:
yield 1
اولین مقدار را تولید میکند.
سپس:
yield 2
دومین مقدار را تولید میکند.
در نهایت:
return "Finished"
Generator را تمام میکند و
"Finished" داخل StopIteration.value قرار میگیرد.پست ۱۱۹ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۲۰
مدیریت منابع با
تا اینجا با چند Magic Method برای کنترل رفتار اشیاء آشنا شدیم.
حالا به یکی از کاربردیترین Protocolهای Python میرسیم:
Context Manager
Context Manager برای مدیریت منابعی مثل فایلها، اتصالها و منابع موقتی استفاده میشود.
معمولاً با
🔹
مثلاً برای کار با فایل:
وقتی بلوک
🔹
یک کلاس میتواند با این دو متد به یک Context Manager تبدیل شود:
حالا:
خروجی:
🔹 ترتیب اجرا
وقتی مینویسیم:
تقریباً این اتفاق میافتد:
یعنی:
🔹
میتوانیم مقدار برگشتی
خروجی:
چون مقدار برگشتی
🔹 مدیریت خطا با
اگر خطایی رخ ندهد:
اگر داخل
مثلاً:
در
🔹 جلوگیری از انتشار خطا
مقدار برگشتی
اگر
برگرداند، Python خطایی را که داخل
مثلاً:
خروجی:
چون:
باعث میشود خطا دوباره به بیرون از
⚠️ اگر
اگر
پس بهطور خلاصه:
این رفتار باید آگاهانه استفاده شود؛ چون سرکوب کردن یک خطای واقعی ممکن است باعث شود مشکل اصلی دیده نشود.
🧠 خلاصه
ترتیب کلی:
اگر خطایی رخ دهد، اطلاعات آن به
🎯 تمرین
کلاس
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
در اینجا با ورود به
اجرا میشود و پیام شروع را چاپ میکند.
سپس کد داخل
و در پایان:
اجرا شده و پیام پایان را چاپ میکند.
پست ۱۲۰ از دوره جامع Python 🐍
مدیریت منابع با
__enter__ و __exit__تا اینجا با چند Magic Method برای کنترل رفتار اشیاء آشنا شدیم.
حالا به یکی از کاربردیترین Protocolهای Python میرسیم:
Context Manager
Context Manager برای مدیریت منابعی مثل فایلها، اتصالها و منابع موقتی استفاده میشود.
معمولاً با
with از آن استفاده میکنیم.🔹
with چیست؟مثلاً برای کار با فایل:
with open("data.txt", "r") as file:
content = file.read()وقتی بلوک
with تمام شود، Python فایل را مدیریت و در صورت نیاز میبندد.🔹
__enter__ و __exit__یک کلاس میتواند با این دو متد به یک Context Manager تبدیل شود:
class MyContext:
def __enter__(self):
print("Start")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("End")
حالا:
with MyContext():
print("Inside")
خروجی:
Start
Inside
End
🔹 ترتیب اجرا
وقتی مینویسیم:
with MyContext():
print("Inside")
تقریباً این اتفاق میافتد:
__enter__()
↓
کد داخل with
↓
__exit__()
یعنی:
__enter__ → ورود به Context
__exit__ → خروج از Context
🔹
return self در __enter__میتوانیم مقدار برگشتی
__enter__ را بعد از as دریافت کنیم:class MyContext:
def __enter__(self):
print("Start")
return "Hello"
def __exit__(self, exc_type, exc_value, traceback):
print("End")
with MyContext() as value:
print(value)
خروجی:
Start
Hello
End
چون مقدار برگشتی
__enter__ داخل value قرار میگیرد.🔹 مدیریت خطا با
__exit____exit__ اطلاعات مربوط به خطای احتمالی را دریافت میکند:def __exit__(self, exc_type, exc_value, traceback):
...
اگر خطایی رخ ندهد:
exc_type → None
exc_value → None
traceback → None
اگر داخل
with خطایی رخ دهد، اطلاعات خطا به __exit__ ارسال میشود.مثلاً:
class MyContext:
def __enter__(self):
print("Start")
def __exit__(self, exc_type, exc_value, traceback):
print("Error:", exc_value)
with MyContext():
print(10 / 0)
در
__exit__ اطلاعات خطای تقسیم بر صفر دریافت میشود.🔹 جلوگیری از انتشار خطا
مقدار برگشتی
__exit__ اهمیت دارد.اگر
__exit__ مقدار:True
برگرداند، Python خطایی را که داخل
with رخ داده سرکوب میکند.مثلاً:
class MyContext:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Error:", exc_value)
return True
with MyContext():
print(10 / 0)
print("Program continues")
خروجی:
Error: division by zero
Program continues
چون:
return True
باعث میشود خطا دوباره به بیرون از
with منتقل نشود.⚠️ اگر
True برنگردانیماگر
__exit__ مقدار False یا None برگرداند، خطا سرکوب نمیشود و به بیرون منتقل خواهد شد.پس بهطور خلاصه:
return True → خطا سرکوب میشود
return False/None → خطا ادامه پیدا میکند
این رفتار باید آگاهانه استفاده شود؛ چون سرکوب کردن یک خطای واقعی ممکن است باعث شود مشکل اصلی دیده نشود.
🧠 خلاصه
with → استفاده از Context Manager
__enter__ → هنگام ورود
__exit__ → هنگام خروج
as → دریافت مقدار برگشتی __enter__
__exit__:
True → سرکوب خطا
False/None → انتشار خطا
ترتیب کلی:
with
↓
__enter__()
↓
کد داخل with
↓
__exit__()
اگر خطایی رخ دهد، اطلاعات آن به
__exit__ ارسال میشود.🎯 تمرین
کلاس
Timer را طوری کامل کن که هنگام ورود و خروج پیام مناسب چاپ کند:class Timer:
def __enter__(self):
# code
def __exit__(self, exc_type, exc_value, traceback):
# code
with Timer():
print("Working...")
خروجی مورد انتظار:
Timer started
Working...
Timer finished
✅ پاسخ کامل تمرین
class Timer:
def __enter__(self):
print("Timer started")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Timer finished")
with Timer():
print("Working...")
خروجی:
Timer started
Working...
Timer finished
در اینجا با ورود به
with:__enter__()
اجرا میشود و پیام شروع را چاپ میکند.
سپس کد داخل
with اجرا میشود:print("Working...")و در پایان:
__exit__()
اجرا شده و پیام پایان را چاپ میکند.
پست ۱۲۰ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۲۱
کنترل خطا در
در پست قبل دیدیم که
حالا میخواهیم دقیقتر ببینیم چطور میتوانیم خطا را بررسی کنیم و تصمیم بگیریم آن را سرکوب کنیم یا نه.
🔹 بررسی نوع خطا
سه پارامتر
اگر داخل
مثلاً:
در اینجا:
🔹 تشخیص یک خطای خاص
میتوانیم بررسی کنیم خطا از نوع مشخصی است یا نه:
حالا:
خروجی:
چون
🔹 فقط خطای موردنظر را مدیریت کنیم
بهتر است هر خطایی را بدون بررسی سرکوب نکنیم.
مثلاً:
در این حالت:
این روش کنترل بیشتری روی خطاها میدهد.
⚠️ نکته مهم
اگر هیچ خطایی رخ ندهد:
همگی
بنابراین میتوانیم ابتدا بررسی کنیم آیا خطایی وجود دارد یا نه:
🧠 خلاصه
🎯 تمرین
کلاس زیر را طوری کامل کن که فقط
خروجی مورد انتظار:
پست ۱۲۱ از دوره جامع Python 🐍
کنترل خطا در
Context Manager با __exit__در پست قبل دیدیم که
__exit__ هنگام خروج از with اجرا میشود.حالا میخواهیم دقیقتر ببینیم چطور میتوانیم خطا را بررسی کنیم و تصمیم بگیریم آن را سرکوب کنیم یا نه.
🔹 بررسی نوع خطا
سه پارامتر
__exit__ اطلاعات خطا را دریافت میکنند:class MyContext:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
print(exc_type)
print(exc_value)
print(traceback)
اگر داخل
with خطایی رخ دهد، این مقادیر اطلاعات مربوط به آن خطا را در اختیارمان قرار میدهند.مثلاً:
with MyContext():
print(10 / 0)
در اینجا:
exc_type → نوع خطا
exc_value → اطلاعات خطا
traceback → اطلاعات محل وقوع خطا
🔹 تشخیص یک خطای خاص
میتوانیم بررسی کنیم خطا از نوع مشخصی است یا نه:
class MyContext:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is ZeroDivisionError:
print("Cannot divide by zero")
return True
حالا:
with MyContext():
print(10 / 0)
print("Program continues")
خروجی:
Cannot divide by zero
Program continues
چون
__exit__ مقدار True برگرداند، خطای ZeroDivisionError سرکوب شد.🔹 فقط خطای موردنظر را مدیریت کنیم
بهتر است هر خطایی را بدون بررسی سرکوب نکنیم.
مثلاً:
class MyContext:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is ZeroDivisionError:
print("Division by zero is not allowed")
return True
return False
در این حالت:
ZeroDivisionError → مدیریت و سرکوب میشود
سایر خطاها → به بیرون منتقل میشوند
این روش کنترل بیشتری روی خطاها میدهد.
⚠️ نکته مهم
اگر هیچ خطایی رخ ندهد:
exc_type
exc_value
traceback
همگی
None هستند.بنابراین میتوانیم ابتدا بررسی کنیم آیا خطایی وجود دارد یا نه:
if exc_type is not None:
print("An error occurred")
🧠 خلاصه
exc_type → نوع Exception
exc_value → مقدار/پیام Exception
traceback → اطلاعات مسیر خطا
return True
→ خطا سرکوب میشود
return False / None
→ خطا ادامه پیدا میکند
🎯 تمرین
کلاس زیر را طوری کامل کن که فقط
ValueError را مدیریت کند:class SafeContext:
def __enter__(self):
print("Start")
def __exit__(self, exc_type, exc_value, traceback):
# اگر ValueError بود:
# پیام "ValueError handled" را چاپ کن
# و خطا را سرکوب کن
# سایر خطاها باید ادامه پیدا کنند
with SafeContext():
raise ValueError("Invalid value")
print("Program continues")
خروجی مورد انتظار:
Start
ValueError handled
Program continues
پست ۱۲۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۲۲
ساخت Context Manager با
تا اینجا Context Manager را با
اما Python راه سادهتری هم دارد:
در بسیاری از موارد لازم نیست یک کلاس کامل بنویسیم.
🔹
از
حالا میتوانیم از آن با
خروجی:
🔹
در اینجا:
کد قبل از
کد بعد از
یعنی:
🔹 ارسال مقدار به
میتوانیم از
حالا:
خروجی:
مقدار
🔹 چرا
برای Context Managerهای ساده، این روش معمولاً خواناتر از نوشتن:
است.
مثلاً:
استفاده:
⚠️ نکته مهم
بخش قبل از
🧠 خلاصه
🎯 تمرین
یک Context Manager به نام
شروع کد:
پست ۱۲۲ از دوره جامع Python 🐍
ساخت Context Manager با
contextlibتا اینجا Context Manager را با
__enter__ و __exit__ ساختیم.اما Python راه سادهتری هم دارد:
contextlib
در بسیاری از موارد لازم نیست یک کلاس کامل بنویسیم.
🔹
@contextmanagerاز
contextmanager برای ساخت Context Manager با یک تابع استفاده میکنیم:from contextlib import contextmanager
@contextmanager
def my_context():
print("Start")
yield
print("End")
حالا میتوانیم از آن با
with استفاده کنیم:with my_context():
print("Inside")
خروجی:
Start
Inside
End
🔹
yield چه نقشی دارد؟در اینجا:
@contextmanager
def my_context():
print("Start")
yield
print("End")
کد قبل از
yield هنگام ورود به with اجرا میشود.کد بعد از
yield هنگام خروج از with اجرا میشود.یعنی:
ورود به with
↓
کد قبل از yield
↓
yield
↓
کد داخل with
↓
کد بعد از yield
🔹 ارسال مقدار به
withمیتوانیم از
yield یک مقدار هم برگردانیم:from contextlib import contextmanager
@contextmanager
def get_message():
print("Start")
yield "Hello Python"
print("End")
حالا:
with get_message() as message:
print(message)
خروجی:
Start
Hello Python
End
مقدار
yield داخل message قرار میگیرد.🔹 چرا
contextlib مفید است؟برای Context Managerهای ساده، این روش معمولاً خواناتر از نوشتن:
__enter__()
__exit__()
است.
مثلاً:
@contextmanager
def database_connection():
print("Connect")
yield
print("Disconnect")
استفاده:
with database_connection():
print("Working with database")
⚠️ نکته مهم
@contextmanager از ما میخواهد تابع حداقل یک yield داشته باشد.بخش قبل از
yield نقش ورود را دارد و بخش بعد از آن نقش خروج را.🧠 خلاصه
@contextmanager → ساخت Context Manager با تابع
yield → نقطه انتقال کنترل به کد داخل with
قبل از yield → ورود
بعد از yield → خروج
🎯 تمرین
یک Context Manager به نام
open_session بساز که خروجی زیر را ایجاد کند:Session started
Working...
Session closed
شروع کد:
from contextlib import contextmanager
@contextmanager
def open_session():
# code
with open_session():
print("Working...")
پست ۱۲۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۲۳
مدیریت خطا در
در پست قبل با
حالا میخواهیم ببینیم اگر داخل
🔹
میتوانیم
حالا:
خروجی:
🔹 چرا
وقتی برنامه به:
میرسد، کنترل موقتاً به کد داخل
اگر آن کد خطایی ایجاد کند:
خطا به بخش
پس:
🔹 اگر خطا را مدیریت نکنیم چه میشود؟
مثلاً:
اگر بنویسیم:
بخش
اما
🔹
یکی از کاربردهای مهم Context Manager این است که عملیات پایانی حتماً انجام شود.
برای این کار
حتی اگر داخل
باز هم:
سپس خطا به بیرون منتقل میشود.
⚠️ تفاوت مهم
مثلاً:
ممکن است از
🧠 خلاصه
در Context Managerها معمولاً:
برای cleanup بسیار مهم است.
🎯 تمرین
Context Manager زیر را کامل کن تا:
پیام
کد داخل
حتی اگر خطایی رخ داد،
خروجی مورد انتظار:
✅ پاسخ کامل تمرین
خروجی:
اما یک نکته مهم وجود دارد:
با وجود اینکه
همچنان به بیرون منتقل میشود؛ چون در این مثال
پس
در این مثال:
حتی هنگام رخ دادن خطا نیز اجرا میشود.
پست ۱۲۳ از دوره جامع Python 🐍
مدیریت خطا در
contextmanagerدر پست قبل با
@contextmanager آشنا شدیم.حالا میخواهیم ببینیم اگر داخل
with خطایی رخ دهد، چگونه میتوانیم آن را مدیریت کنیم.🔹
try و except داخل Context Managerمیتوانیم
try و except را اطراف yield قرار دهیم:from contextlib import contextmanager
@contextmanager
def my_context():
print("Start")
try:
yield
except ValueError:
print("ValueError handled")
print("End")
حالا:
with my_context():
print("Inside")
raise ValueError("Invalid value")
خروجی:
Start
Inside
ValueError handled
End
🔹 چرا
except بعد از yield قرار دارد؟وقتی برنامه به:
yield
میرسد، کنترل موقتاً به کد داخل
with منتقل میشود.اگر آن کد خطایی ایجاد کند:
raise ValueError("Invalid value")خطا به بخش
try در Context Manager برمیگردد.پس:
__enter__ / شروع
↓
try
↓
yield
↓
کد داخل with
↓
Exception
↓
except
↓
ادامه Context Manager
🔹 اگر خطا را مدیریت نکنیم چه میشود؟
مثلاً:
from contextlib import contextmanager
@contextmanager
def my_context():
print("Start")
try:
yield
finally:
print("End")
اگر بنویسیم:
with my_context():
raise ValueError("Something went wrong")
بخش
finally اجرا میشود:Start
End
اما
ValueError همچنان به بیرون منتقل میشود.🔹
finally برای Cleanupیکی از کاربردهای مهم Context Manager این است که عملیات پایانی حتماً انجام شود.
برای این کار
finally بسیار مهم است:from contextlib import contextmanager
@contextmanager
def resource():
print("Resource opened")
try:
yield
finally:
print("Resource closed")
حتی اگر داخل
with خطا رخ دهد:with resource():
print("Working...")
raise ValueError("Error")
باز هم:
Resource opened
Working...
Resource closed
سپس خطا به بیرون منتقل میشود.
⚠️ تفاوت مهم
except و finallyexcept → برای مدیریت یک خطا
finally → برای اجرای قطعی عملیات پایانی
مثلاً:
try:
...
except ValueError:
...
finally:
...
ممکن است از
except برای مدیریت خطا و از finally برای آزاد کردن منابع استفاده کنیم.🧠 خلاصه
@contextmanager
↓
yield
↓
کد داخل with
↓
خطا؟
↙️ ↘️
بله خیر
↓ ↓
except finally
↓
cleanup
در Context Managerها معمولاً:
try/finally
برای cleanup بسیار مهم است.
🎯 تمرین
Context Manager زیر را کامل کن تا:
پیام
Start هنگام ورود چاپ شود.کد داخل
with اجرا شود.حتی اگر خطایی رخ داد،
End حتماً چاپ شود.from contextlib import contextmanager
@contextmanager
def my_context():
# code
with my_context():
print("Working...")
raise ValueError("Something went wrong")
خروجی مورد انتظار:
Start
Working...
End
✅ پاسخ کامل تمرین
from contextlib import contextmanager
@contextmanager
def my_context():
print("Start")
try:
yield
finally:
print("End")
with my_context():
print("Working...")
raise ValueError("Something went wrong")
خروجی:
Start
Working...
End
اما یک نکته مهم وجود دارد:
با وجود اینکه
End چاپ میشود، خطای:ValueError: Something went wrong
همچنان به بیرون منتقل میشود؛ چون در این مثال
except نداریم و خطا را سرکوب نکردهایم.پس
finally باعث مدیریت یا حذف خطا نمیشود؛ وظیفه آن این است که کد پایانی را اجرا کند.در این مثال:
finally:
print("End")
حتی هنگام رخ دادن خطا نیز اجرا میشود.
پست ۱۲۳ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۲۴
Context Manager آماده در Python |
تا اینجا یاد گرفتیم چطور Context Manager خودمان را با
اما گاهی یک شیء داریم که متد
برای این حالت میتوانیم از
🔹
مثلاً:
خروجی:
🔹 چرا
اجرا شود.
یعنی تقریباً:
این موضوع برای منابعی که باید بعد از استفاده بسته یا آزاد شوند مفید است.
🔹 دریافت شیء با
میتوانیم شیء را مستقیماً با
در اینجا:
است.
⚠️ نکته مهم
دارند.
اگر شیء چنین متدی نداشته باشد، استفاده از
🧠 خلاصه
پس
🎯 تمرین
کد زیر را کامل کن:
بدون اجرا، خروجی را حدس بزن.
پست ۱۲۴ از دوره جامع Python 🐍
Context Manager آماده در Python |
closing()تا اینجا یاد گرفتیم چطور Context Manager خودمان را با
__enter__، __exit__ و @contextmanager بسازیم.اما گاهی یک شیء داریم که متد
close() دارد، ولی خودش Context Manager نیست.برای این حالت میتوانیم از
closing() در contextlib استفاده کنیم.🔹
closing() چیست؟closing() باعث میشود در پایان بلوک with، متد close() شیء بهصورت خودکار اجرا شود.مثلاً:
from contextlib import closing
class Resource:
def open(self):
print("Resource opened")
def close(self):
print("Resource closed")
resource = Resource()
with closing(resource):
resource.open()
print("Working...")
خروجی:
Resource opened
Working...
Resource closed
🔹 چرا
close() اجرا شد؟closing() در پایان with تضمین میکند که:resource.close()
اجرا شود.
یعنی تقریباً:
with
↓
کد اصلی
↓
close()
این موضوع برای منابعی که باید بعد از استفاده بسته یا آزاد شوند مفید است.
🔹 دریافت شیء با
asمیتوانیم شیء را مستقیماً با
as دریافت کنیم:from contextlib import closing
resource = Resource()
with closing(resource) as item:
item.open()
در اینجا:
item → همان resource
است.
⚠️ نکته مهم
closing() برای اشیایی مناسب است که متد:close()
دارند.
اگر شیء چنین متدی نداشته باشد، استفاده از
closing() مناسب نیست.🧠 خلاصه
closing(object)
↓
with
↓
کد داخل with
↓
object.close()
پس
closing() یک راه ساده برای استفاده از اشیای دارای close() بهعنوان Context Manager است.🎯 تمرین
کد زیر را کامل کن:
from contextlib import closing
class Connection:
def connect(self):
print("Connected")
def close(self):
print("Connection closed")
connection = Connection()
with closing(connection):
connection.connect()
print("Working...")
بدون اجرا، خروجی را حدس بزن.
پست ۱۲۴ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۲۵
مدیریت چند Context Manager با
گاهی لازم است چند منبع را همزمان مدیریت کنیم.
مثلاً چند فایل، اتصال یا Resource مختلف.
برای این کار
🔹 مشکل چیست؟
میتوانیم چند
اما اگر تعداد Context Managerها پویا باشد، این روش مناسب نیست.
🔹
با
در پایان بلوک
🔹
برای وارد کردن یک Resource به
استفاده میکنیم.
مثلاً:
هر بار که
مقدار برگشتی
پس:
و هنگام خروج:
یعنی Resourceها به ترتیب ورود وارد میشوند، اما به ترتیب معکوس خارج میشوند.
🔹 چرا
فرض کنیم تعداد فایلها مشخص نیست:
میتوانیم همه را با یک حلقه باز کنیم:
اینجا تعداد فایلها میتواند تغییر کند، بدون اینکه تعداد
🔹 ترتیب خروج
اگر:
باشد، هنگام خروج:
این رفتار شبیه ساختار Stack است:
🔹 اضافه کردن Cleanup
خروجی:
تابع
⚠️ نکته مهم
تعداد Context Managerها از قبل مشخص نیست.
منابع بهصورت پویا ایجاد میشوند.
چند Resource باید با هم مدیریت شوند.
Cleanup باید حتی هنگام رخ دادن Exception انجام شود.
🧠 خلاصه
و ترتیب خروج:
🎯 تمرین
کد زیر را کامل کن تا سه Resource را با
خروجی مورد انتظار:
پست ۱۲۵ از دوره جامع Python 🐍
مدیریت چند Context Manager با
ExitStackگاهی لازم است چند منبع را همزمان مدیریت کنیم.
مثلاً چند فایل، اتصال یا Resource مختلف.
برای این کار
contextlib ابزاری به نام ExitStack دارد.🔹 مشکل چیست؟
میتوانیم چند
with را داخل هم بنویسیم:with open("file1.txt") as file1:
with open("file2.txt") as file2:
print("Working...")اما اگر تعداد Context Managerها پویا باشد، این روش مناسب نیست.
🔹
ExitStackبا
ExitStack میتوانیم Context Managerها را بهصورت پویا مدیریت کنیم:from contextlib import ExitStack
with ExitStack() as stack:
file1 = stack.enter_context(open("file1.txt"))
file2 = stack.enter_context(open("file2.txt"))
print("Working...")
در پایان بلوک
with، منابع مدیریتشده توسط stack بهصورت خودکار خارج میشوند.🔹
enter_context() چگونه کار میکند؟برای وارد کردن یک Resource به
ExitStack از:stack.enter_context(resource)
استفاده میکنیم.
مثلاً:
with ExitStack() as stack:
resource_a = stack.enter_context(Resource("A"))
resource_b = stack.enter_context(Resource("B"))
resource_c = stack.enter_context(Resource("C"))
print("Working...")
هر بار که
enter_context() اجرا میشود:__enter__() همان Resource اجرا میشود.مقدار برگشتی
__enter__() برگردانده میشود.__exit__() برای زمان خروج توسط ExitStack ثبت میشود.پس:
Resource("A")
↓
A.__enter__()
Resource("B")
↓
B.__enter__()
Resource("C")
↓
C.__enter__()و هنگام خروج:
C.__exit__()
↓
B.__exit__()
↓
A.__exit__()
یعنی Resourceها به ترتیب ورود وارد میشوند، اما به ترتیب معکوس خارج میشوند.
🔹 چرا
ExitStack مفید است؟فرض کنیم تعداد فایلها مشخص نیست:
files = ["a.txt", "b.txt", "c.txt"]
میتوانیم همه را با یک حلقه باز کنیم:
from contextlib import ExitStack
files = ["a.txt", "b.txt", "c.txt"]
with ExitStack() as stack:
opened_files = []
for filename in files:
file = stack.enter_context(open(filename))
opened_files.append(file)
print(len(opened_files))
اینجا تعداد فایلها میتواند تغییر کند، بدون اینکه تعداد
withها را از قبل مشخص کنیم.🔹 ترتیب خروج
ExitStack Context Managerها را به ترتیب معکوس ورود خارج میکند.اگر:
A → ورود
B → ورود
C → ورود
باشد، هنگام خروج:
C → خروج
B → خروج
A → خروج
این رفتار شبیه ساختار Stack است:
Last In → First Out
🔹 اضافه کردن Cleanup
ExitStack حتی میتواند یک تابع cleanup را هم ثبت کند:from contextlib import ExitStack
def cleanup():
print("Cleanup done")
with ExitStack() as stack:
stack.callback(cleanup)
print("Working...")
خروجی:
Working...
Cleanup done
تابع
cleanup هنگام خروج از with اجرا میشود.⚠️ نکته مهم
ExitStack زمانی بسیار مفید است که:تعداد Context Managerها از قبل مشخص نیست.
منابع بهصورت پویا ایجاد میشوند.
چند Resource باید با هم مدیریت شوند.
Cleanup باید حتی هنگام رخ دادن Exception انجام شود.
🧠 خلاصه
ExitStack
↓
مدیریت چند Context Manager
↓
enter_context()
↓
__enter__()
↓
مدیریت Resource
↓
__exit__()
و ترتیب خروج:
A → B → C
↓
C → B → A
🎯 تمرین
کد زیر را کامل کن تا سه Resource را با
ExitStack مدیریت کند:from contextlib import ExitStack
class Resource:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f"{self.name} started")
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f"{self.name} closed")
with ExitStack() as stack:
# سه Resource با نامهای A ،B و C را وارد Stack کن
# سپس عبارت زیر را چاپ کن:
print("Working...")
خروجی مورد انتظار:
A started
B started
C started
Working...
C closed
B closed
A closed
پست ۱۲۵ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۲۶
مدیریت موقت Context Manager با
گاهی تعداد Resourceها مشخص نیست یا یک Resource فقط در شرایط خاصی لازم است.
🔹 ورود شرطی Resource
اگر
🔹 اضافه کردن چند Resource
Resourceها هنگام خروج، برعکس ترتیب ورود cleanup میشوند:
همچنین میتوانیم تعداد متغیری Resource را با حلقه مدیریت کنیم:
🔹 اگر هنگام ورود Resource خطا رخ دهد؟
ممکن است
حالا:
خروجی:
چون
اما
🔹 مدیریت خطا با
میتوانیم خطای ورود را مدیریت کنیم:
خروجی:
اینجا:
⚠️ نکته مهم
اگر نخواهیم خطا را متوقف کنیم، آن را در
اگر بخواهیم خطا دوباره به بیرون منتقل شود:
🧠 خلاصه
مزیت
🎯 تمرین
کد را کامل کن:
خروجی مورد انتظار:
پست ۱۲۶ از دوره جامع Python 🐍
مدیریت موقت Context Manager با
ExitStackگاهی تعداد Resourceها مشخص نیست یا یک Resource فقط در شرایط خاصی لازم است.
ExitStack اجازه میدهد Resourceها را بهصورت پویا و شرطی وارد مدیریت کنیم.🔹 ورود شرطی Resource
from contextlib import ExitStack
use_cache = True
with ExitStack() as stack:
if use_cache:
cache = stack.enter_context(Resource("Cache"))
print("Application running")
اگر
use_cache برابر False باشد، Resource اصلاً وارد Stack نمیشود.🔹 اضافه کردن چند Resource
with ExitStack() as stack:
if condition_1:
resource_a = stack.enter_context(Resource("A"))
if condition_2:
resource_b = stack.enter_context(Resource("B"))
print("Working...")
Resourceها هنگام خروج، برعکس ترتیب ورود cleanup میشوند:
A → B → C
↓
C → B → A
همچنین میتوانیم تعداد متغیری Resource را با حلقه مدیریت کنیم:
resources = ["A", "B", "C"]
with ExitStack() as stack:
for name in resources:
stack.enter_context(Resource(name))
print("All resources are active")
🔹 اگر هنگام ورود Resource خطا رخ دهد؟
ممکن است
__enter__() یک Resource خطا ایجاد کند:class Resource:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f"{self.name} started")
if self.name == "D":
raise RuntimeError("D failed")
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f"{self.name} closed")
حالا:
with ExitStack() as stack:
a = stack.enter_context(Resource("A"))
d = stack.enter_context(Resource("D"))
print("Working...")
خروجی:
A started
D started
RuntimeError: D failed
A closed
چون
A قبلاً با موفقیت وارد Stack شده، در پایان cleanup میشود.اما
D به دلیل خطا در __enter__() وارد Stack نشده است.🔹 مدیریت خطا با
try/exceptمیتوانیم خطای ورود را مدیریت کنیم:
with ExitStack() as stack:
try:
a = stack.enter_context(Resource("A"))
d = stack.enter_context(Resource("D"))
except RuntimeError as error:
print("Failed to enter resource:", error)
print("Working...")
خروجی:
A started
D started
Failed to enter resource: D failed
Working...
A closed
اینجا:
try/except → مدیریت خطا
ExitStack → مدیریت Resourceها
A همچنان توسط ExitStack cleanup میشود.⚠️ نکته مهم
اگر نخواهیم خطا را متوقف کنیم، آن را در
except مدیریت میکنیم.اگر بخواهیم خطا دوباره به بیرون منتقل شود:
except RuntimeError as error:
print(error)
raise
🧠 خلاصه
enter_context()
↓
__enter__()
↓
موفق → ثبت __exit__
خطا → try/except
↓
Cleanup Resourceهای قبلی
مزیت
ExitStack:مدیریت پویا، شرطی و امن چند Resource، حتی زمانی که هنگام ورود یکی از آنها خطا رخ دهد.
🎯 تمرین
کد را کامل کن:
from contextlib import ExitStack
with ExitStack() as stack:
try:
# Resource A را وارد کن
# Resource D را وارد کن
pass
except RuntimeError as error:
print("Failed to enter resource:", error)
print("Working...")
خروجی مورد انتظار:
A started
D started
Failed to enter resource: D failed
Working...
A closed
پست ۱۲۶ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۲۷
ساخت Context Manager با
تا اینجا Context Manager را با کلاس و متدهای
اما Python روش سادهتری هم دارد:
با
🔹 ساخت یک Context Manager ساده
حالا میتوانیم از آن با
خروجی:
🔹
در Context Manager ساختهشده با
کد قبل از
کد بعد از
یعنی:
🔹 استفاده از
میتوانیم از
حالا:
خروجی:
مقداری که
⚠️ نکته مهم
ساختار کلی:
بخش
🎯 تمرین
یک Context Manager به نام
قبل از
داخل
بعد از
پست ۱۲۷ از دوره جامع Python 🐍
ساخت Context Manager با
@contextmanagerتا اینجا Context Manager را با کلاس و متدهای
__enter__() و __exit__() ساختیم.اما Python روش سادهتری هم دارد:
from contextlib import contextmanager
با
@contextmanager میتوانیم یک تابع را به Context Manager تبدیل کنیم.🔹 ساخت یک Context Manager ساده
from contextlib import contextmanager
@contextmanager
def my_context():
print("Start")
yield
print("End")
حالا میتوانیم از آن با
with استفاده کنیم:with my_context():
print("Working...")
خروجی:
Start
Working...
End
🔹
yield چه نقشی دارد؟در Context Manager ساختهشده با
@contextmanager:@contextmanager
def my_context():
# قبل از with
yield
# بعد از with
کد قبل از
yield هنگام ورود اجرا میشود.کد بعد از
yield هنگام خروج اجرا میشود.یعنی:
__enter__
↓
قبل از yield
↓
کد داخل with
↓
بعد از yield
↓
__exit__
🔹 استفاده از
asمیتوانیم از
yield یک مقدار برگردانیم:from contextlib import contextmanager
@contextmanager
def resource():
print("Opening")
data = "Python"
yield data
print("Closing")
حالا:
with resource() as value:
print(value)
خروجی:
Opening
Python
Closing
مقداری که
yield میکند، داخل متغیر value قرار میگیرد.⚠️ نکته مهم
@contextmanager از ما میخواهد تابع شامل yield باشد.ساختار کلی:
@contextmanager
def my_context():
# setup
yield value
# cleanup
بخش
setup قبل از yield و بخش cleanup بعد از آن قرار میگیرد.🎯 تمرین
یک Context Manager به نام
database_connection بساز که:قبل از
yield چاپ کند:Connecting...
داخل
with این پیام چاپ شود:Query running...
بعد از
yield چاپ کند:Connection closed.
پست ۱۲۷ از دوره جامع Python 🐍