Python Coding Academy
135 subscribers
7 photos
3 files
4 links
🐍 Learn Python. Build Skills. Create the Future.

📚 Python lessons from beginner to advanced
💻 Clear explanations & real-world examples
🚀 Learn to code. Learn to build.
Download Telegram
🐍 آموزش جامع Python — پست ۱۴۰
تغییر موقت پوشه کاری با contextlib.chdir()
گاهی برنامه لازم دارد برای مدت کوتاهی در یک پوشه خاص کار کند و بعد از اتمام عملیات، به پوشه قبلی برگردد.
در Python می‌توانیم از chdir() در contextlib استفاده کنیم.
contextlib.chdir()
از Python 3.11 اضافه شده است.

🔹 استفاده ساده
from contextlib import chdir
import os

print(os.getcwd())

with chdir("data"):
print(os.getcwd())

print(os.getcwd())

اگر پوشه data وجود داشته باشد:
C:\project
C:\project\data
C:\project

یعنی:
قبل از with → پوشه اصلی
داخل with → پوشه data
بعد از with → پوشه اصلی

🔹 چرا chdir() مفید است؟
فرض کنید:
project/
├── main.py
└── data/
├── users.txt
└── products.txt

می‌توانیم موقتاً وارد data شویم:
from contextlib import chdir

with chdir("data"):
with open("users.txt", "r") as file:
print(file.read())

داخل with، مسیر نسبی users.txt نسبت به پوشه data بررسی می‌شود.
🔹 بازگشت خودکار
اگر خطایی هم رخ دهد، مسیر قبلی بازگردانده می‌شود:
from contextlib import chdir

try:
with chdir("data"):
print("Inside data")
raise ValueError("Something went wrong")
except ValueError:
print("Error handled")

بعد از خروج از with، CWD دوباره به مسیر قبلی برمی‌گردد.
⚠️ محدودیت مهم: تغییر مسیر سراسری
نکته مهم این است که CWD متعلق به کل Process است، نه یک تابع یا Thread خاص.
مثلاً:
from contextlib import chdir
import os

with chdir("data"):
print(os.getcwd())

تا زمانی که داخل with هستیم، هر کدی که از مسیر نسبی استفاده کند، تغییر مسیر را می‌بیند:
open("file.txt")

در این حالت Python فایل را نسبت به:
data/file.txt

جست‌وجو می‌کند.
🔥 مشکل در Threadها
فرض کنیم دو Thread داریم:
def worker_a():
os.chdir("folder_a")

def worker_b():
open("file.txt")

اگر worker_a مسیر کاری را تغییر دهد، worker_b هم ممکن است مسیر جدید را ببیند.
چون CWD متعلق به Thread اول نیست؛ وضعیت کل Process تغییر کرده است.
بنابراین در برنامه‌های چندنخی، تغییر CWD می‌تواند باعث رفتارهای غیرقابل‌پیش‌بینی شود.
🔹 حتی توابع دیگر هم تحت تأثیر قرار می‌گیرند
مثلاً:
def load_config():
return open("config.json")

حالا:
from contextlib import chdir

with chdir("data"):
load_config()

تابع load_config() هیچ اطلاعی از data ندارد، اما فایل را نسبت به پوشه جدید جست‌وجو می‌کند.
یعنی تغییر CWD می‌تواند روی کدهای دیگری که از مسیر نسبی استفاده می‌کنند نیز اثر بگذارد.
✅ راهکار بهتر در بسیاری از موارد
به‌جای تغییر CWD، می‌توانیم مسیر فایل را مستقیماً مشخص کنیم:
from pathlib import Path

base = Path("data")
file_path = base / "config.json"

with open(file_path) as file:
print(file.read())

در این روش، تابع به CWD سراسری وابسته نیست.
🔹 تفاوت با os.chdir()
می‌توانیم مستقیماً بنویسیم:
import os

os.chdir("data")

اما در این حالت باید خودمان مسیر قبلی را ذخیره و بعداً برگردانیم.
contextlib.chdir() این کار را به‌صورت Context Manager انجام می‌دهد:
from contextlib import chdir

with chdir("data"):
...

پس تغییر مسیر موقت و قابل‌بازگشت است؛ اما همچنان خاصیت سراسری CWD را دارد.
🧠 خلاصه
CWD
↓
متعلق به کل Process
↓
همه کدهای استفاده‌کننده از مسیر نسبی آن را می‌بینند

chdir(path)
↓
تغییر موقت CWD
↓
اجرای کد
↓
بازگشت خودکار به مسیر قبلی

⚠️ در کدهای چندنخی یا کدهایی که نباید وضعیت سراسری برنامه را تغییر دهند، بهتر است تا حد امکان از مسیرهای صریح با pathlib استفاده کنیم.
🎯 تمرین
ساختار زیر را در نظر بگیر:
project/
├── main.py
└── files/
└── data.txt

برنامه‌ای بنویس که:
مسیر فعلی را چاپ کند.
با chdir() وارد files شود.
data.txt را باز کند و محتوایش را چاپ کند.
بعد از خروج از with دوباره مسیر فعلی را چاپ کند.
باید مشاهده کنی که مسیر بعد از with به مسیر قبلی برگشته است.
پست ۱۴۰ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۴۱
کار با مسیرها با pathlib
تا اینجا برای کار با مسیرها از رشته‌هایی مثل:
"data/config.json"

و همچنین os استفاده کردیم.
اما در Python یک روش مدرن‌تر و خواناتر برای کار با مسیرها وجود دارد:
pathlib

🔹 ساخت یک مسیر
ابتدا Path را import می‌کنیم:
from pathlib import Path

path = Path("data")

print(path)

خروجی:
data

Path مسیر را به‌صورت یک شیء در اختیار ما قرار می‌دهد.
🔹 ساخت مسیرهای تو در تو
به‌جای چسباندن رشته‌ها با +، می‌توانیم از / برای ترکیب مسیرها استفاده کنیم:
from pathlib import Path

base = Path("project")

file_path = base / "data" / "users.txt"

print(file_path)

خروجی:
project/data/users.txt

این / در pathlib به معنی تقسیم نیست؛ برای ترکیب مسیرها استفاده می‌شود.
🔹 ساخت پوشه‌های تو در تو
فرض کنیم می‌خواهیم این ساختار را ایجاد کنیم:
project/
└── data/
└── users/

می‌توانیم بنویسیم:
from pathlib import Path

path = Path("project/data/users")

path.mkdir(parents=True, exist_ok=True)

اینجا:
parents=True

یعنی اگر پوشه‌های والد وجود نداشته باشند، آن‌ها را هم بساز.
بنابراین اگر هیچ‌کدام از این پوشه‌ها وجود نداشته باشند:
project
└── data
└── users

هر سه پوشه ساخته می‌شوند.
گزینه:
exist_ok=True

یعنی اگر پوشه‌ها از قبل وجود داشته باشند، Python خطا ندهد.
پس ترکیب:
mkdir(parents=True, exist_ok=True)

برای ساخت پوشه‌های تو‌در‌تو بسیار کاربردی است.
🔹 تفاوت parents=True و exist_ok=True
این دو گزینه کار متفاوتی انجام می‌دهند:
parents=True
→ پوشه‌های والد را هم ایجاد کن

exist_ok=True
→ اگر پوشه از قبل وجود داشت، خطا نده

مثلاً:
Path("project/data/users").mkdir(
parents=True,
exist_ok=True
)

حتی اگر project و data وجود نداشته باشند، Python مسیر را ایجاد می‌کند.
🔹 بررسی وجود مسیر
با exists() می‌توانیم بررسی کنیم مسیر وجود دارد یا نه:
from pathlib import Path

path = Path("data")

print(path.exists())

نتیجه:
True

یا:
False

🔹 فایل است یا پوشه؟
Path متدهای مختلفی برای بررسی نوع مسیر دارد:
from pathlib import Path

path = Path("data")

print(path.is_file())
print(path.is_dir())

اگر data یک پوشه باشد:
False
True

🔹 ساخت یک پوشه ساده
اگر فقط یک پوشه را بخواهیم ایجاد کنیم:
from pathlib import Path

path = Path("data")

path.mkdir(exist_ok=True)

در این حالت چون فقط خود data را می‌سازیم، نیازی به parents=True نداریم.
🔹 نام فایل و پسوند
فرض کنیم:
from pathlib import Path

path = Path("data/users.txt")

print(path.name)
print(path.stem)
print(path.suffix)

خروجی:
users.txt
users
.txt

یعنی:
name   → نام کامل فایل
stem → نام فایل بدون پسوند
suffix → پسوند فایل

⚠️ نکته مهم
pathlib فقط مخصوص فایل‌ها نیست.
می‌توانیم از آن برای:
فایل‌ها
پوشه‌ها
مسیرهای نسبی
مسیرهای مطلق
ترکیب مسیرها
ساخت پوشه‌های تو‌در‌تو
استفاده کنیم.
همچنین استفاده از Path باعث می‌شود کد مدیریت مسیرها خواناتر و قابل‌حمل‌تر باشد.
🧠 خلاصه
from pathlib import Path

path = Path("data")

path.exists() # وجود دارد؟
path.is_file() # فایل است؟
path.is_dir() # پوشه است؟
path.mkdir() # ساخت پوشه
path.name # نام کامل
path.stem # نام بدون پسوند
path.suffix # پسوند

