برخی خطاهای برنامهای که نوشتهایم در هنگام اجرا و با توجه به شرایط رخ میدهد. اگر ورودی نامناسبی از طرف کاربر وارد شود و یا برنامه نتواند یک فایل را باز کند، با یک نوع خطا که به آن استثنا میگوییم مواجه خواهیم شد. در این مقاله با مدیریت خطا در پایتون آشنا شده و در انتهای آن خواهیم توانست انواع استثنا در پایتون را مدیریت کنیم.
همانطور که میدانید در برنامه نویسی خطاهای مختلفی داریم. برخی خطاهای نوشتاری بوده و برخی خطاهای منطقی هستند. یک نوع دیگر از انواع خطا در برنامه نویسی، خطاهایی است که در هنگام اجرای برنامه رخ داده و موجب توقف آن میشوند. به اینگونه خطاها استثنا یا Exception هم گفته میشود.
فرض کنید در برنامه خود از کاربر خواستهاید تا نمره درس دانشجو را وارد کند. کاربر به جای وارد کردن عدد، از حروف فارسی یا انگلیسی استفاده میکند. اگر شما بخواهید روی این مقدار ورودی پردازشهایی انجام دهید (جمع، تفریق و تقسیم و …) قطعاً با خطا مواجه میشوید.
بهتر است در برنامه نویسی همواره سعی کنیم از خطاهایی که ممکن است رخ دهد جلوگیری کنیم. این کار به وسیله مدیریت خطا (مدیریت استثنا یا Exception Handling) انجام خواهد شد. در ادامه پس از بررسی مدیریت خطا در پایتون، به چگونگی مدیریت استثنا در پایتون خواهیم پرداخت.
فهرست محتوای آموزش
خطا در پایتون
در حالت کلی، پایتون برای مدیریت خطاهای پیشبینی نشده در برنامهها دو روش در نظر گرفته است. این دو راهحل عبارتاند از:
- مدیریت استثنا (Exception Handling): در این روش بخشهایی از برنامه که ممکن است باعث ایجاد خطا شود را مدیریت کرده و در صورت بروز خطا، کارهای جایگزین انجام خواهیم داد.
- تست توابع (Assertion): با استفاده از این روش، میتوان ورودی و خروجی یک تابع یا عملیات را بررسی کنیم. در صورتی که مشکلی در ورودی و خروجی مورد انتظار وجود داشت، تصمیماتی برای ادامه برنامه میگیریم.
ما در این مقاله به مورد اول میپردازیم. مورد دوم بیشتر برای مواقعی استفاده میشود که خروجی یک کار قابل پیشبینی است. یعنی ما میدانیم خروجی تابع توان دوم، همواره برابر است با ورودی به توان دو! اما در قسمتهایی که ممکن است خطا به خاطر اشتباه کاربر یا مشکلاتی سیستمی رخ دهد، با استفاده از مدیریت استثنا در پایتون میتوان عملکرد بهتری داشت.
استثنا در پایتون
هر خطایی که در حین اجرای کد پایتون رخ میهد شامل یک نام بوده و از نوعِ خاصی است. استثناها هم نوعی خطا هستند. در نتیجه همه آنها دارای اسم بوده که میتواند به ما در مدیریت بهتر خطاها کمک کند.
فرض کنید یک رشته متنی در اختیار داریم که میخواهیم آن را به عدد تبدیل کنیم. برای این کار از تابع int()
استفاده کرده و رشته را به عنوان ورودی به آن میدهیم. در صورتی که درون رشته فقط عدد وجود داشته باشد، عملیات با موفقیت انجام شده و خروجی ما یک عدد صحیح خواهد بود.
num = "2568"
print( int(num) )
# output: 2568
اما اگر درون رشته ما علاوه بر عدد، حروف انگلیسی نیز وجود داشته باشد، با خطا مواجه خواهیم شد.
num = "25sabz68"
print( int(num) )
در ابتدای خط آخر خطایی که رخ داد، نوع خطا (استثنا) مشخص شده است. خطای ValueError مواقعی رخ میدهد که ورودی تابع اشتباه باشد. در اینجا ما ورودی نادرستی به تابع int()
دادیم که باعث بروز این خطا شد.
Traceback (most recent call last): File ".\run.py", line 2, in <module> print( int(num) ) ValueError: invalid literal for int() with base 10: '25sabz68'
در زبان پایتون خطاها و استثناهای مختلفی وجود دارد که میتوانید تمام آنها را در جدول موجود در این صفحه بررسی کنید. برخی از پرتکرارترین استثناها عبارتاند از:
اسم خطا | شرح خطا |
---|---|
Exception | خانواده انواع استثناها |
ArithmeticError | کلاس خطا برای محاسبات عددی |
ZeroDivisionError | خطای خاص از تقسیم عدد بر صفر |
TypeError | ورودی تابع از نوع شئ قابلقبول نیست |
ValueError | مقدار آرگومان ورودی تابع اشتباه است |
مدیریت خطا در پایتون
برای درک بهتر شیوه مدیریت استثنا در پایتون، یک صورت مسئله را در نظر بگیرید.
برای این کار، ابتدا با دستور input()
یک ورودی از کاربر میگیریم. همزمان با دریافت ورودی، آن را به عدد تبدیل کرده و درون متغیر num نگه میداریم. در نهایت با استفاده از دستور print()
و عملگر توان (**
) توان دوم عدد را در خروجی چاپ خواهیم کرد.
num = int( input("Please enter the number: ") )
print( num**2 )
اگر قطعه کد بالا را اجرا کرده و عدد 5 را به عنوان ورودی به آن بدهیم، نتیجه دلخواهمان را خواهیم گرفت.
Please enter the number: 5 25
اگر با تابع input()
و ترفندهای آن آشنا نیستید، میتوانید آموزش گرفتن ورودی از کاربر در پایتون را ببینید.
اما اگر سهواً یا عمداً به جای عدد یک حرف انگلیسی وارد کرده یا ترکیبی از عدد و حروف را به عنوان ورودی به آن بدهیم، با خطای ValueError مواجه خواهیم شد.
Please enter the number: 7s Traceback (most recent call last): File ".\run.py", line 1, in <module> num = int( input("Please enter the number: ") ) ValueError: invalid literal for int() with base 10: '7s'
در ادامه میخواهیم از این خطا در پایتون جلوگیری کنیم. در اصل میخواهیم کاری کنیم که با وجود مشکل در ورودی، برنامه متوقف نشود. دو سناریو برای ادامه برنامه داریم:
- سناریو اول: یک پیغام خطا برای کاربر چاپ کرده و برنامه را به پایان برسانیم.
- سناریو دوم: با چاپ پیغام خطا، مجدداً از کاربر ورودی دریافت کرده و فرآیند را تکرار کنیم.
دستور try برای مدیریت استثنا در پایتون
به کمک دستور try:
میتوانیم یک بلوک مدیریتی داشته باشیم. اگر خطایی در این بلوک رخ دهد، برنامه متوقف نشده و به بلوک دیگر (بلوک except) خواهیم رفت.
دستور except برای مدیریت خطای رخ داده
با استفاده از دستور except:
یک بلوک ایجاد میکنیم. محتوای این بلوک فقط هنگامی اجرا میشوند که در بلوک قبلی (یعنی بلوک try) خطایی رخ داده باشد.
این دستور به دو شیوه تعریف میشود:
- حالت کلی
- حالت مخصوص یک استثنا
حالت کلی دستور except
در این حالت دستور را به صورت except:
نوشته و استفاده میکنیم. هر گونه خطایی که در بلوک try رخ دهد، برنامه وارد این بلوک خواهد شد. فرقی ندارد که خطای رخ داده از نوع ValueError بوده یا خطای مربوط به باز کردن فایل!
حالت خاص مدیریت استثنا با except
در این حالت، در جلوی کلمه کلیدی except و قبل از علامت دو نقطه (:
) نوع خطا را مشخص میکنیم. مثلاً بلوک except زیر در صورتی اجرا میشود که خطای رخ داده در try از نوع ValueError باشد.
except ValueError:
print("Value Error Occurred!")
بلوک finally در مدیریت خطای پایتون
یک بلوک دیگر هم در مدیریت استثنا در پایتون داریم. این بلوک که با کلمه کلیدی finally مشخص میشود، اختیاری بوده و میتوان از آن در یک بلوک try except
استفاده کرد.
این بلوک، پس از اجرای try یا except اجرا خواهد شد. یعنی چه خطا رخ داده باشد و چه نه، محتویات این بلوک اجرا خواهند شد.
یک بلوک مدیریت خطا در پایتون باید حتماً حاوی بخش try
و except
باشد؛ اما بلوک finally
میتواند به دلخواه وجود داشته یا نداشته باشد.
برنامه مدیریت شده مثالی که داشتیم، به صورت زیر خواهد شد.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
اگر بخواهیم از بلوک finally هم استفاده کرده و پایان برنامه را اعلام کنیم، میتوان کد را به شکل زیر تغییر داد.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
finally:
print("Ended!")
اگر این قطعه کد را اجرا کرده و ورودی درست به آن بدهیم، خروجی چیزی شبیه زیر خواهد شد:
Please enter the number: 15 225 Ended!
اگر به همین برنامه، یک ورودی اشتباه بدهیم، نتیجهای شبیه به زیر خواهیم داشت:
Please enter the number: 20sabzdanesh لطفا در ورودي فقط عدد وارد کنيد! Ended!
برای مثال اگر در کد بالا بخواهیم جلو خطاهای دیگری که از آنها اطلاع نداریم را بگیریم، میتوانیم از یک بلوک except کلی استفاده کنیم.
try:
num = int( input("Please enter the number: ") )
print( num**2 )
except ValueError:
print("لطفا در ورودي فقط عدد وارد کنيد!")
except:
print("يک خطاي غيرمنتظره رخ داده است!")
finally:
print("Ended!")
مثالی دیگر از مدیریت خطا در پایتون
از جمله موارد دیگری که میتواند هنگام برنامه نویسی باعث دردسر شود، کار با فایل است. به طور کلی کارهای سیستمی ممکن است موجب خطاهای پیشبینی نشده در برنامهی ما شوند. به همین دلیل بهتر است در هنگام انجام عملیاتهایی که مربوط به سیستم عامل است از تکنیکهای مدیریت خطا استفاده کنیم.
در مقالهای دیگر در مورد کار با فایل در پایتون صحبت کردهایم. باز کردن یک فایل ممکن است باعث ایجاد خطاهای ناخواستهای شود. مثلاً:
- فایل توسط برنامه دیگری در حال استفاده است و اجازه باز کردن آن را نداریم.
- فایل وجود ندارد. (مسیر اشتباه یا نام و فرمت اشتباه)
برای جلوگیری از خطا در هنگام باز کردن فایل، بهتر است از بلوکهای try except استفاده کنیم. در مثال زیر ابتدا تلاش کردهایم که یک فایل به نام file.txt
را باز کرده و متنی درون آن بنویسیم.
try:
f = open("file.txt")
f.write("Test from SabzDanesh.com")
except:
print("Something went wrong when writing to the file!")
finally:
f.close()
در جلسه بعدی آموزش پایتون با روشها و ترفندهای خواندن و نوشتن فایل آشنا میشویم.
مدیریت خطای پیشرفته در پایتون
بیایید دو مسئلهای که تا به اینجا مطرح کردیم را با هم ترکیب کنیم. یعنی در ابتدا سعی کنیم یک فایل را باز کرده و خط اول آن را بخوانیم. سپس محتویات آن خط را به عدد تبدیل کنیم. میخواهیم در این مثال، چند خطای ممکن را پیشبینی کرده و مدیریت کنیم.
کد زیر یک نتیجه مطلوب برای این کار خواهد بود.
try:
f = open('file.txt')
s = f.readline()
i = int( s.strip() )
except OSError:
print("We have OS error!")
except ValueError:
print("Could not convert data to an integer!")
except:
print("Unexpected error!")
در برنامه بالا به صورت زیر عمل کردهایم:
- خط اول تا چهارم: تلاش برای انجام عملیاتهای مورد نظر (باز کردن فایل، خواندن آن و تبدیل خط اول به عدد)
- خط پنجم و ششم: در صورتی که خطایی در هنگام باز کردن فایل رخ دهد، آن را مدیریت میکنیم.
- خط هفتم و هشتم: برای مدیریت خطای تبدیل رشته به عدد در نظر گرفته شده است.
- خط نهم و دهم: برای مدیریت خطای ناخواسته در پایتون این بلوک را نوشتهایم.
تولید استثنا در پایتون
هنگامی که در حال نوشتن یک تابع یا کلاس در برنامه خود هستیم، ممکن است لازم داشته باشیم تا در صورت وجود شرایطی خاص، یک استثنا ایجاد کنیم. این استثنا میتواند از استثناهای پیشفرض پایتون بوده و یا استثنایی باشد که خودمان آن را ایجاد کردهایم.
در هر حال، برای تولید خطا در پایتون میتوان از کلمه کلیدی raise
استفاده کرد. ساختار کلی این دستور به صورت زیر است.
raise [Exception [, args [, traceback]]]
اولین آرگومان آن ضروری و دو مورد دیگر اختیاری هستند.
- آرگومان اول (Exception) : نام یا نوع استثنا و خطایی است که میخواهیم اتفاق بیافتد.
- آرگومان دوم (args) : این مقدار به عنوان آرگومان ورودی exception صدا زده شده در نظر گرفته میشود.
- پارامتر سوم (traceback) : نشاندهنده یک پشته از خطاها و رویدادهای مربوط به خطای اتفاق افتاده است.
در قطعه کد زیر، اگر مقدار متغیر i
از 23 بیشتر بود، یک خطای ValueError تولید میکنیم.
if i > 23:
raise ValueError
جمعبندی: مدیریت خطا در پایتون
در این آموزش با مدیریت خطا در پایتون آشنا شدیم. مدیریت خطا یا مدیریت استثنا در پایتون بسیار ساده اما کاربردی است. ما میتوانیم با استفاده از یک ساختار بلوکی نسبت به مدیریت خطاهای پیشبینی نشده اقدام کنیم.
در ابتدا با دستور try
سعی میکنیم که کاری را انجام دهیم. در صورتی که مشکلی به وجود بیاید با استفاده از دستور except
آن را مدیریت کرده یا پیغام مناسبی را در خروجی نمایش میدهیم. در انتها به کمک finally
میتوان یک بلوک را در هر شرایطی اجرا کرد. یعنی چه بخش try با موفقیت اجرا شده و چه وارد except شده باشیم، بلوک finally اجرا خواهد شد.
همچنین یاد گرفتیم که میتوان چند بلوک except برای مدیریت خطاهای مختلف در برنامه تعریف کرد.
این آموزش بخشی از یک آموزش جامع و قدم به قدم در سبز دانش است: دوره رایگان آموزش پایتون
این آموزش برای همیشه رایگانه! میتونید با اشتراکگذاری لینک این صفحه از ما حمایت کنید یا با خرید یه فنجون نوشیدنی بهمون انرژی بدید!
میخوام یه نوشیدنی مهمونتون کنم
سلام من این کد را از chagpt گرفتم وقتی که می خوام خروجی بگیرم مینویسه
ولی به من خروجی نمیده
کدم هم این است
(کدها به دلیل طولانی بودن حذف شدند!)
لطفا بگید چه کاری بکنم
سلام
ما امکان خطایابی کدهاتون رو نداریم. (مخصوصاً اینکه برنامهتون نزدیک به صد خط هست!)
اما خطایی که میده یک خطای نوشتاری هست. یعنی در جایی که مشخص کرده، یک ساختار نامفهوم یا اشتباه در زبان پایتون استفاده کردید. در اینجا یه space بین نام متغیر وجود داره که باعث این خطا شده.
سلام . ممنون بابت توضیحات خوبتون
من یک برنامه نوشتم با کتابخانه tkinter :
و توی تابع stra گفتم که :
بعدش یک کاری رو انجام بده ولی این خطا رو میده :
میشه راهنماییم کنید دلیل این خطا چیه ؟؟؟ ممنون
سلام
در خطا که گفته متغیر f قبل از اینکه مقداردهی بشه فراخوانی شده. اگه در اجرای برنامه، اول تابع رو صدا میزنید، احتمالاً به همون دلیله، چون در بدنه تابع متغیر f تعریف نشده و اگه اینها در یه scope نباشن به نوعی دارید سراسری فراخوانیش میکنید که یکم ممکنه گیج بشید. پیشنهاد میکنم f رو در هر جا که نیازه به عنوان پارامتر به تابع بفرستید.
سلام و خسته نباشید ، من توی این قسمت کدم با ارور
IndexError
مواجه میشم ، باید چکار کنم؟
سلام
این خطا مربوط به اینه که در خط آخر (جایی که عناصر لیست رو فراخوانی میکنید) اندیسی در بازهی غیرمجاز میدید. مثلاً اگه لیست 5 تا عضو داره عددی غیر از 0 تا 4 (یا معادلهای منقیش) میدید.
حلقه رو واسه خودتون trace کنید تا ببینید کجای محاسباتتون اشتباه میشه.
سلام. بعد از دستور except باید دوباره دستور قبل از خطا تکرار بشه. که دوباره input اجرا بشه؟چطور میشه اینو داخل while آورد؟
یعنی مثلا تو این دستور اگر به جای عدد رشته دریافت شد و خروجی گفت خطا و یا اعلام کرد فقط عدد وارد کنید بعد except آیا باید دستور input تکرار بشه؟ یا با while میشه دستور کلی بهش داد؟ اگه بله چطور؟ while میاد داخل بلوک try؟
سلام
به طور کلی درست گفتید.
اگه بخواید با while چنین کاری کنید، معمولاً یک while True ایجاد میشه و در بلوک try اگه خط مورد نظر (مثلاً تبدیل به int) با موفقیت اجرا شد، در خطوط بعدش از حلقه خارج میشن. (با break)
اگه با این کلمات کلیدی آشنا نیستید جلسه حلقهها از دوره جامع رو ببینید.
ممنونم
سلام. الان این کد درست کار میکنه ولی اگر دو دفعه ورودی غیر عددی بگیره خطا میده. مشکلش کجاست؟ یعنی دو دفعه باید while بدم؟
سلام
پیشنهاد میکنم حتماً جلسه حلقه در پایتون رو ببینید. ما میتونیم از دستور break استفاده کنیم تا ازش خارج بشیم.
برای این کار، اول یه حلقه بینهایت (while True) بنویسید و داخل اون دو تا بلوک try except قرار بدید. داخل try ورودی رو بگیرید، تبدیل و break کنید. در except صرفاً پیامتون رو نمایش بدید.
اینطوری تا زمانی که به break نرسه (تبدیل به درستی انجام نشه و خطا داشته باشه) کدتون تکرار میشه.
سلام برای حل این مشکل می تونید از تابع بازگشتی استفاده کنید که برنامه این شکلی میشه:
سلام
البته بدنه این کد هم یکم مشکل (یا بهتره بگم بخش های اضافی) داره. در کل میشه از تابع بازگشتی هم استفاده کرد ولی با ساختار منظم و کوتاهتر از چیزی که شما پیشنهاد دادین.
درود و خسته نباشید
امکانش هست وقتی میخوایم استثنایی رو تولید کنیم واسش اسم جدید بذاریم؟؟ یعنی اگر هنگام وارد کردن عدد در ورودی، بزرگتر از 10 بود ارور بده به اسم isMax و اگر کوچکتر بود isMin و بعد در قسمت except بشه باهاش مدیریت کرد؟
منظورم این کدی که نوشتم هستش اما خطای TypeError میده
ممنون میشم راهنماییم کنید.
سلام
آره میتونید. ابتدا باید یک کلاس با نام استثنای مورد نظرتون (مثلاً isMax) ایجاد کنید. این کلاس از کلاس Exception ارثبری میکنه. برای ایجادش میتونید آموزش کلاس در پایتون رو مشاهده کنید.
بعدش به جای رشتههایی که جلوی raise و except گذاشتید، نام کلاس رو قرار بدید.
یه پیشنهاد: اسم کلاس استثنا رو طوری انتخاب کنید که مشخص باشه یک خطا یا exception هست. دو اسمی که گفتید بیشتر شکل سؤالی (یا شرطی) دارند.
متوجه شدم خیلی ممنون از توضیحات کاملتون
متوجه شدم
isMax و isMin رو حواسم نبود یه معنی دیگه هم میده ممنون که گفتین
خیلی ممنون از توضیحات کاملتون
سلام کد زیر را نوشتم
الان لاین ۱۶ اشاره به bg ارور tcl را میده و مینویسه اپشن ناشناخته.
سلام
سؤالتون مربوط به مدیریت خطای try except پایتون نیست! این مورد به تعریف دکمه در tkinter ربط داره. کلاس Button پارامتر bg داره و شما هم درست تعریف کردید، اما مطمئن نیستیم که همراه با عکس میتونید پسزمینه هم مشخص کنید یا نه. سعی کنید یکم تغییرات در نحوه تعریفش بدید و قدم به قدم اجزا رو اضافه کنید تا دلیل ارور رو شناسایی کنید.
سلام و درود میخوام که یک دکمه بسازم که با هر بار کلیک عدد یک که روش هست یکی یکی اضافه بشه کد زیر را نوشتم
الان برای اینکه دوباره تکرار بشه کد باید قسمت دوم را در تابع قرار بدم؟چون هر کاری کردم بعد از کلیک وارد press شد و تغییری نکرد و یک سوال دیگه که دارم چجوری میتونم مشخص کنم که دقیقا در کدام قسمت این دکمه نمایش داده بشه؟
سلام
البته سؤالتون مربوط به مدیریت خطای پایتون نمیشه! 😉
بهتره بعد از تنظیم عنوان، یک متغیر روی شی tk تعریف کنید. (مثل
root.counter = 0
)حالا داخل تابع، همین مقدار رو یکی افزایش بدید.
برای نمایش شمارنده روی دکمه، تا به حال چنین کاری نکردم ولی به نظر کاری که انجام دادید درسته. میتونید یک لیبل ایجاد کنین و متن اون رو مشابهاً عوض کنید.
سلام ممنون فقط سوال دومی که پرسیدم را جواب ندادی اینکه الان من با
button1.pack()
دکمه دقیقا بالا قرار میگیره حالا اگر بخوام تغییر بدم مکانشو باید چیکار کنم؟من فکر کردم میخواید شمارنده رو داخل دکمه قرار بدید!
برای تغییر محل دکمه میتونید از متد
place(x=20, y=50)
روی شئ دکمه کمک بگیرید.سلام چرا به جواب قبلی ای هم که دادید نیاز داشتم و خیلی کمکم کرد ممنون از کمکت
سلام در مورد این ارور یکم توضیح میدید این ارور وقتی هم میاد یه لاین بیشتر از کد هایی را که نوشتم نشون میده مثالا اگر ۲۳ تا لاین نوشتم این ارور میاد و به لاین ۲۴ اشاره میکنه
سلام
دو تا مورد رو تست کنید و اگه هنوز مشکل برقرار بود چند خط انتهایی کدتون رو بفرستید تا بیشتر بررسیش کنیم:
– بعد از کدتون، دو یا سه خط خالی ایجاد کنید.
– فایل رو با فرمت UTF-8 ذخیره کنید. (اگه با notepad++ بازش کنید، یه گزینه دقیقا به همین نام در تب encoding داره)
سلامی دوباره و دوباره
اولا که شرمنده من خیلی ذحمتتون دادم 🙁
دوما که من یک سری کد هارو انجام دادم مثل :
خطای زیر رو مشاهده کردم
و کد رو به صورت زیر تغییر دادم و اروری دریافت نکردم میخواستم ببینم درست انجام دادم!؟
و بعد از این کد هایی وارد کردم تا به این کد رسیدم
به ارور رسیدم که نتونستم تشخیص بدم مشکل از چیه
اگه میشه راهنمایی کنید یک دنیا ممنونم و اینکه شرمنده خیلی ذحمت میدم…
سلام. برای اتصال FTP همون دستور اول صحیحه. به جای domain میتونید ftp address که برای هاست یا سرور تعریف شده رو هم تست کنید.
خطایی که میگیرید، هم اولی و هم دومی به خاطر عدم برقراری ارتباط FTP با سرور هست. خطای فعلی میگه کانکشن از سمت سرور رد شده!
ابتدا سرویس هاست رو بررسی کنید و درصورت امکان یه یوزر جدید بسازید. شاید مشکل رفع بشه. ممکنه اگر کد رو داخل idle خط به خط اجرا کنید، خطای مرتبطتری بگیرید.
مثالهات عالیه مرسی
دمتون گرم
جالب بود .دمتتون گرم
خوشحالم که براتون مفید بوده! موفق باشید.
خلاصه و مفید
با تشکر از شما
خوشحالم که این آموزش براتون مفید بوده و ممنون از انرژی خوبتون.
موفق باشید