آموزش پایتون: ساخت اپلیکیشن دیکشنری — از صفر تا صد

۱۴۱۱ بازدید
آخرین به‌روزرسانی: ۰۵ مهر ۱۴۰۲
زمان مطالعه: ۱۲ دقیقه
آموزش پایتون: ساخت اپلیکیشن دیکشنری — از صفر تا صد

اینترنت جای شلوغی است و گاهی اوقات منابع مناسبی که را در جستجویش هستیم در آن نمی‌یابیم. بدین ترتیب یادگیری یک زبان برنامه‌نویسی جدید ممکن است کار دشواری باشد. در این حالت، اغلب یادگیرندگان تسلیم می‌شوند و یا موضوع دیگری را انتخاب می‌کنند. بنابراین باید تأکید کنیم که این سری مطالب راهنما صرفاً یک نوشته ساده در مورد آموزش پایتون نیست که به فراوانی در اینترنت یافت می‌شود. شما در این سری ده‌گانه از مطالب آموزشی، 10 اپلیکیشن واقعی پایتون می‌سازید و در این مسیر ابزارهای مهم و ضروری دیگر برای ارتقای مهارت‌های پایتون در مسیر یادگیری علم داده را فرا می‌گیرید. نخستین بخش از این سری مطالب به آموزش ساخت اپلیکیشن دیکشنری اختصاص دارد.

master-python

اغلب افراد این نکته را درک نمی‌کنند که یادگیری یک زبان برنامه‌نویسی اهمیت بالایی دارد. مهم نیست که این کار تا چه حد دشوار به نظر می‌رسد، شما بالاخره باید از یک جایی شروع کنید. بنابراین اگر هیچ تجربه عملی از برنامه‌نویسی با استفاده از پایتون ندارید، نباید نگران باشید، چون اگر صرفاً یک آشنایی مقدماتی با پایتون داشته باشید، می‌توانید شروع به مطالعه این راهنما بکنید.

اپلیکیشن دیکشنری

نخستین اپلیکیشنی که قرار است در این سری راهنماهای پایتون بنویسیم یک دیکشنری است. این دیکشنری از نوع تعاملی است. شاید این کار به نظر ساده بیاید؛ اما یک سفر هزاران کیلومتری هم از گام نخست آغاز می‌شود، بنابراین در این نوشته اولین گام خود را برمی‌داریم. اکنون سؤال این است که این دیکشنری چه کار خواهد کرد؟ دیکشنری ما چنان که انتظار می‌رود تعریف یک واژه را که کاربر وارد کرده است بازگشت می‌دهد. علاوه بر آن اگر کاربر هنگام وارد کردن واژه‌های مورد نظر خود، غلط املایی داشته باشد، برنامه ما نزدیک‌ترین کلمه را با بیان «آیا منظورتان این بود؟» (did you mean this instead) پیشنهاد می‌کند و اگر واژه‌ای بیش از یک تعریف داشته باشید، همه آن‌ها را بازیابی می‌کند. شاید ساخت این اپلیکیشن اکنون دیگر به نظرتان ساده نیاید. در هر حال با ما همراه باشید.

توجه کنید که هدف ما در این نوشته آن است که علاوه بر یادگیری روش ساخت اپلیکیشن، با روش کدنویسی نیز آشنا شویم، چون کد تمیز بسیار حائز اهمیت است.

گام 1: داده‌ها

برای این که بدانید دیکشنری چگونه کار خواهد کرد، باید بدانید که از چه داده‌هایی برای اجرای اعمال فوق‌الذکر استفاده می‌کند. داده‌ها در این اپلیکیشن در قالب JSON هستند. اگر از قبل JSON را می‌شناسید، می‌توانید چند خط آینده را رد کنید و به ادامه مطالعه این راهنما بپردازید. با این حال اگر نخستین باری است که واژه JSON را می‌شنوید و یا به کمی یادآوری اطلاعات نیاز دارید، می‌توانید در ادامه نمونه‌ای از قالب JSON را ببینید:

1{
2  "abandoned industrial site": [
3    "Site that cannot be used for any purpose, being contaminated by pollutants."
4  ],
5  "abandoned vehicle": [
6    "A vehicle that has been discarded in the environment, urban or otherwise, often found wrecked, destroyed, damaged or with a major component part stolen or missing."
7  ],
8  "abiotic factor": [
9    "Physical, chemical and other non-living environmental factor."
10  ],
11  "access road": [
12    "Any street or narrow stretch of paved surface that leads to a specific destination, such as a main highway."
13  ],
14  "access to the sea": [
15    "The ability to bring goods to and from a port that is able to harbor sea faring vessels."
16  ],
17  "accident": [
18    "An unexpected, unfortunate mishap, failure or loss with the potential for harming human life, property or the environment.",
19    "An event that happens suddenly or by chance without an apparent cause."
20  ],
21  "accumulator": [
22    "A rechargeable device for storing electrical energy in the form of chemical energy, consisting of one or more separate secondary cells.\\n(Source: CED)"
23  ],
24  "acidification": [
25    "Addition of an acid to a solution until the pH falls below 7."
26  ],
27  "acidity": [
28    "The state of being acid that is of being capable of transferring a hydrogen ion in solution."
29  ],
30  "acidity degree": [
31    "The amount of acid present in a solution, often expressed in terms of pH."
32  ],
33  "acid rain": [
34    "Rain having a pH less than 5.6."
35  ],
36  "acid": [
37    "A compound capable of transferring a hydrogen ion in solution.",
38    "Being harsh or corrosive in tone.",
39    "Having an acid, sharp or tangy taste.",
40    "A powerful hallucinogenic drug manufactured from lysergic acid.",
41    "Having a pH less than 7, or being sour, or having the strength to neutralize  alkalis, or turning a litmus paper red."
42  ],
43  "acoustic filter": [
44    "A device employed to reject sound in a particular range of frequencies while passing sound in another range of frequencies."
45  ],
46  "acoustic insulation": [
47    "The process of preventing the transmission of sound by surrounding with a nonconducting material."
48  ]
49}

شاید اطلاع نداشته باشید که در سال 2017 در هر ثانیه 2,500,000,000,000,000,000 (2.5 کوینتیلیون) بایت داده تولید شده است.

JSON چیست؟

JSON اختصاری برای عبارت «نمادگذاری شیء جاوا اسکریپت» (JavaScript Object Notation) به طور عمده شامل دو بخش کلید و مقدار است که یک مقدار را به یک کلید انتساب می‌دهد.

اینک نوبت به بررسی کد رسیده است. ابتدا کتابخانه JSON را ایمپورت می‌کنیم و سپس از متد load این کتابخانه برای بارگذاری داده‌هایی که در قالب JSON هستند استفاده خواهیم کرد. نکته مهم آن است که ما داده‌ها را در قالب JSON بارگذاری می‌کنیم؛ اما این داده‌ها در متغیر data در قالب «دیکشنری» پایتون ذخیره می‌شوند. اگر در مورد نوع داده دیکشنری در پایتون اطلاع ندارید، باید بگوییم که این نوع داده یک قالب برای ذخیره‌سازی داده‌ها است که شباهت زیادی به قالب JSON دارد و از همان حالت «کلید-مقدار» (key-value) پیروی می‌کند.

1#Import library
2import json
3
4#Loading the json data as python dictionary
5#Try typing "type(data)" in terminal after executing first two line of this snippet
6data = json.load(open("data.json"))
7
8#Function for retriving definition
9def retrive_definition(word):
10    return data[word]
11
12#Input from user
13word_user = input("Enter a word: ")
14
15#Retrive the definition using function and print the result
16print(retrive_definition(word_user))

به محض این که داده‌ها در پایتون بارگذاری شدند، یک تابع می‌نویسیم که کلمه را بگیرد و به دنبال تعریف آن در داده‌ها بگردد. این کار آسان است:

Data
گام 1– داده‌ها (خروجی) -- برای مشاهد تصویر در ابعاد اصلی روی این لینک کلیک کنید (+).

گام 2: بررسی کلمات ناموجود

ما می‌توانیم با بهره‌گیری از یک ساختار if-else به بررسی وضعیت وجود یا عدم وجود کلمات در میان داده‌های خود بپردازیم. اگر کلمه‌ای در میان داده‌ها موجود نباشد، این امر را به کاربر اطلاع می‌دهیم. در این مورد این کار با نمایش پیام «The word doesn’t exist, please double check it» صورت می‌گیرد.