برای ساخت پوشه‌های تو‌در‌تو:
path.mkdir(
parents=True,
exist_ok=True
)

🎯 تمرین
کدی بنویس که:
مسیر project/data/users را ایجاد کند.
حتی اگر project و data وجود نداشتند، آن‌ها را هم بسازد.
اگر پوشه‌ها از قبل وجود داشتند، خطا ندهد.
مسیر فایل users.json را داخل پوشه users بسازد.
نام فایل و پسوند آن را چاپ کند.
شروع:
from pathlib import Path

users_dir = Path("project/data/users")

پست ۱۴۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۲
خواندن و نوشتن فایل با pathlib
در پست قبل با Path و ساخت مسیرها آشنا شدیم.
حالا می‌توانیم با Path مستقیماً فایل متنی را بخوانیم و بنویسیم.
🔹 نوشتن در فایل با write_text()
from pathlib import Path

file = Path("data/message.txt")

file.write_text("Hello Python", encoding="utf-8")

اگر فایل وجود نداشته باشد، ساخته می‌شود.
⚠️ اما اگر پوشه data وجود نداشته باشد، write_text() خودش آن را ایجاد نمی‌کند و با خطا مواجه می‌شویم.
🔹 ساخت پوشه والد قبل از فایل
برای اطمینان، می‌توانیم ابتدا پوشه والد فایل را بسازیم:
from pathlib import Path

file = Path("data/message.txt")

file.parent.mkdir(parents=True, exist_ok=True)

file.write_text("Hello Python", encoding="utf-8")

اینجا:
file.parent

پوشه والد فایل را برمی‌گرداند:
data

و:
file.parent.mkdir(
parents=True,
exist_ok=True
)

پوشه data را ایجاد می‌کند.
اگر پوشه‌های والد بیشتری داشته باشیم، parents=True همه آن‌ها را ایجاد می‌کند.
مثلاً:
from pathlib import Path

file = Path("project/data/users/info.txt")

file.parent.mkdir(parents=True, exist_ok=True)

file.write_text("Ali", encoding="utf-8")

ساختار زیر ایجاد می‌شود:
project/
└── data/
└── users/
└── info.txt

🔹 خواندن فایل با read_text()
بعد از نوشتن می‌توانیم فایل را بخوانیم:
from pathlib import Path

file = Path("data/message.txt")

text = file.read_text(encoding="utf-8")

print(text)

خروجی:
Hello Python

🔹 نوشتن چند خط
می‌توانیم از \n برای ایجاد خط جدید استفاده کنیم:
from pathlib import Path

file = Path("data/users.txt")

file.parent.mkdir(parents=True, exist_ok=True)

file.write_text(
"Ali\nSara\nReza",
encoding="utf-8"
)

محتوای فایل:
Ali
Sara
Reza

🔹 خواندن همه محتوا
read_text() کل محتوای فایل را به‌صورت یک str برمی‌گرداند:
text = file.read_text(encoding="utf-8")

print(type(text))

خروجی:
<class 'str'>

⚠️ نکته مهم درباره encoding
برای فایل‌های متنی، مخصوصاً زمانی که فارسی داریم، بهتر است encoding را مشخص کنیم:
encoding="utf-8"

مثلاً:
from pathlib import Path

file = Path("data/message.txt")

file.parent.mkdir(parents=True, exist_ok=True)

file.write_text(
"سلام Python",
encoding="utf-8"
)

print(file.read_text(encoding="utf-8"))

خروجی:
سلام Python

🔹 تفاوت با open()
قبلاً برای کار با فایل‌ها می‌توانستیم از:
open()

استفاده کنیم.
با pathlib برای عملیات ساده متنی می‌توانیم مستقیماً از:
file.write_text(...)

و:
file.read_text(...)

استفاده کنیم.
⚠️ نکته مهم
write_text() محتوای قبلی فایل را جایگزین می‌کند:
file.write_text("First", encoding="utf-8")
file.write_text("Second", encoding="utf-8")

در پایان محتوای فایل:
Second

خواهد بود.
برای اضافه‌کردن محتوا به انتهای فایل، بعداً روش مناسب آن را بررسی می‌کنیم.
🧠 خلاصه
from pathlib import Path

file = Path("data/message.txt")

file.parent.mkdir(parents=True, exist_ok=True)

file.write_text("Hello", encoding="utf-8")

text = file.read_text(encoding="utf-8")

یعنی:
file.parent → پوشه والد فایل
mkdir() → ساخت پوشه
write_text → نوشتن متن
read_text → خواندن متن

🎯 تمرین
برنامه‌ای بنویس که:
مسیر project/data/users/info.txt را ایجاد کند.
تمام پوشه‌های والد را در صورت نیاز بسازد.
داخل فایل نام و سن یک کاربر را بنویسد.
فایل را دوباره بخواند.
محتوای آن را چاپ کند.
مثلاً:
Name: Ali
Age: 25

پست ۱۴۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۳
اضافه کردن محتوا به فایل با حالت append
در پست قبل یاد گرفتیم write_text() محتوای فایل را جایگزین می‌کند.
اما گاهی نمی‌خواهیم اطلاعات قبلی حذف شود؛ می‌خواهیم متن جدید را به انتهای فایل اضافه کنیم.
برای این کار از حالت:
"a"

استفاده می‌کنیم.
🔹 اضافه کردن متن به فایل
با Path.open() می‌توانیم فایل را در حالت append باز کنیم:
from pathlib import Path

file = Path("data/users.txt")

with file.open("a", encoding="utf-8") as f:
f.write("Ali\n")

اگر فایل قبلاً این محتوا را داشته باشد:
Sara
Reza

بعد از اجرای کد:
Sara
Reza
Ali

خواهد شد.
🔹 تفاوت w و a
حالت w برای نوشتن و جایگزین کردن محتوا است:
with file.open("w", encoding="utf-8") as f:
f.write("Ali\n")

محتوای قبلی حذف می‌شود.
اما حالت a برای اضافه کردن است:
with file.open("a", encoding="utf-8") as f:
f.write("Ali\n")

محتوای قبلی باقی می‌ماند و متن جدید به انتهای فایل اضافه می‌شود.
🔹 یک مثال کاربردی
فرض کنیم می‌خواهیم کاربران را یکی‌یکی به فایل اضافه کنیم:
from pathlib import Path

file = Path("data/users.txt")

with file.open("a", encoding="utf-8") as f:
f.write("Ali\n")

with file.open("a", encoding="utf-8") as f:
f.write("Sara\n")

with file.open("a", encoding="utf-8") as f:
f.write("Reza\n")

محتوای فایل:
Ali
Sara
Reza

هر بار که فایل با حالت a باز می‌شود، اطلاعات جدید به انتهای آن اضافه می‌شود.
🔹 چرا از with استفاده می‌کنیم؟
with file.open("a", encoding="utf-8") as f:
f.write("Ali\n")

with باعث می‌شود بعد از پایان کار، فایل به‌درستی بسته شود.
این روش برای مدیریت فایل‌ها امن‌تر و استانداردتر است.
⚠️ نکته مهم درباره \n
اگر می‌خواهیم هر مقدار در یک خط جدید قرار بگیرد، باید خط جدید ایجاد کنیم:
f.write("Ali\n")

اگر \n نگذاریم:
f.write("Ali")
f.write("Sara")

نتیجه:
AliSara

اما:
f.write("Ali\n")
f.write("Sara\n")

نتیجه:
Ali
Sara

🧠 خلاصه
"w" → نوشتن و جایگزین کردن محتوا
"a" → اضافه کردن محتوا به انتهای فایل
"r" → خواندن فایل

مثلاً برای اضافه کردن:
with file.open("a", encoding="utf-8") as f:
f.write("New data\n")

🎯 تمرین
برنامه‌ای بنویس که:
فایل data/log.txt را باز کند.
عبارت Program started را به انتهای فایل اضافه کند.
دوباره همین برنامه را اجرا کن و تفاوت را ببین.
در هر اجرا، متن در یک خط جدید اضافه شود.
کد را طوری بنویس که اطلاعات قبلی فایل حذف نشود.
پست ۱۴۳ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۴
خواندن فایل خط‌به‌خط با readlines() و readline()
در پست‌های قبل یاد گرفتیم کل محتوای فایل را با read_text() بخوانیم.
اما همیشه لازم نیست کل فایل را یکجا بخوانیم.
گاهی می‌خواهیم فایل را خط‌به‌خط پردازش کنیم.
🔹 readline()
متد readline() یک خط از فایل را می‌خواند:
from pathlib import Path

file = Path("data/users.txt")

with file.open("r", encoding="utf-8") as f:
print(f.readline())
print(f.readline())

اگر فایل این باشد:
Ali
Sara
Reza

خروجی:
Ali

Sara

هر بار که readline() را اجرا می‌کنیم، به خط بعدی می‌رویم.
🔹 readlines()
readlines() تمام خطوط فایل را می‌خواند و آن‌ها را داخل یک لیست قرار می‌دهد:
from pathlib import Path

file = Path("data/users.txt")

with file.open("r", encoding="utf-8") as f:
lines = f.readlines()

print(lines)

اگر فایل:
Ali
Sara
Reza

باشد، نتیجه چیزی شبیه این است:
['Ali\n', 'Sara\n', 'Reza\n']

دقت کن \n نیز ممکن است داخل رشته هر خط وجود داشته باشد.
🔹 پردازش خط‌به‌خط با for
در بسیاری از مواقع لازم نیست از readlines() استفاده کنیم.
خود فایل قابل پیمایش است:
from pathlib import Path

file = Path("data/users.txt")

with file.open("r", encoding="utf-8") as f:
for line in f:
print(line)

این روش برای خواندن فایل‌های بزرگ بسیار مناسب‌تر است، چون همه خطوط را یکجا داخل یک لیست قرار نمی‌دهد.
🔹 حذف \n با strip()
اگر نمی‌خواهیم خط جدید همراه متن باقی بماند:
from pathlib import Path

file = Path("data/users.txt")

with file.open("r", encoding="utf-8") as f:
for line in f:
print(line.strip())

اگر فایل:
Ali
Sara
Reza

باشد، هر خط به‌صورت تمیز پردازش می‌شود.
⚠️ تفاوت مهم
readline()
→ یک خط را می‌خواند

readlines()
→ همه خطوط را در یک لیست قرار می‌دهد

for line in file
→ فایل را خط‌به‌خط پیمایش می‌کند

برای فایل‌های کوچک، هر سه روش می‌توانند مناسب باشند.
برای فایل‌های بزرگ، پیمایش خط‌به‌خط معمولاً انتخاب مناسب‌تری است.
🧠 یک مثال کاربردی
فرض کنیم فایل شامل نمره‌ها باشد:
18
15
20
12

می‌توانیم آن‌ها را خط‌به‌خط بخوانیم:
from pathlib import Path

file = Path("data/scores.txt")

with file.open("r", encoding="utf-8") as f:
for line in f:
score = int(line.strip())
print(score)

خروجی:
18
15
20
12

اینجا:
line.strip()

متن خط را تمیز می‌کند و:
int(...)

آن را به عدد صحیح تبدیل می‌کند.
🧠 خلاصه
f.readline()

یک خط.
f.readlines()

تمام خطوط به شکل لیست.
for line in f:

پردازش خط‌به‌خط.
🎯 تمرین
فایلی به نام:
data/numbers.txt

بساز و داخل آن قرار بده:
10
20
30
40

سپس برنامه‌ای بنویس که:
فایل را باز کند.
اعداد را خط‌به‌خط بخواند.
هر خط را به int تبدیل کند.
مجموع اعداد را محاسبه کند.
مجموع را چاپ کند.
خروجی مورد انتظار:
100

پست ۱۴۴ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۵
کپی، انتقال و حذف فایل و پوشه با pathlib
حالا که با ساخت، خواندن و نوشتن فایل‌ها آشنا شدیم، وقت آن است که عملیات مدیریتی روی فایل‌ها و پوشه‌ها را یاد بگیریم.
برای این کار می‌توانیم از shutil در کنار pathlib استفاده کنیم.
🔹 کپی کردن فایل
برای کپی فایل از shutil.copy2() استفاده می‌کنیم:
from pathlib import Path
import shutil

source = Path("data/users.txt")
destination = Path("backup/users.txt")

destination.parent.mkdir(parents=True, exist_ok=True)

shutil.copy2(source, destination)

در اینجا:
source      → فایل اصلی
destination → محل کپی

محتوای فایل اصلی حفظ می‌شود و یک نسخه در backup ایجاد می‌شود.
🔹 انتقال یا تغییر نام فایل
برای انتقال فایل می‌توانیم از shutil.move() استفاده کنیم:
from pathlib import Path
import shutil

source = Path("data/users.txt")
destination = Path("archive/users.txt")

destination.parent.mkdir(parents=True, exist_ok=True)

shutil.move(source, destination)

بعد از اجرا:
data/users.txt

دیگر وجود ندارد و فایل به:
archive/users.txt

منتقل شده است.
move() برای تغییر نام هم کاربرد دارد:
shutil.move(
"data/old.txt",
"data/new.txt"
)

🔹 حذف یک فایل
برای حذف فایل می‌توانیم از Path.unlink() استفاده کنیم:
from pathlib import Path

file = Path("data/users.txt")

file.unlink()

بعد از اجرای این کد، فایل حذف می‌شود.
⚠️ unlink() را با دقت استفاده کن؛ حذف فایل عملیاتی برگشت‌پذیر مانند انتقال یا کپی نیست.
🔹 بررسی قبل از حذف
بهتر است قبل از حذف، وجود فایل را بررسی کنیم:
from pathlib import Path

file = Path("data/users.txt")

if file.exists():
file.unlink()

🔹 حذف پوشه خالی
برای حذف یک پوشه خالی:
from pathlib import Path

folder = Path("data/old")

folder.rmdir()

⚠️ rmdir() فقط پوشه‌ای را حذف می‌کند که خالی باشد.
اگر داخل پوشه فایل یا پوشه دیگری وجود داشته باشد، حذف انجام نمی‌شود.
🔹 حذف پوشه دارای محتوا
برای حذف یک پوشه همراه با تمام محتویات آن از shutil.rmtree() استفاده می‌شود:
from pathlib import Path
import shutil

folder = Path("data/old")

if folder.exists():
shutil.rmtree(folder)

⚠️ این عملیات می‌تواند تمام فایل‌ها و پوشه‌های داخل مسیر را حذف کند؛ بنابراین باید با دقت استفاده شود.
🧠 خلاصه
shutil.copy2() → کپی فایل
shutil.move() → انتقال / تغییر نام
Path.unlink() → حذف فایل
Path.rmdir() → حذف پوشه خالی
shutil.rmtree() → حذف پوشه و تمام محتویات

🎯 تمرین
ساختار زیر را در نظر بگیر:
project/
├── data/
│ └── users.txt
└── backup/

برنامه‌ای بنویس که:
فایل users.txt را داخل backup کپی کند.
فایل اصلی را به archive/users.txt منتقل کند.
وجود فایل منتقل‌شده را بررسی کند.
در صورت وجود، آن را حذف کند.
پست ۱۴۵ از دوره جامع Python 🐍
calculator.py
8.1 KB
🖩 ماشین‌حساب قدرتمند پایتون
یک ماشین‌حساب علمی با پایتون ساختم — سریع، امن و بدون نیاز به کتابخونه‌ی اضافه.
امکانات: عملیات پایه، توابع علمی (sin, cos, log, sqrt, fact...)، حافظه (M+/MR/MC)، تاریخچه، ثابت‌های pi و e، و محاسبه عبارات تودرتو.
راه‌اندازی
bash
python3 calculator.py

تست سریع
>>> (3+5)*sqrt(16) - sin(30)
= 31.5
>>> fact(5)
= 120
>>> help ← راهنمای کامل

🔐 برخلاف اکثر ماشین‌حساب‌های مشابه، از eval() استفاده نمی‌کنه — تجزیه با AST انجام میشه، پس ورودی کاربر نمی‌تونه کد دلخواه اجرا کنه.
🐍 آموزش جامع Python — پست ۱۴۶
پیدا کردن فایل‌ها و پوشه‌ها با Path.glob()
در پست قبل یاد گرفتیم چگونه فایل‌ها و پوشه‌ها را مدیریت کنیم.
حالا فرض کنید داخل یک پوشه تعداد زیادی فایل داریم و می‌خواهیم فقط فایل‌های موردنظرمان را پیدا کنیم.
برای این کار می‌توانیم از glob() در pathlib استفاده کنیم.
🔹 پیدا کردن فایل‌های خاص
فرض کنیم ساختار زیر را داریم:
project/
├── data/
│ ├── users.txt
│ ├── products.txt
│ ├── notes.txt
│ └── image.png

می‌توانیم تمام فایل‌های .txt را پیدا کنیم:
from pathlib import Path

data = Path("project/data")

for file in data.glob("*.txt"):
print(file)

خروجی:
project/data/users.txt
project/data/products.txt
project/data/notes.txt

فایل image.png پیدا نمی‌شود، چون پسوند آن .png است.
🔹 glob("*.txt") یعنی چه؟
در:
data.glob("*.txt")

علامت:
*

یعنی هر نامی.
پس:
*.txt

یعنی:
هر فایلی که پسوند آن

.txt
باشد.

مثلاً:
users.txt
notes.txt
data.txt

همه با این الگو مطابقت دارند.
🔹 پیدا کردن فایل‌های Python
برای پیدا کردن فایل‌های .py:
from pathlib import Path

project = Path("project")

for file in project.glob("*.py"):
print(file)

🔹 جستجو در پوشه‌های تو‌در‌تو با rglob()
glob() به‌صورت معمول در همان پوشه جستجو می‌کند.
اما اگر بخواهیم داخل تمام زیرپوشه‌ها نیز جستجو کنیم، می‌توانیم از rglob() استفاده کنیم:
from pathlib import Path

project = Path("project")

for file in project.rglob("*.txt"):
print(file)

مثلاً اگر ساختار این باشد:
project/
├── data/
│ ├── users.txt
│ └── products.txt
│
└── backup/
└── old/
└── users.txt

rglob("*.txt") هر سه فایل .txt را پیدا می‌کند.
🔹 فقط پوشه‌ها
می‌توانیم با glob() پوشه‌ها را هم پیدا کنیم:
from pathlib import Path

project = Path("project")

for item in project.glob("*"):
if item.is_dir():
print(item)

🔹 بررسی نوع نتیجه
هر چیزی که از glob() دریافت می‌کنیم یک شیء Path است:
from pathlib import Path

data = Path("project/data")

for item in data.glob("*"):
print(item)
print(type(item))