1#Import library
2import json
3
4#Loading the json data as python dictionary
5#Try typing "type(data)" in terminal after executing first two line of this snippet
6data = json.load(open("dictionary.json"))
7
8#Function for retriving definition
9def retrive_definition(word):
10    #Check for non existing words
11    if word in data:
12        return data[word]
13    else:
14        return ("The word doesn't exist, please double check it.")
15
16#Input from user
17word_user = input("Enter a word: ")
18
19#Retrive the definition using function and print the result
20print(retrive_definition(word_user))
گام 2– بررسی عدم وجود کلمه‌ها (خروجی)

گام 3: حساسیت به حروف کوچک یا بزرگ

هر کاربر روش خاصی برای نوشتن کلمات دارد. با این که برخی افراد همه حروف را به صورت کوچک می‌نویسند؛ اما برخی دیگر حروف ابتدای کلمات را بزرگ می‌نویسند، هدف ما این است که خروجی کلمه‌هایی که به صورت‌های مختلف نوشته می‌شوند، همواره یکسان باشد. برای نمونه Rain و rain هر دو یک خروجی ایجاد می‌کنند. برای رسیدن به این هدف باید کلمه‌ای که کاربر وارد کرده را به حالت «همه حروف کوچک»، تبدیل کنیم، چون داده‌های ما در همین قالب هستند. این کار در پایتون با استفاده از متد ()lower ممکن است.

  • حالت 1 – برای اطمینان از این که برنامه ما تعریف کلماتی را که با حرف بزرگ آغاز شده‌اند (مانند Texas یا Tehran) به درستی بازگشت می‌دهد باید با استفاده از یک شرط if-else این وضعیت را بررسی کنیم.
  • حالت 2– برای اطمینان از این که برنامه ما تعریف کلمات اختصاری (مانند USA یا UN) را به درستی بازگشت می‌دهد، باید حالت‌های حروف بزرگ را نیز بررسی کنیم.
1#Import library
2import json
3
4#Loading the json data as python dictionary
5#Try typing "type(data)" in terminal after executing first two line of this snippet
6data = json.load(open("dictionary.json"))
7
8#Function for retriving definition
9def retrive_definition(word):
10    #Removing the case-sensitivity from the program
11    #For example 'Rain' and 'rain' will give same output
12    #Converting all letters to lower because out data is in that format
13    word = word.lower()
14
15    #Check for non existing words
16    #1st elif: To make sure the program return the definition of words that start with a capital letter (e.g. Delhi, Texas)
17    #2nd elif: To make sure the program return the definition of acronyms (e.g. USA, NATO)
18    if word in data:
19        return data[word]
20    elif word.title() in data:
21        return data[word.title()]
22    elif word.upper() in data:
23        return data[word.upper()]
24
25#Input from user
26word_user = input("Enter a word: ")
27
28#Retrive the definition using function and print the result
29print(retrive_definition(word_user))
گام 3– حساسیت به کوچکی/بزرگی حروف - برای مشاهده تصویر در ابعاد اصلی روی این لینک کلیک کنید (+).

در این حالت دیکشنری ما آماده ارائه اهداف اولیه خود شده است، یعنی تعاریف واژه‌ها را بازیابی می‌کند. اما باید یک گام دیگر نیز به پیش برویم. آیا تاکنون از روش پیشنهاد کلمات از سوی گوگل، برای اصلاح واژه‌ها در هنگام وارد کردن یک عبارت اشتباه در نوار جستجو شگفت‌زده شده‌اید؟

ما می‌خواهیم این امکان جالب را در دیکشنری خود قرار دهیم. پیش از آن که این کار را در گام 5 این راهنما انجام دهیم، باید سازوکار عملی این کار را در گام 4 درک کنیم.

گام 4: نزدیک‌ترین مطابقت

اکنون در مواردی که کاربر هنگام وارد کردن کلمه مورد نظر خود اشتباه املایی دارد، می خواهیم نزدیک‌ترین کلمه مطابق را به او پیشنهاد دهیم و بپرسیم آیا معنای این کلمه را می‌خواهد یا نه. این کار در پایتون با استفاده از کتابخانه difflib ممکن است. دو متد برای انجام این کار وجود دارد. ما هر دو را معرفی می‌کنیم و سپس آن را که کارایی بیشتری دارد انتخاب می‌کنیم.