⚠️ نکته مهم
glob() خودش فایل‌ها را حذف یا تغییر نمی‌دهد.
فقط مسیرهای مطابق الگو را پیدا می‌کند.
مثلاً:
files = data.glob("*.txt")

فقط فایل‌های .txt را پیدا می‌کند.
بعد می‌توانیم روی آن‌ها عملیات دیگری انجام دهیم.
🔥 مثال کاربردی
فرض کنیم می‌خواهیم اندازه فایل‌های متنی را بررسی کنیم:
from pathlib import Path

data = Path("project/data")

for file in data.glob("*.txt"):
print(file.name, file.stat().st_size)

stat().st_size اندازه فایل را برحسب بایت برمی‌گرداند.
🧠 خلاصه
glob("*.txt")
→ پیدا کردن فایل‌های txt در همان پوشه

glob("*")
→ پیدا کردن موارد موجود در همان پوشه

rglob("*.txt")
→ پیدا کردن فایل‌های txt در پوشه و تمام زیرپوشه‌ها

🎯 تمرین
ساختار زیر را در نظر بگیر:
project/
├── data/
│ ├── users.txt
│ ├── products.txt
│ └── image.png
│
└── backup/
└── old/
└── users.txt

برنامه‌ای بنویس که:
تمام فایل‌های .txt داخل project و زیرپوشه‌های آن را پیدا کند.
نام هر فایل را چاپ کند.
مسیر کامل هر فایل را نیز چاپ کند.
پست ۱۴۶ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۷
اطلاعات فایل با stat() و ویژگی‌های مسیر
در پست قبل با glob() فایل‌ها را پیدا کردیم.
حالا می‌خواهیم اطلاعات بیشتری درباره یک فایل یا مسیر به دست بیاوریم؛ مثل اندازه فایل، زمان تغییر و نوع مسیر.
برای این کار می‌توانیم از stat() استفاده کنیم.
🔹 اندازه فایل با stat()
from pathlib import Path

file = Path("data/users.txt")

info = file.stat()

print(info.st_size)

st_size اندازه فایل را برحسب بایت برمی‌گرداند.
مثلاً:
125

یعنی اندازه فایل ۱۲۵ بایت است.
🔹 چند ویژگی مهم
شیء برگشتی از stat() اطلاعات مختلفی دارد:
from pathlib import Path

file = Path("data/users.txt")

info = file.stat()

print(info.st_size)
print(info.st_mtime)

یعنی:
st_size → اندازه فایل برحسب بایت
st_mtime → زمان آخرین تغییر

مقدار st_mtime معمولاً به شکل یک timestamp عددی نمایش داده می‌شود.
🔹 تبدیل زمان تغییر به تاریخ قابل خواندن
برای نمایش زمان به شکل قابل فهم‌تر:
from pathlib import Path
from datetime import datetime

file = Path("data/users.txt")

timestamp = file.stat().st_mtime

modified = datetime.fromtimestamp(timestamp)

print(modified)

خروجی مثلاً:
2026-09-23 18:42:10

زمان دقیق به سیستم شما بستگی دارد.
🔹 بررسی اندازه فایل
می‌توانیم از اندازه فایل برای یک بررسی ساده استفاده کنیم:
from pathlib import Path

file = Path("data/users.txt")

if file.stat().st_size == 0:
print("File is empty")
else:
print("File has content")

اگر فایل خالی باشد:
File is empty

🔹 exists()، is_file() و is_dir()
در پست‌های قبل با این متدها آشنا شدیم:
path.exists()
path.is_file()
path.is_dir()

می‌توانیم آن‌ها را برای تصمیم‌گیری درباره مسیر استفاده کنیم:
from pathlib import Path

path = Path("data/users.txt")

if not path.exists():
print("Path does not exist")
elif path.is_file():
print("This is a file")
elif path.is_dir():
print("This is a directory")

ترتیب این بررسی‌ها مهم است؛ ابتدا مطمئن می‌شویم مسیر واقعاً وجود دارد.
🔹 یک مثال کاربردی
فرض کنیم می‌خواهیم مشخصات فایل‌های متنی یک پوشه را نمایش دهیم:
from pathlib import Path

data = Path("data")

for file in data.glob("*.txt"):
info = file.stat()

print("Name:", file.name)
print("Size:", info.st_size, "bytes")
print("Modified:", info.st_mtime)
print()

در اینجا ابتدا فایل‌ها را با glob() پیدا می‌کنیم و سپس با stat() اطلاعات هر فایل را می‌گیریم.
⚠️ نکته مهم
stat() اطلاعات سیستم فایل را می‌خواند؛ بنابراین برای مسیری که وجود ندارد، معمولاً خطای FileNotFoundError دریافت می‌کنیم.
بهتر است قبل از استفاده، وجود مسیر را بررسی کنیم:
if file.exists():
info = file.stat()

🧠 خلاصه
path.stat()
→ دریافت اطلاعات فایل یا مسیر

st_size
→ اندازه فایل برحسب بایت

st_mtime
→ زمان آخرین تغییر

exists()
→ آیا مسیر وجود دارد؟

is_file()
→ آیا فایل است؟

is_dir()
→ آیا پوشه است؟

🎯 تمرین
برنامه‌ای بنویس که:
فایل‌های .txt داخل data را پیدا کند.
نام هر فایل را چاپ کند.
اندازه هر فایل را برحسب بایت چاپ کند.
زمان آخرین تغییر هر فایل را نمایش دهد.
پست ۱۴۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۸
مسیرهای مطلق و نسبی با pathlib
در پست‌های قبل بیشتر با مسیرهایی مثل:
Path("data/users.txt")

کار کردیم.
این نوع مسیر یک مسیر نسبی (Relative Path) است.
حالا باید تفاوت مسیر نسبی و مسیر مطلق (Absolute Path) را یاد بگیریم.
🔹 مسیر نسبی چیست؟
مسیر نسبی از پوشه کاری فعلی برنامه محاسبه می‌شود.
مثلاً:
from pathlib import Path

file = Path("data/users.txt")

print(file)

این مسیر می‌گوید:
پوشه فعلی
└── data
└── users.txt

اگر پوشه کاری برنامه تغییر کند، محل این فایل هم می‌تواند متفاوت باشد.
🔹 مسیر مطلق چیست؟
مسیر مطلق، آدرس کامل فایل یا پوشه را مشخص می‌کند.
مثلاً در Windows:
from pathlib import Path

file = Path("C:/Users/Ali/project/data/users.txt")

print(file.is_absolute())

خروجی:
True

مسیر مطلق دیگر به پوشه کاری فعلی وابسته نیست.
🔹 بررسی مطلق یا نسبی بودن مسیر
با:
path.is_absolute()

می‌توانیم بررسی کنیم مسیر مطلق است یا نه.
مثلاً:
from pathlib import Path

relative = Path("data/users.txt")
absolute = Path("C:/project/data/users.txt")

print(relative.is_absolute())
print(absolute.is_absolute())

خروجی:
False
True

🔹 تبدیل مسیر نسبی به مسیر مطلق
با resolve() می‌توانیم مسیر را به مسیر مطلق تبدیل کنیم:
from pathlib import Path

path = Path("data/users.txt")

absolute_path = path.resolve()

print(absolute_path)

مثلاً ممکن است خروجی چیزی شبیه این باشد:
C:\Users\Ali\project\data\users.txt

مسیر دقیق به محل اجرای برنامه بستگی دارد.
🔹 پیدا کردن پوشه کاری فعلی
می‌توانیم مسیر کاری فعلی را با:
Path.cwd()

ببینیم:
from pathlib import Path

current = Path.cwd()

print(current)

مثلاً:
C:\Users\Ali\project

این همان مسیری است که مسیرهای نسبی معمولاً نسبت به آن تفسیر می‌شوند.
🔹 ساخت مسیر نسبت به پوشه کاری
می‌توانیم مسیر فعلی را با مسیر دیگری ترکیب کنیم:
from pathlib import Path

base = Path.cwd()

file = base / "data" / "users.txt"

print(file)

⚠️ نکته مهم
مسیر نسبی همیشه بد نیست.
برای پروژه‌هایی که ساختار مشخصی دارند، مسیر نسبی می‌تواند بسیار مناسب باشد:
project/
├── main.py
└── data/
└── users.txt

مثلاً:
Path("data/users.txt")

خواناتر از یک مسیر مطلق طولانی است.
اما مسیر مطلق برای بعضی کاربردها لازم است؛ مخصوصاً زمانی که باید دقیقاً یک محل مشخص در سیستم فایل را مشخص کنیم.
🔥 مثال مقایسه‌ای
مسیر نسبی:
Path("data/users.txt")

مسیر مطلق:
Path("C:/Users/Ali/project/data/users.txt")

اولی به محیط اجرای برنامه وابسته است.
دومی محل دقیق فایل را مشخص می‌کند.
🧠 خلاصه
Relative Path
→ مسیر نسبت به پوشه کاری فعلی

Absolute Path
→ مسیر کامل و مستقل از پوشه کاری

Path.cwd()
→ مسیر کاری فعلی

is_absolute()
→ بررسی مطلق بودن مسیر

resolve()
→ تبدیل مسیر به مسیر مطلق

🎯 تمرین
کدی بنویس که:
مسیر نسبی data/users.txt را ایجاد کند.
مسیر کاری فعلی را چاپ کند.
مسیر را با resolve() به مسیر مطلق تبدیل کند.
بررسی کند مسیر مطلق است یا نه.
خروجی باید شامل این سه مورد باشد:
Current directory: ...
Absolute path: ...
Is absolute: True

پست ۱۴۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۴۹
مسیر والد و اجزای مسیر با pathlib
در پست‌های قبل با مسیر نسبی، مسیر مطلق و resolve() آشنا شدیم.
حالا می‌خواهیم یاد بگیریم چطور بخش‌های مختلف یک مسیر را جدا کنیم.
🔹 مسیر والد با parent
فرض کنیم:
from pathlib import Path

path = Path("project/data/users.txt")

print(path.parent)

خروجی:
project/data

parent پوشه‌ای را نشان می‌دهد که فایل یا مسیر موردنظر داخل آن قرار دارد.
🔹 چند سطح والد
می‌توانیم به والدهای بالاتر هم دسترسی داشته باشیم:
from pathlib import Path

path = Path("project/data/users.txt")

print(path.parent)
print(path.parent.parent)
print(path.parent.parent.parent)

خروجی:
project/data
project
.

در این مثال:
users.txt
↓
data
↓
project

🔹 نام فایل با name
برای دریافت نام کامل فایل:
from pathlib import Path

path = Path("project/data/users.txt")

print(path.name)

خروجی:
users.txt

🔹 نام بدون پسوند با stem
print(path.stem)

خروجی:
users

🔹 پسوند با suffix
print(path.suffix)

خروجی:
.txt

🔹 تمام پسوندها با suffixes
گاهی فایل چند پسوند دارد:
from pathlib import Path

path = Path("backup.tar.gz")

print(path.suffix)
print(path.suffixes)

خروجی:
.gz
['.tar', '.gz']

پس:
suffix   → آخرین پسوند
suffixes → تمام پسوندها

🔹 بخش‌های مسیر با parts
با parts می‌توانیم مسیر را به اجزای جداگانه تقسیم کنیم:
from pathlib import Path

path = Path("project/data/users.txt")

print(path.parts)

خروجی:
('project', 'data', 'users.txt')

هر بخش مسیر به‌صورت یک عضو جداگانه در اختیار ما قرار می‌گیرد.
🔥 مثال کاربردی
فرض کنیم کاربر یک مسیر فایل را وارد می‌کند:
from pathlib import Path

path = Path("project/data/report.pdf")

print("File:", path.name)
print("Folder:", path.parent)
print("Extension:", path.suffix)

خروجی:
File: report.pdf
Folder: project/data
Extension: .pdf

⚠️ نکته مهم
این ویژگی‌ها اطلاعات مسیر را استخراج می‌کنند؛ فایل یا پوشه‌ای ایجاد یا حذف نمی‌کنند.
مثلاً:
path.parent

فقط مسیر والد را به ما می‌دهد.
برای ساخت آن باید از:
path.parent.mkdir(parents=True, exist_ok=True)

استفاده کنیم.
🧠 خلاصه
parent   → پوشه والد
name → نام کامل فایل
stem → نام بدون پسوند
suffix → آخرین پسوند
suffixes → تمام پسوندها
parts → اجزای مسیر

مثلاً برای:
Path("project/data/report.tar.gz")

داریم:
parent  → project/data
name → report.tar.gz
stem → report.tar
suffix → .gz
suffixes → ['.tar', '.gz']

🎯 تمرین
مسیر زیر را در نظر بگیر:
path = Path("project/data/backups/database.sql.gz")

برنامه‌ای بنویس که موارد زیر را چاپ کند:
نام کامل فایل
پوشه والد
نام فایل
آخرین پسوند
تمام پسوندها
اجزای مسیر

پست ۱۴۹ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۰
تغییر نام و جایگزینی مسیرها با Path.rename() و Path.replace()
در پست‌های قبل با کپی، انتقال و حذف فایل‌ها آشنا شدیم.
حالا می‌خواهیم دو عملیات مهم دیگر را با pathlib یاد بگیریم:
rename()
replace()

🔹 تغییر نام فایل با rename()
فرض کنیم فایل زیر را داریم:
data/
└── old_name.txt

می‌توانیم نام آن را تغییر دهیم:
from pathlib import Path

file = Path("data/old_name.txt")

new_file = file.rename("data/new_name.txt")

print(new_file)

حالا ساختار فایل:
data/
└── new_name.txt

خواهد بود.
rename() یک شیء Path مربوط به مسیر جدید را برمی‌گرداند.
🔹 تغییر نام پوشه
rename() فقط برای فایل نیست.
می‌توانیم نام یک پوشه را هم تغییر دهیم:
from pathlib import Path

folder = Path("data/old")

new_folder = folder.rename("data/new")

print(new_folder)

حالا:
data/
└── new/

داریم.
🔹 انتقال فایل با rename()
rename() می‌تواند فایل را به مسیر دیگری نیز منتقل کند:
from pathlib import Path

file = Path("data/users.txt")

file.rename("backup/users.txt")

اگر پوشه backup از قبل وجود داشته باشد، فایل به آنجا منتقل می‌شود.
⚠️ rename() خودش پوشه مقصد را ایجاد نمی‌کند.
بنابراین اگر لازم است:
from pathlib import Path

file = Path("data/users.txt")
destination = Path("backup/users.txt")

destination.parent.mkdir(parents=True, exist_ok=True)

file.rename(destination)

🔹 replace() چیست؟
replace() شبیه rename() است، اما یک تفاوت مهم دارد:
اگر مسیر مقصد از قبل وجود داشته باشد، آن را جایگزین می‌کند.
from pathlib import Path

source = Path("data/new.txt")
destination = Path("data/current.txt")

source.replace(destination)

اگر current.txt وجود داشته باشد، با new.txt جایگزین می‌شود.
🔥 تفاوت rename() و replace()
به‌صورت ساده:
rename()
→ تغییر نام یا جابه‌جایی مسیر

replace()
→ تغییر مسیر + جایگزین کردن مقصد در صورت وجود

به همین دلیل replace() را باید با دقت بیشتری استفاده کرد.
🔹 مثال کاربردی: جایگزینی فایل تنظیمات
فرض کنیم برنامه یک فایل تنظیمات جدید ساخته است:
data/
├── config.json
└── config_new.json

می‌توانیم نسخه جدید را جایگزین نسخه فعلی کنیم:
from pathlib import Path

new_config = Path("data/config_new.json")
current_config = Path("data/config.json")

new_config.replace(current_config)

بعد از اجرا:
data/
└── config.json

نسخه جدید جایگزین فایل قبلی شده است.
⚠️ نکته مهم
قبل از استفاده از replace() باید بدانیم که مقصد ممکن است جایگزین شود.
مثلاً:
source.replace(destination)

اگر destination وجود داشته باشد، محتوای قبلی آن دیگر در همان مسیر باقی نمی‌ماند.
پس برای عملیات حساس، مسیر مقصد را با دقت مشخص کن.
🧠 خلاصه
file.rename(new_path)

برای:
تغییر نام
انتقال

و:
file.replace(new_path)

برای:
انتقال/تغییر نام
+
جایگزینی مقصد در صورت وجود

همچنین:
destination.parent.mkdir(
parents=True,
exist_ok=True
)

برای ساخت پوشه مقصد در صورت نیاز کاربرد دارد.
🎯 تمرین
ساختار زیر را در نظر بگیر:
project/
└── data/
├── old.txt
└── final.txt

برنامه‌ای بنویس که:
old.txt را به new.txt تغییر نام دهد.
یک فایل جدید به نام latest.txt ایجاد کند.
new.txt را با latest.txt جایگزین کند.
در پایان بررسی کند که latest.txt وجود دارد.
پست ۱۵۰ از دوره جامع Python 🐍
🐍 پروژه اول دوره Python
سیستم مدیریت اطلاعات کاربران با فایل‌ها
تبریک! 🎉
تا اینجا ۱۵۰ پست از دوره جامع Python را پشت سر گذاشتیم.
حالا وقت آن رسیده که چیزهایی که یاد گرفتیم را در یک پروژه واقعی استفاده کنیم.
📌 پروژه: سیستم مدیریت کاربران
یک برنامه بنویسید که بتواند اطلاعات کاربران را ذخیره، نمایش، جستجو و مدیریت کند.
اطلاعات هر کاربر:
نام
سن
ایمیل
شماره تماس

🎯 قابلیت‌های موردنیاز
1️⃣ ساخت پوشه پروژه
برنامه باید در اولین اجرا ساختار زیر را ایجاد کند:
UserManager/
│
├── data/
│ └── users.txt
│
└── backup/

اگر پوشه‌ها وجود نداشتند، برنامه باید خودش آن‌ها را بسازد.
2️⃣ اضافه کردن کاربر
کاربر بتواند اطلاعات جدید وارد کند:
مثال:
Name: Ali
Age: 25
Email: ali@example.com
Phone: 09123456789

و اطلاعات داخل فایل ذخیره شود.
3️⃣ نمایش تمام کاربران
برنامه باید اطلاعات ذخیره‌شده را از فایل بخواند و نمایش دهد.
مثال خروجی:
----------------
User 1

Name: Ali
Age: 25
Email: ali@example.com
Phone: 09123456789
----------------

4️⃣ جستجوی کاربر
کاربر بتواند با نام، کاربر موردنظر را پیدا کند.
مثلاً:
Search name: Ali