روش اول: Sequence Matcher

برای درک این متد، ابتدا باید کتابخانه را ایمپورت کنیم و متد مورد نظر را از آن واکشی کنیم. تابع ()SequenceMatcher کلاً سه پارامتر می‌گیرد. پارامتر اول به بررسی صحت کلمه می‌پردازد، یعنی آیا کاراکتر فاصله یا خطوط خالی دارد یا نه، که در مورد ما چنین چیزی وجود ندارد. پارامترهای دوم و سوم، دو کلمه‌ای هستند که می‌خواهید مشابهت‌های آن‌ها را بیابید. این متد نتیجه را به صورت یک عدد کسری ارائه می‌کند.

1#Import library
2import json
3# This is a python library for 'Text Processing Serveices', as the offcial site suggests.
4import difflib
5from difflib import SequenceMatcher
6
7#Let's load the same data again
8data = json.load(open("dictionary.json"))
9
10#Run a Sequence Matcher
11#First parameter is 'Junk' which includes white spaces, blank lines and so onself.
12#Second and third parameters are the words you want to find similarities in-between.
13#Ratio is used to find how close those two words are in numerical terms
14value = SequenceMatcher(None, "rainn", "rain").ratio()
15
16#Print out the value
17print(value)
گام 4.1 - Sequence Matcher (خروجی)

همان طور که شاهد هستید، شباهت بین کلمه‌های rainn و rain برابر با 0.89 است که همان 89% است. این یک روش برای انجام کار فوق است. با این وجود روش دیگری نیز وجود دارد که در همین کتابخانه قابل انجام است و در آن نزدیک‌ترین مورد مطابقت مستقیم با یک کلمه ارائه می‌شود و دیگر از اعداد استفاده نمی‌شود.

روش دوم: دریافت نزدیک‌ترین مورد مطابقت

روش کار این متد چنین است که پارامتر نخست به عنوان کلمه‌ای است که می‌خواهیم مطابق آن را بیابیم و پارامتر دوم فهرست کلماتی است که مطابقت داشته‌اند. پارامتر سوم تعداد موارد مطابقتی را شامل می‌شود که می‌خواهید در خروجی خود داشته باشید. پارامتر آخر نیز یک معیار است که به صورت عددی نسبی، مانند متد قبلی عمل می‌کند. بدین ترتیب می‌توانیم مثلاً یک معیار به صورت 99% تعیین کنیم که صرفاً کلماتی که این معیار را دارند بازگشت یابند. بر حسب نیاز خود یک معیار برای این پارامتر تعیین کنید.

1#Import library
2import json
3# This is a python library for 'Text Processing Serveices', as the offcial site suggests.
4import difflib
5from difflib import get_close_matches
6
7#Let's load the same data again
8data = json.load(open("dictionary.json"))
9
10#Before you dive in, the basic template of this function is as follows
11#get_close_matches(word, posibilities, n=3, cutoff=0.66)
12#First parameter is of course the word for which you want to find close matches
13#Second is a list of sequences against which to match the word
14#[optional]Third is maximum number of close matches
15#[optional]where to stop considering a word as a match (0.99 being the closest to word while 0.0 being otherwise)
16
17output = get_close_matches("rain", ["help","mate","rainy"], n=1, cutoff = 0.75)
18
19# Print out output, any guesses?
20print(output)
اپلیکیشن دیکشنری در پایتون
گام 4.2 - دریافت نزدیک‌ترین مورد مطابقت (خروجی)

همان طور که شاهد هستید، خروجی فوق نیازی به توضیح ندارد. نزدیک‌ترین کلمه در میان آن سه کلمه، عبارت «rainy» است. اگر به این مرحله رسیده‌اید، باید بدانید که اینک بخش دشوار کار را به انجام رسانده‌اید. در ادامه کافی است این بخش را در کد خود قرار دهیم تا بتوانیم خروجی‌های مربوطه را داشته باشیم.

گام 5: «آیا منظورتان این بود؟»