خروجی:
User found:
Ali
25
ali@example.com

5️⃣ حذف کاربر
امکان حذف یک کاربر وجود داشته باشد.
بعد از حذف، فایل اصلی باید با اطلاعات جدید ذخیره شود.
6️⃣ تهیه نسخه پشتیبان
یک گزینه برای Backup قرار دهید.
برنامه باید فایل:
users.txt

را به پوشه:
backup/

کپی کند.
📚 مباحثی که باید استفاده کنید
در این پروژه از مباحث زیر استفاده کنید:
✅ متغیرها
✅ شرط‌ها (if / elif / else)
✅ حلقه‌ها
✅ توابع
✅ لیست‌ها و دیکشنری‌ها
✅ کار با رشته‌ها
✅ مدیریت خطا (try/except)
✅ کار با فایل‌ها
✅ pathlib
✅ ساخت پوشه‌ها
✅ خواندن و نوشتن فایل
✅ کپی فایل‌ها با shutil
⭐️ امکانات اضافی (اختیاری)
اگر خواستید پروژه را حرفه‌ای‌تر کنید:
🔹 منوی متنی برای برنامه بسازید:
1. Add User
2. Show Users
3. Search User
4. Delete User
5. Backup
6. Exit

🔹 جلوگیری از ثبت ایمیل تکراری
🔹 بررسی معتبر بودن سن
🔹 ذخیره زمان ثبت کاربر
🔹 استفاده از JSON به جای فایل متنی
📌 قوانین ارسال پروژه
هنگام انجام پروژه:
کد را کامل بنویسید.
برای بخش‌های مهم توضیح کوتاه داخل کد قرار دهید.
اگر خطایی دریافت کردید، متن خطا و کد مربوطه را ارسال کنید.
منتظر پروژه‌های شما هستیم 🐍🔥
اولین پروژه عملی دوره جامع Python
1
🐍 آموزش جامع Python — پست ۱۵۱
کار با JSON در Python
در پروژه قبلی اطلاعات کاربران را داخل فایل ذخیره کردیم.
اما فایل متنی برای ذخیره اطلاعات ساختاریافته چندان مناسب نیست.
برای مثال، اگر بخواهیم اطلاعات یک کاربر را ذخیره کنیم:
Name: Ali
Age: 25
Email: ali@example.com

خواندن و مدیریت این اطلاعات به شکل دستی سخت‌تر می‌شود.
اینجاست که JSON کاربرد پیدا می‌کند.
🔹 JSON چیست؟
JSON یک قالب متنی استاندارد برای ذخیره و انتقال داده‌های ساختاریافته است.
مثلاً اطلاعات یک کاربر می‌تواند به این شکل باشد:
{
"name": "Ali",
"age": 25,
"email": "ali@example.com"
}

ساختار JSON شباهت زیادی به dict در Python دارد.
🔹 تبدیل Dictionary به JSON
برای کار با JSON از ماژول داخلی json استفاده می‌کنیم:
import json

user = {
"name": "Ali",
"age": 25,
"email": "ali@example.com"
}

data = json.dumps(user)

print(data)

خروجی:
{"name": "Ali", "age": 25, "email": "ali@example.com"}

dumps() داده Python را به رشته JSON تبدیل می‌کند.
🔹 خواناتر کردن JSON
می‌توانیم JSON را مرتب‌تر نمایش دهیم:
import json

user = {
"name": "Ali",
"age": 25,
"email": "ali@example.com"
}

data = json.dumps(user, indent=4)

print(data)

خروجی:
{
"name": "Ali",
"age": 25,
"email": "ali@example.com"
}

indent=4 باعث می‌شود خروجی خواناتر شود.
🔹 تبدیل JSON به Dictionary
برعکس dumps()، با loads() می‌توانیم یک رشته JSON را دوباره به Python تبدیل کنیم:
import json

text = '{"name": "Ali", "age": 25}'

user = json.loads(text)

print(user)
print(user["name"])

خروجی:
{'name': 'Ali', 'age': 25}
Ali

پس:
dict → json.dumps() → JSON string

JSON string → json.loads() → dict

⚠️ نکته مهم
JSON خودش یک dict پایتون نیست.
مثلاً:
user = {
"name": "Ali"
}

یک dict است.
اما:
text = '{"name": "Ali"}'

یک str است که محتوایش با قالب JSON نوشته شده.
می‌توانیم تفاوت آن‌ها را ببینیم:
print(type(user))
print(type(text))

خروجی:
<class 'dict'>
<class 'str'>

🧠 خلاصه
json.dumps()
Python object → JSON string

json.loads()
JSON string → Python object

در پست بعدی یاد می‌گیریم چطور JSON را مستقیماً داخل فایل ذخیره و دوباره از فایل بخوانیم.
🎯 تمرین
Dictionary زیر را به JSON تبدیل کن:
import json

product = {
"name": "Laptop",
"price": 1200,
"available": True
}

آن را با json.dumps() به JSON تبدیل کن و با indent=4 نمایش بده.
پست ۱۵۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۲
ذخیره و خواندن JSON از فایل
در پست قبل یاد گرفتیم چگونه بین dict و JSON تبدیل انجام دهیم.
اما در برنامه‌های واقعی معمولاً اطلاعات را داخل فایل ذخیره می‌کنیم تا بعداً دوباره بتوانیم از آن‌ها استفاده کنیم.
برای این کار از:
json.dump()
json.load()

استفاده می‌کنیم.
🔹 ذخیره Dictionary داخل فایل JSON
import json

user = {
"name": "Ali",
"age": 25,
"email": "ali@example.com"
}

with open("user.json", "w", encoding="utf-8") as file:
json.dump(user, file, indent=4)

بعد از اجرا فایل:
user.json

ساخته می‌شود.
🔹 خواندن JSON از فایل
import json

with open("user.json", "r", encoding="utf-8") as file:
user = json.load(file)

print(user)

بعد از load() داده دوباره به شکل dict در Python برمی‌گردد.
🎯 پاسخ کامل تمرین
ذخیره و خواندن محصولات با JSON
کد کامل:
import json

products = [
{
"name": "Laptop",
"price": 1200
},
{
"name": "Mouse",
"price": 25
}
]


# ذخیره محصولات داخل فایل JSON
with open("products.json", "w", encoding="utf-8") as file:
json.dump(
products,
file,
indent=4
)


# خواندن محصولات از فایل JSON
with open("products.json", "r", encoding="utf-8") as file:
loaded_products = json.load(file)


# نمایش نام محصولات
for product in loaded_products:
print(product["name"])

خروجی:
Laptop
Mouse

توضیح کد:
ابتدا لیست محصولات را ساختیم:
products = [...]

سپس با:
json.dump()

آن را داخل فایل:
products.json

ذخیره کردیم.
بعد با:
json.load()

اطلاعات فایل را دوباره خواندیم.
در آخر با حلقه:
for product in loaded_products:

روی تمام محصولات حرکت کردیم و نام آن‌ها را چاپ کردیم.
نسخه بهتر با مدیریت خطا:
import json

try:
with open("products.json", "r", encoding="utf-8") as file:
products = json.load(file)

for product in products:
print(product["name"])

except FileNotFoundError:
print("File not found")

except json.JSONDecodeError:
print("Invalid JSON file")

اینجا اگر:
فایل وجود نداشته باشد
ساختار JSON خراب باشد
برنامه به‌جای متوقف شدن، خطا را مدیریت می‌کند.
پست ۱۵۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۳
مدیریت فایل JSON با pathlib
در پست‌های قبل با json.dump() و json.load() آشنا شدیم.
حالا می‌خواهیم ترکیب:
pathlib + json

را استفاده کنیم تا مدیریت فایل‌های JSON حرفه‌ای‌تر شود.
🔹 باز کردن فایل JSON با Path
from pathlib import Path
import json

file = Path("data/users.json")

users = [
{
"name": "Ali",
"age": 25
}
]

with file.open("w", encoding="utf-8") as f:
json.dump(users, f, indent=4)

🎯 پاسخ کامل تمرین
ساخت فایل تنظیمات JSON
هدف:
ساخت مسیر:
app/
└── data/
└── settings.json

ذخیره تنظیمات:
{
"language": "fa",
"theme": "dark",
"notifications": True
}

و خواندن مقدار theme.
کد کامل:
from pathlib import Path
import json


# ساخت مسیر فایل
file = Path("app/data/settings.json")


# ساخت پوشه‌های والد
file.parent.mkdir(
parents=True,
exist_ok=True
)


# اطلاعات تنظیمات
settings = {
"language": "fa",
"theme": "dark",
"notifications": True
}


# ذخیره JSON داخل فایل
with file.open(
"w",
encoding="utf-8"
) as f:
json.dump(
settings,
f,
indent=4,
ensure_ascii=False
)


# خواندن فایل JSON
with file.open(
"r",
encoding="utf-8"
) as f:
loaded_settings = json.load(f)


# نمایش مقدار theme
print(loaded_settings["theme"])

خروجی:
dark

توضیح بخش‌های مهم:
ساخت مسیر:
file = Path("app/data/settings.json")

فایل را داخل پوشه مشخص قرار می‌دهد.
ساخت پوشه‌ها:
file.parent.mkdir(
parents=True,
exist_ok=True
)

اگر:
app
data

وجود نداشته باشند، آن‌ها را ایجاد می‌کند.
ذخیره اطلاعات:
json.dump(settings, f)

Dictionary پایتون را به فایل JSON تبدیل می‌کند.
خواندن اطلاعات:
json.load(f)

فایل JSON را دوباره به Dictionary تبدیل می‌کند.
دسترسی به مقدار:
loaded_settings["theme"]

مقدار کلید theme را برمی‌گرداند.
نسخه همراه با مدیریت خطا:
from pathlib import Path
import json


file = Path("app/data/settings.json")


try:
file.parent.mkdir(
parents=True,
exist_ok=True
)

settings = {
"language": "fa",
"theme": "dark",
"notifications": True
}

with file.open("w", encoding="utf-8") as f:
json.dump(
settings,
f,
indent=4,
ensure_ascii=False
)


with file.open("r", encoding="utf-8") as f:
data = json.load(f)


print(data["theme"])


except PermissionError:
print("Permission denied")

except json.JSONDecodeError:
print("Invalid JSON file")

پست ۱۵۳ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۴
مدیریت خطا هنگام کار با فایل‌های JSON
در پست‌های قبل یاد گرفتیم چگونه فایل‌های JSON را ذخیره و بخوانیم.
اما در برنامه‌های واقعی همیشه همه چیز طبق انتظار پیش نمی‌رود.
ممکن است:
فایل وجود نداشته باشد.
ساختار JSON خراب شده باشد.
دسترسی به فایل نداشته باشیم.
فایل خالی باشد.
برای جلوگیری از متوقف شدن برنامه از:
try
except

استفاده می‌کنیم.
🔹 خطای نبودن فایل
فرض کنیم می‌خواهیم یک فایل را بخوانیم:
import json

with open("settings.json", "r") as file:
data = json.load(file)

اگر فایل وجود نداشته باشد:
FileNotFoundError

دریافت می‌کنیم.
برای مدیریت:
import json

try:
with open("settings.json", "r") as file:
data = json.load(file)

except FileNotFoundError:
print("File does not exist")