ما برای این که این بخش کد خوانایی داشته باشد آن را به قسمت if-else برنامه خود اضافه کرده‌ایم. اینک با دو گزاره if-else اول آشنا هستیم و صرفاً مورد سوم را توضیح می‌دهیم. در این گزاره ابتدا طول نزدیک‌ترین مورد مطابقت را بررسی می‌کنیم، چون تنها می‌توانیم مواردی را نمایش دهیم که 1 یا چند مورد مطابقت نزدیک داشته باشند. تابع دریافت نزدیک‌ترین موارد مطابقت، کلمه‌ای را که کاربر وارد کرده است به عنوان نخستین پارامتر می‌گیرد و کل مجموعه داده‌های ما را می‌گردد تا نزدیک‌ترین مورد مطابقت را بیاید. در این بخش «کلید» (key) کلمه‌ای است که در میان داده‌های ما قرار دارد و «مقدار» (value) تعریف آن را شامل می‌شود که قبل‌تر توضیح دادیم. مقدار [0] در گزاره بازگشتی نزدیک‌ترین مورد مطابقت را در میان همه موارد مطابقت نشان می‌دهد.

1#Check for non existing words
2#1st elif: To make sure the program return the definition of words that start with a capital letter (e.g. Delhi, Texas)
3#2nd elif: To make sure the program return the definition of acronyms (e.g. USA, NATO)
4if word in data:
5    return data[word]
6elif word.title() in data:
7    return data[word.title()]
8elif word.upper() in data:
9    return data[word.upper()]
10#3rd elif: To find a similar word
11#-- len > 0 because we can print only when the word has 1 or more close matches
12#-- In the return statement, the last [0] represents the first element from the list of close matches
13elif len(get_close_matches(word, data.keys())) > 0:
14return ("Did you mean %s instead?" % get_close_matches(word, data.keys())[0])
اپلیکیشن دیکشنری در پایتون
گام 5 – «آیا منظورتان این بود؟»

بدین ترتیب ما نزدیک‌ترین کلمه به ورودی کاربر را تشخیص داده و از وی در مورد آن می‌پرسیم. اما نمی‌توانیم کاربر را با این سؤال تنها بگذاریم. بنابراین در گام بعدی ادامه مراحل را توضیح می‌دهیم.

گام 6: بازیابی تعریف

به این منظور یک ورودی دیگر از کاربر می‌گیریم و با تعریف یک لایه دیگر if-else آن را بررسی می‌کنیم. بدین ترتیب تعریف کلمه پیشنهادی به صورت زیر است:

1elif len(get_close_matches(word, data.keys())) > 0:
2    action = input("Did you mean %s instead? [y or n]: " % get_close_matches(word, data.keys())[0])
3    #-- If the answers is yes, retrive definition of suggested word
4    if (action == "y"):
5        return data[get_close_matches(word, data.keys())[0]]
6    elif (action == "n"):
7        return ("The word doesn't exist, yet.")
8    else:
9return ("We don't understand your entry. Apologies.")
اپلیکیشن دیکشنری در پایتون
گام 6 – بازیابی تعریف (خروجی) - برای مشاهده تصویر در ابعاد اصلی روی این لینک کلیک کنید (+).

گام 7: تزئین خروجی

در بخش قبل موفق شدیم تعریف واژه را از داده‌های خود استخراج کنیم و به کاربر نمایش دهیم؛ اما با وجود کروشه و موارد دیگر، متن خروجی چندان زیبا دیده نمی‌شود. در ادامه، این موارد را حذف کرده و ظاهر آن را زیباتر می‌کنیم. آیا متوجه شدید که واژه rain بیش از یک تعریف دارد؟ چندین واژه در میان داده‌های ما وجود دارند که بیش از یک تعریف دارند و از این رو باید برای کلماتی که بیش از یک معنی ‌دارند، حلقه‌ای روی خروجی قرار دهیم و تنها مواردی را نمایش دهیم که یک تعریف دارند:

1#Retrive the definition using function and print the result
2output = retrive_definition(word_user)
3
4#If a word has more than one definition, print them recursively
5if type(output) == list:
6    for item in output:
7        print("-",item)
8#For words having single definition
9else:
10print("-",output)
اپلیکیشن دیکشنری در پایتون
گام 7 – تزیین خروجی