حالا برنامه متوقف نمی‌شود.
🔹 JSON خراب
فرض کنیم فایل:
{
"name": "Ali",
"age": 25

باشد.
این JSON ناقص است.
در هنگام خواندن:
json.load(file)

خطای:
JSONDecodeError

ایجاد می‌شود.
مدیریت آن:
import json

try:
with open("user.json", "r") as file:
user = json.load(file)

except json.JSONDecodeError:
print("Invalid JSON format")

🔹 مدیریت چند خطا با هم
می‌توانیم چند خطا را جداگانه مدیریت کنیم:
import json

try:
with open("user.json", "r", encoding="utf-8") as file:
user = json.load(file)

except FileNotFoundError:
print("File not found")

except json.JSONDecodeError:
print("JSON is invalid")

except PermissionError:
print("No permission to access file")

هر خطا پیام مناسب خودش را دارد.
🔹 استفاده از else
در try/except می‌توانیم از else هم استفاده کنیم.
کد داخل else فقط زمانی اجرا می‌شود که خطایی رخ نداده باشد:
import json

try:
with open("user.json", "r") as file:
user = json.load(file)

except FileNotFoundError:
print("File not found")

else:
print(user)

🔹 استفاده از finally
گاهی می‌خواهیم بخشی از کد همیشه اجرا شود.
برای این کار از:
finally

استفاده می‌کنیم.
مثلاً:
try:
file = open("user.json", "r")

data = file.read()

except FileNotFoundError:
print("File not found")

finally:
print("Operation finished")

بخش finally چه خطا رخ بدهد چه ندهد اجرا می‌شود.
🔥 مثال کامل با pathlib
from pathlib import Path
import json


file = Path("data/settings.json")


try:
with file.open("r", encoding="utf-8") as f:
settings = json.load(f)

print(settings["theme"])


except FileNotFoundError:
print("Settings file not found")


except json.JSONDecodeError:
print("Invalid JSON data")


except KeyError:
print("Theme key does not exist")

اینجا سه مشکل مختلف مدیریت شده است:
FileNotFoundError
→ فایل وجود ندارد

JSONDecodeError
→ ساختار JSON خراب است

KeyError
→ کلید موردنظر وجود ندارد

🧠 خلاصه
FileNotFoundError
→ فایل پیدا نشد

JSONDecodeError
→ JSON خراب است

PermissionError
→ دسترسی وجود ندارد

KeyError
→ کلید Dictionary وجود ندارد

try/except
→ جلوگیری از توقف برنامه

🎯 تمرین
برنامه‌ای بنویس که:
فایل:
data/config.json

را بخواند.
مقدار:
{
"theme": "dark"
}

را دریافت کند.
خطاهای زیر را مدیریت کند:
نبودن فایل
خراب بودن JSON
نبودن کلید theme
خروجی موفق:
dark

پست ۱۵۴ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۵
ساخت سیستم تنظیمات برنامه با JSON
در پست‌های قبل یاد گرفتیم چگونه اطلاعات را در فایل JSON ذخیره کنیم و خطاهای مربوط به آن را مدیریت کنیم.
حالا می‌خواهیم از این قابلیت برای ساخت یک سیستم تنظیمات واقعی برنامه استفاده کنیم.
بسیاری از برنامه‌ها اطلاعاتی مثل:
زبان برنامه
حالت نمایش
تنظیمات کاربر
وضعیت فعال بودن قابلیت‌ها

را در یک فایل تنظیمات ذخیره می‌کنند.
🔹 ساختار پروژه
ساختار پیشنهادی:
MyApp/
│
├── main.py
│
└── config/
└── settings.json

فایل:
settings.json

تنظیمات برنامه را نگهداری می‌کند.
🔹 ایجاد فایل تنظیمات پیش‌فرض
ابتدا یک تابع برای ساخت تنظیمات ایجاد می‌کنیم:
from pathlib import Path
import json


config_file = Path("config/settings.json")


def create_default_config():

config_file.parent.mkdir(
parents=True,
exist_ok=True
)

default_settings = {
"language": "fa",
"theme": "dark",
"notifications": True
}

with config_file.open(
"w",
encoding="utf-8"
) as file:

json.dump(
default_settings,
file,
indent=4,
ensure_ascii=False
)

اگر پوشه یا فایل وجود نداشته باشد، ساخته می‌شود.
🔹 خواندن تنظیمات
حالا یک تابع برای خواندن تنظیمات:
def load_config():

with config_file.open(
"r",
encoding="utf-8"
) as file:

return json.load(file)

استفاده:
settings = load_config()

print(settings["theme"])

خروجی:
dark

🔹 تغییر تنظیمات
فرض کنیم کاربر می‌خواهد حالت نمایش را تغییر دهد:
def update_theme(theme):

settings = load_config()

settings["theme"] = theme

with config_file.open(
"w",
encoding="utf-8"
) as file:

json.dump(
settings,
file,
indent=4,
ensure_ascii=False
)

استفاده:
update_theme("light")

حالا فایل:
{
"language": "fa",
"theme": "light",
"notifications": true
}

خواهد بود.
🔹 بررسی وجود فایل تنظیمات
در برنامه واقعی بهتر است قبل از خواندن بررسی کنیم فایل وجود دارد یا نه:
if not config_file.exists():
create_default_config()

settings = load_config()

یعنی:
اگر تنظیمات وجود ندارد → ایجاد کن.
اگر وجود دارد → بخوان.
🔥 مثال کامل
from pathlib import Path
import json


config_file = Path("config/settings.json")


def create_config():

config_file.parent.mkdir(
parents=True,
exist_ok=True
)

settings = {
"language": "fa",
"theme": "dark",
"notifications": True
}

with config_file.open(
"w",
encoding="utf-8"
) as file:

json.dump(
settings,
file,
indent=4,
ensure_ascii=False
)


def load_config():

with config_file.open(
"r",
encoding="utf-8"
) as file:

return json.load(file)


if not config_file.exists():
create_config()


config = load_config()

print(json.dumps(
config,
indent=4,
ensure_ascii=False
))

خروجی:
{
"language": "fa",
"theme": "dark",
"notifications": true
}

⚠️ نکته مهم
فایل تنظیمات معمولاً اطلاعاتی مثل:
رمز عبور
کلید API
اطلاعات حساس
را نباید بدون رمزنگاری ذخیره کند.
JSON فقط یک روش ذخیره‌سازی است و امنیت داخلی ندارد.
🧠 خلاصه
JSON
→ ذخیره تنظیمات برنامه

Path
→ مدیریت مسیر فایل

exists()
→ بررسی وجود فایل

dump()
→ ذخیره تنظیمات

load()
→ خواندن تنظیمات

🎯 تمرین
یک سیستم تنظیمات ساده بسازید:
قابلیت‌ها:
اگر فایل:
config/app.json

وجود نداشت، آن را بسازد.
تنظیمات پیش‌فرض:
{
"username": "Guest",
"language": "fa",
"theme": "dark"
}

را ذخیره کند.
تنظیمات را بخواند.
مقدار username را چاپ کند.
خروجی:
Guest

پست ۱۵۵ از دوره جامع Python 🐍
1
security_toolkit.py
5.9 KB
Security Toolkit 🔐
جعبه‌ابزار پایتون برای کارهای پایه‌ای امنیتی: تولید و بررسی قدرت رمز عبور، هش کردن (SHA-256)، و رمزنگاری/رمزگشایی متقارن (AES via Fernet).
قابلیت‌ها
تولید رمز عبور تصادفی و امن (با secrets، نه random)
بررسی و امتیازدهی به قدرت یک رمز عبور
هش SHA-256 برای متن و فایل
رمزنگاری/رمزگشایی متن با کلید متقارن
نیازمندی‌ها (Requirements)
Python 3.8 یا بالاتر
کتابخانه cryptography
نصب:
pip install cryptography

نحوه اجرا
python security_toolkit.py

استفاده در کد خودتان
from security_toolkit import SecurityToolkit

toolkit = SecurityToolkit()
password = toolkit.generate_password(length=16)
key = toolkit.generate_key()
token = toolkit.encrypt_text("پیام محرمانه", key)

نکات مهم برای انتشار
کلید تولیدشده (generate_key) را هرگز داخل کد یا ریپازیتوری عمومی قرار ندهید؛ در متغیر محیطی (env) یا یک secret manager نگه دارید.
این ابزار برای یادگیری و محافظت از سیستم/داده‌های خودتان است، نه برای نفوذ به سیستم دیگران.
لایسنس پیشنهادی برای انتشار عمومی: MIT
🐍 آموزش جامع Python — پست ۱۵۶
به‌روزرسانی JSON با اعتبارسنجی نوع و مقدار
در پست قبل یاد گرفتیم چگونه یک تنظیم خاص را در فایل JSON تغییر دهیم.
اما یک مشکل وجود دارد:
ممکن است کاربر مقدار نامعتبر وارد کند.
مثلاً:
update_setting("theme", 123)

در حالی که theme باید یک رشته مثل:
dark
light

باشد.
پس قبل از ذخیره، باید کلید، نوع و مقدار را بررسی کنیم.
🔹 تعریف قوانین تنظیمات
می‌توانیم قوانین مجاز را مشخص کنیم:
settings_rules = {
"username": {
"type": str
},
"language": {
"type": str,
"values": {"fa", "en"}
},
"theme": {
"type": str,
"values": {"dark", "light"}
}
}

یعنی:
username → باید str باشد

language → باید str باشد و فقط fa یا en باشد

theme → باید str باشد و فقط dark یا light باشد

🔹 اعتبارسنجی نوع
ابتدا نوع مقدار را بررسی می‌کنیم:
if not isinstance(value, rule["type"]):
print("Invalid type")
return

مثلاً:
update_setting("theme", 123)

مقدار 123 از نوع int است، در حالی که theme باید str باشد.
پس:
Invalid type

نمایش داده می‌شود.
🔹 اعتبارسنجی مقدار
اگر تنظیم دارای مقادیر مجاز باشد:
if "values" in rule and value not in rule["values"]:
print("Invalid value")
return

مثلاً:
update_setting("theme", "blue")

چون blue در مقادیر مجاز نیست:
Invalid value

نمایش داده می‌شود.
🔥 مثال کامل
from pathlib import Path
import json


file = Path("config/app.json")


settings_rules = {
"username": {
"type": str
},
"language": {
"type": str,
"values": {"fa", "en"}
},
"theme": {
"type": str,
"values": {"dark", "light"}
}
}


def update_setting(key, value):

# بررسی وجود کلید
if key not in settings_rules:
print("Invalid setting")
return

rule = settings_rules[key]

# بررسی نوع داده
if not isinstance(value, rule["type"]):
print("Invalid type")
return

# بررسی مقدار مجاز
if "values" in rule and value not in rule["values"]:
print("Invalid value")
return

# بررسی وجود فایل
if not file.exists():
print("Config file not found")
return

try:
# خواندن تنظیمات
with file.open("r", encoding="utf-8") as f:
settings = json.load(f)

# تغییر مقدار
settings[key] = value

# ذخیره دوباره
with file.open("w", encoding="utf-8") as f:
json.dump(
settings,
f,
indent=4,
ensure_ascii=False
)

print("Setting updated")

except json.JSONDecodeError:
print("Invalid JSON file")

except PermissionError:
print("Permission denied")

🔹 مثال‌های معتبر
update_setting("theme", "light")

خروجی:
Setting updated

یا:
update_setting("language", "en")

خروجی:
Setting updated

🔹 مثال‌های نامعتبر
نوع اشتباه:
update_setting("theme", 123)

خروجی:
Invalid type

مقدار نامعتبر:
update_setting("theme", "blue")

خروجی:
Invalid value

کلید نامعتبر:
update_setting("color", "red")

خروجی:
Invalid setting

🧠 خلاصه
قبل از تغییر تنظیمات، سه مرحله مهم داریم:
کلید
↓
آیا تنظیم مجاز است؟

نوع
↓
آیا نوع داده درست است؟

مقدار
↓
آیا مقدار مجاز است؟

بعد از عبور از این سه مرحله:
JSON
↓
load()
↓
تغییر مقدار
↓
dump()
↓
JSON

این الگو در برنامه‌های واقعی برای جلوگیری از ورود داده‌های نامعتبر بسیار کاربردی است.
🎯 تمرین
قانون جدید زیر را به settings_rules اضافه کن:
"notifications": {
"type": bool
}

سپس این موارد را امتحان کن:
update_setting("notifications", True)
update_setting("notifications", False)
update_setting("notifications", "yes")

دو مورد اول باید موفق باشند و مورد سوم باید:
Invalid type

نمایش دهد.
پست ۱۵۶ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۷
اعتبارسنجی ساختارهای تو‌در‌تو در JSON
در پست قبل یاد گرفتیم نوع و مقدار تنظیمات را بررسی کنیم.
اما داده‌های JSON همیشه ساده نیستند؛ گاهی شامل لیست، دیکشنری و ساختارهای تو‌در‌تو هستند.
مثلاً:
settings = {
"user": {
"name": "Ali",
"age": 25
},
"theme": "dark"
}

اینجا user خودش یک دیکشنری است.
🔹 بررسی کلیدهای اجباری
گاهی بعضی کلیدها باید حتماً وجود داشته باشند.
مثلاً برای اطلاعات کاربر، name و age را اجباری در نظر می‌گیریم:
user = {
"name": "Ali",
"age": 25
}

required_keys = ["name", "age"]

for key in required_keys:
if key not in user:
print(f"Missing key: {key}")

اگر هر دو کلید وجود داشته باشند، چیزی چاپ نمی‌شود.
اما اگر:
user = {
"name": "Ali"
}

خروجی:
Missing key: age

🔹 بررسی وجود کلید اصلی
برای ساختارهای تو‌در‌تو، ابتدا باید کلید اصلی را بررسی کنیم:
if "user" not in settings:
print("Missing user")

بعد نوع آن را بررسی می‌کنیم:
user = settings.get("user")

if not isinstance(user, dict):
print("Invalid user")

سپس می‌توانیم کلیدهای اجباری داخل آن را بررسی کنیم.
🔹 ترکیب کلیدهای اجباری و نوع داده
settings = {
"user": {
"name": "Ali",
"age": 25
}
}

user = settings.get("user")

required_keys = ["name", "age"]

if not isinstance(user, dict):
print("Invalid user")

elif not all(key in user for key in required_keys):
print("Missing required key")

elif not isinstance(user["name"], str):
print("Invalid name")

elif not isinstance(user["age"], int):
print("Invalid age")

else:
print("User data is valid")

خروجی:
User data is valid

🔹 all() چه کاری انجام می‌دهد؟
در این قسمت:
all(key in user for key in required_keys)

بررسی می‌کنیم که تمام کلیدهای اجباری وجود داشته باشند.
مثلاً:
required_keys = ["name", "age"]

user = {
"name": "Ali",
"age": 25
}

نتیجه:
True

اما اگر age وجود نداشته باشد:
False

⚠️ نکته مهم
بین این دو مفهوم تفاوت وجود دارد:
کلید وجود ندارد:
"user" not in data

کلید وجود دارد اما مقدار آن اشتباه است:
"user" in data

ولی:
not isinstance(data["user"], dict)

پس اعتبارسنجی بهتر است مرحله‌به‌مرحله انجام شود:
JSON
↓
کلید اصلی وجود دارد؟
↓
نوع مقدار درست است؟
↓
کلیدهای اجباری وجود دارند؟
↓
نوع مقادیر داخلی درست است؟
↓
مقادیر مجاز هستند؟

🔹 مثال با لیست کاربران
data = {
"users": [
{"name": "Ali", "age": 25},
{"name": "Sara", "age": 22}
]
}

users = data.get("users")

if isinstance(users, list):

for user in users:

if not isinstance(user, dict):
print("Invalid user")
continue

if not all(key in user for key in ["name", "age"]):
print("Missing required key")
continue

print(user["name"])

خروجی:
Ali
Sara

🧠 خلاصه
در اعتبارسنجی JSON می‌توانیم بررسی کنیم:
کلید اصلی وجود دارد؟
نوع داده درست است؟
کلیدهای اجباری وجود دارند؟
نوع مقادیر داخلی درست است؟
مقدار مجاز است؟

کلیدهای اجباری برای جلوگیری از داده‌های ناقص بسیار مهم هستند.
🎯 تمرین
داده زیر را اعتبارسنجی کن:
data = {
"user": {
"name": "Ali",
"age": 25
}
}

شرایط:
user → باید dict باشد
name → کلید اجباری و باید str باشد
age → کلید اجباری و باید int باشد

اگر همه درست بودند:
User data is valid

در غیر این صورت:
Invalid user data

پست ۱۵۷ از دوره جامع Python 🐍