اینک خروجی ما ظاهر بهتری یافته است. کد کامل برنامه را برای مطالعه در ادامه ارائه می‌کنیم. شما می‌توانید این برنامه را بنا بر نیازهای خود اصلاح کرده و یا ارتقا دهید.

کد برنامه دیکشنری در پایتون

1#Import library
2import json
3from difflib import get_close_matches
4
5#Loading the json data as python dictionary
6#Try typing "type(data)" in terminal after executing first two line of this snippet
7data = json.load(open("data.json"))
8
9#Function for retriving definition
10def retrive_definition(word):
11    #Removing the case-sensitivity from the program
12    #For example 'Rain' and 'rain' will give same output
13    #Converting all letters to lower because out data is in that format
14    word = word.lower()
15
16    #Check for non existing words
17    #1st elif: To make sure the program return the definition of words that start with a capital letter (e.g. Delhi, Texas)
18    #2nd elif: To make sure the program return the definition of acronyms (e.g. USA, NATO)
19    #3rd elif: To find a similar word
20    #-- len > 0 because we can print only when the word has 1 or more close matches
21    #-- In the return statement, the last [0] represents the first element from the list of close matches
22    if word in data:
23        return data[word]
24    elif word.title() in data:
25        return data[word.title()]
26    elif word.upper() in data:
27        return data[word.upper()]
28    elif len(get_close_matches(word, data.keys())) > 0:
29        action = input("Did you mean %s instead? [y or n]: " % get_close_matches(word, data.keys())[0])
30        #-- If the answers is yes, retrive definition of suggested word
31        if (action == "y"):
32            return data[get_close_matches(word, data.keys())[0]]
33        elif (action == "n"):
34            return ("The word doesn't exist, yet.")
35        else:
36            return ("We don't understand your entry. Apologies.")
37
38#Input from user
39word_user = input("Enter a word: ")
40
41#Retrive the definition using function and print the result
42output = retrive_definition(word_user)
43
44#If a word has more than one definition, print them recursively
45if type(output) == list:
46    for item in output:
47        print("-",item)
48#For words having single definition
49else:
50print("-",output)

سخن پایانی

شما در این نوشته موارد زیادی را آموختید. در این مقاله در مورد داده‌های JOSN، کارکردهای ابتدایی پایتون، کتابخانه جدیدی به نام difflib و همچنین اهمیت نوشتن کدهای تمیز مطالبی را آموختیم. همچنین می‌توانید از مجموعه داده‌های مختلف استفاده کرده و مهارت‌هایی را که آموخته‌اید روی آن‌ها اعمال کنید. به این ترتیب می‌توانید به یک فرد خبره در رشته «علم داده» (Data Science) تبدیل شوید.

برای مطالعه بخش بعدی این مطلب می‌توانید از لینک زیر استفاده کنید:

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱۵ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
towardsdatascience
۴ دیدگاه برای «آموزش پایتون: ساخت اپلیکیشن دیکشنری — از صفر تا صد»

salam merc

چجوری پایگاه داده مون رو برای برنامه تعریف کنیم؟
من میخوام یه دیکشنری تخصصی طراحی کنم و واژه های خاصی رو تعریف کنم و معانی رو داخل دیکشنری قرار بدم.

مهندس جان ، انشاالله مستدام باشید و سرافراز.

تنها چیزی که پایتون را کمی غیر دلچسب می کنه نداشتن خروجی واضحه، سالها قبل اکشن اسکریپت و ویژوال بیسیک کار می کردم.
از همان hello word شما خروجی را می دیدید یا می توانستید به عنوان یک برنامه روی دسکتاپ خودتان مشاهده کنید!

آیا راهی نیست که کد های پایتون را توی یک فریمی قابی ،… چیزی خروجی بگیریم.
مثلاً همین دیکشنری را در گوشی اندروید مان دانلود کنیم و در یک پلت فرم ببینیم؟؟؟

سلام خسته نباشید
کد من وقتی دستور
data = json.load(open(“dic.json”))
رو مینویسم خطا میده
لطفا راهنمایی کنید.

بخشی از خطا :
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 5 column 1 (char 27)

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *