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 — پست ۱۵۱
کار با 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 🐍
🐍 آموزش جامع Python — پست ۱۵۸
اعتبارسنجی لیست‌ها و تعداد اعضا در JSON
در پست قبل یاد گرفتیم چطور کلیدهای اجباری و نوع داده‌های تو‌در‌تو را بررسی کنیم.
حالا یک مرحله جلوتر می‌رویم و یاد می‌گیریم چطور لیست‌ها و تعداد اعضای آن‌ها را اعتبارسنجی کنیم.
🔹 بررسی اینکه مقدار یک لیست است
فرض کنیم JSON ما شامل لیستی از کاربران است:
data = {
"users": [
{"name": "Ali", "age": 25},
{"name": "Sara", "age": 22}
]
}

ابتدا نوع users را بررسی می‌کنیم:
users = data.get("users")

if isinstance(users, list):
print("Users is valid")
else:
print("Invalid users")

🔹 بررسی خالی نبودن لیست
گاهی وجود لیست کافی نیست و باید حداقل یک عضو داشته باشد:
if isinstance(users, list) and len(users) > 0:
print("Users list is not empty")

یا به شکل کوتاه‌تر:
if users:
print("Users list is not empty")

اما روش اول واضح‌تر است، چون مشخص می‌کند دقیقاً چه چیزی را بررسی می‌کنیم.
🔹 تعیین حداقل تعداد اعضا
مثلاً می‌خواهیم حداقل ۲ کاربر وجود داشته باشد:
if isinstance(users, list) and len(users) >= 2:
print("Valid users")
else:
print("Not enough users")

اگر فقط یک کاربر داشته باشیم:
users = [
{"name": "Ali", "age": 25}
]

خروجی:
Not enough users

🔹 تعیین حداکثر تعداد اعضا
گاهی تعداد اعضا نباید بیشتر از یک مقدار مشخص باشد:
if isinstance(users, list) and len(users) <= 100:
print("Users count is valid")
else:
print("Too many users")

پس می‌توانیم یک محدوده تعیین کنیم:
if isinstance(users, list) and 1 <= len(users) <= 100:
print("Valid users")

یعنی:
حداقل ۱ عضو
حداکثر ۱۰۰ عضو

🔹 اعتبارسنجی اعضای لیست
فقط تعداد اعضا کافی نیست.
هر عضو هم باید ساختار درستی داشته باشد:
for user in users:

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

if "name" not in user or "age" not in user:
print("Missing required key")
continue

print("Valid user")

اینجا برای هر کاربر بررسی می‌کنیم:
آیا dict است؟
آیا name دارد؟
آیا age دارد؟

🔥 ترکیب همه بررسی‌ها
data = {
"users": [
{"name": "Ali", "age": 25},
{"name": "Sara", "age": 22}
]
}

users = data.get("users")

if not isinstance(users, list):
print("Invalid users")

elif not 1 <= len(users) <= 100:
print("Invalid users count")

else:
valid = True

for user in users:

if not isinstance(user, dict):
valid = False
break

if not all(key in user for key in ["name", "age"]):
valid = False
break

if not isinstance(user["name"], str):
valid = False
break

if not isinstance(user["age"], int):
valid = False
break

if valid:
print("Users data is valid")
else:
print("Invalid user data")

خروجی:
Users data is valid

⚠️ نکته مهم
در اعتبارسنجی یک لیست، معمولاً چند مرحله داریم:
لیست وجود دارد؟
↓
نوع آن list است؟
↓
تعداد اعضا مجاز است؟
↓
هر عضو ساختار درستی دارد؟
↓
کلیدهای اجباری وجود دارند؟
↓
نوع مقادیر درست است؟

این الگو در کار با JSONهای واقعی بسیار کاربردی است.
🧠 خلاصه
isinstance(users, list)

بررسی نوع لیست.
len(users)

تعداد اعضای لیست.
1 <= len(users) <= 100

تعیین محدوده مجاز.
و با for می‌توانیم تک‌تک اعضای لیست را اعتبارسنجی کنیم.
🎯 تمرین
داده زیر را اعتبارسنجی کن:
data = {
"products": [
{"name": "Laptop", "price": 1200},
{"name": "Mouse", "price": 25}
]
}

قوانین:
products → باید list باشد
تعداد محصولات → بین 1 تا 50
هر محصول → باید dict باشد
name و price → کلیدهای اجباری
name → باید str باشد
price → باید int یا float باشد

اگر همه موارد درست بودند:
Products are valid

در غیر این صورت:
Invalid products

پست ۱۵۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۵۹
اعتبارسنجی مقدارهای عددی در JSON
در پست قبل ساختار لیست‌ها و تعداد اعضای آن‌ها را بررسی کردیم.
حالا می‌خواهیم علاوه بر نوع داده، خودِ محدوده و مقدار عددی را هم اعتبارسنجی کنیم.
🔹 بررسی حداقل و حداکثر مقدار
فرض کنیم اطلاعات یک محصول را داریم:
product = {
"name": "Laptop",
"price": 1200,
"stock": 15
}

می‌توانیم مشخص کنیم قیمت باید بین 1 تا 10000 باشد:
price = product.get("price")

if isinstance(price, (int, float)) and 1 <= price <= 10000:
print("Valid price")
else:
print("Invalid price")

خروجی:
Valid price

🔹 اعتبارسنجی تعداد موجودی
مثلاً موجودی نباید منفی باشد:
stock = product.get("stock")

if isinstance(stock, int) and stock >= 0:
print("Valid stock")
else:
print("Invalid stock")

پس:
stock = 15   → معتبر
stock = 0 → معتبر
stock = -5 → نامعتبر

🔹 چرا ابتدا نوع را بررسی می‌کنیم؟
این روش مناسب نیست:
if product["price"] > 0:
print("Valid")

چون اگر مقدار price رشته باشد:
price = "1200"

مقایسه با عدد می‌تواند باعث خطا شود.
بهتر است ابتدا نوع را بررسی کنیم:
price = product.get("price")

if isinstance(price, (int, float)) and price > 0:
print("Valid")

به دلیل Short-Circuit، اگر isinstance() برابر False باشد، بخش دوم بررسی نمی‌شود.
🔹 تعیین محدوده برای چند مقدار
product = {
"name": "Laptop",
"price": 1200,
"stock": 15,
"discount": 20
}

price = product.get("price")
stock = product.get("stock")
discount = product.get("discount")

if not isinstance(price, (int, float)) or not 1 <= price <= 10000:
print("Invalid price")

elif not isinstance(stock, int) or not 0 <= stock <= 1000:
print("Invalid stock")

elif not isinstance(discount, (int, float)) or not 0 <= discount <= 100:
print("Invalid discount")

else:
print("Product data is valid")

خروجی:
Product data is valid

⚠️ یک نکته مهم درباره bool
در Python:
isinstance(True, int)

نتیجه:
True

چون bool زیرکلاسی از int است.
بنابراین اگر می‌خواهیم مقدار واقعاً عددی باشد و True یا False پذیرفته نشود، بهتر است بررسی دقیق‌تری انجام دهیم:
if isinstance(price, (int, float)) and not isinstance(price, bool):
print("Numeric")

🔹 اعتبارسنجی مقدارهای اعشاری
مثلاً درصد تخفیف:
discount = 25.5

if (
isinstance(discount, (int, float))
and not isinstance(discount, bool)
and 0 <= discount <= 100
):
print("Valid discount")

مقدارهای:
0      → معتبر
25.5 → معتبر
100 → معتبر
-5 → نامعتبر
120 → نامعتبر

🧠 الگوی کلی
برای یک مقدار عددی معمولاً این ترتیب را داریم:
نوع داده
↓
عدد بودن واقعی
↓
حداقل مقدار
↓
حداکثر مقدار

مثلاً:
isinstance(value, (int, float))

و سپس:
min_value <= value <= max_value

🎯 تمرین
داده زیر را اعتبارسنجی کن:
product = {
"name": "Phone",
"price": 850,
"stock": 20,
"discount": 15
}

قوانین:
name     → باید str باشد
price → عدد بین 1 تا 10000
stock → عدد صحیح بین 0 تا 1000
discount → عدد بین 0 تا 100

اگر همه معتبر بودند:
Product is valid

در غیر این صورت:
Invalid product

پست ۱۵۹ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۶۰
اعتبارسنجی رشته‌ها در JSON
در پست قبل مقدارهای عددی مثل price، stock و discount را اعتبارسنجی کردیم.
حالا نوبت رشته‌ها (str) است.
در داده‌های واقعی معمولاً لازم است بررسی کنیم:
مقدار واقعاً رشته باشد.
خالی نباشد.
طول آن در محدوده مشخص باشد.
فقط شامل مقدارهای مجاز باشد.
🔹 بررسی نوع رشته
name = "Laptop"

if isinstance(name, str):
print("Valid name")
else:
print("Invalid name")

🔹 جلوگیری از رشته خالی
این مقدار:
name = ""

رشته است، اما ممکن است برای برنامه ما معتبر نباشد.
if isinstance(name, str) and name.strip():
print("Valid name")
else:
print("Invalid name")

strip() فاصله‌های ابتدا و انتهای متن را حذف می‌کند.
بنابراین:
"Ali"     → معتبر
" Ali " → معتبر
"" → نامعتبر
" " → نامعتبر

🔹 بررسی طول رشته
مثلاً نام کاربر باید بین ۳ تا ۳۰ کاراکتر باشد:
username = "Ali"

if (
isinstance(username, str)
and 3 <= len(username.strip()) <= 30
):
print("Valid username")
else:
print("Invalid username")

🔹 محدود کردن مقدار به گزینه‌های مشخص
گاهی رشته باید فقط یکی از چند مقدار مشخص باشد.
مثلاً زبان برنامه:
language = "fa"

allowed_languages = {"fa", "en", "ar"}

if language in allowed_languages:
print("Valid language")
else:
print("Invalid language")

خروجی:
Valid language

اگر:
language = "de"

باشد:
Invalid language

🔥 ترکیب چند قانون
فرض کنیم اطلاعات محصول را داریم:
product = {
"name": "Laptop",
"category": "electronics"
}

می‌خواهیم:
name     → رشته و غیرخالی
category → یکی از گزینه‌های مجاز

name = product.get("name")
category = product.get("category")

allowed_categories = {
"electronics",
"clothing",
"books"
}

if not isinstance(name, str) or not name.strip():
print("Invalid name")

elif category not in allowed_categories:
print("Invalid category")

else:
print("Product strings are valid")

خروجی:
Product strings are valid

⚠️ نکته مهم
در اعتبارسنجی رشته‌ها، isinstance() به‌تنهایی کافی نیست.
مثلاً:
value = ""

این مقدار:
isinstance(value, str)

را True می‌کند، اما ممکن است از نظر برنامه نامعتبر باشد.
پس باید قوانین مربوط به خود مقدار را هم بررسی کنیم:
نوع
↓
خالی نبودن
↓
طول
↓
مقدارهای مجاز

🧠 خلاصه
isinstance(value, str)

بررسی نوع.
value.strip()

بررسی اینکه رشته فقط شامل فاصله نباشد.
len(value)

بررسی طول.
value in allowed_values

بررسی مقدارهای مجاز.
🎯 تمرین
داده زیر را اعتبارسنجی کن:
user = {
"username": "Ali",
"language": "fa",
"theme": "dark"
}

قوانین:
username → باید str و غیرخالی باشد
username → طول بین 3 تا 20 کاراکتر
language → فقط "fa" یا "en"
theme → فقط "dark" یا "light"

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

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

پست ۱۶۰ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۶۱
اعتبارسنجی Boolean و مقادیر True و False در JSON
در پست‌های قبل یاد گرفتیم مقدارهای عددی و رشته‌ای را اعتبارسنجی کنیم.
حالا می‌خواهیم مقدارهای Boolean را بررسی کنیم؛ مثل:
True
False

در JSON هم Boolean فقط با این دو مقدار نمایش داده می‌شود:
true
false

🔹 بررسی نوع Boolean
برای اینکه مطمئن شویم مقدار واقعاً bool است:
notifications = True

if isinstance(notifications, bool):
print("Valid")
else:
print("Invalid")

خروجی:
Valid

🔹 چرا bool() کافی نیست؟
این کار برای اعتبارسنجی مناسب نیست:
value = "yes"

if bool(value):
print("Valid")

خروجی:
Valid

چون "yes" یک رشته غیرخالی و Truthy است.
اما مقدار ما باید واقعاً Boolean باشد.
بنابراین:
isinstance(value, bool)

روش مناسب‌تری است.
🔹 مثال در تنظیمات برنامه
فرض کنیم:
settings = {
"notifications": True,
"dark_mode": False
}

می‌توانیم هر دو مقدار را بررسی کنیم:
notifications = settings.get("notifications")
dark_mode = settings.get("dark_mode")

if not isinstance(notifications, bool):
print("Invalid notifications")

elif not isinstance(dark_mode, bool):
print("Invalid dark_mode")

else:
print("Settings are valid")

خروجی:
Settings are valid

⚠️ 0 و 1 را با Boolean اشتباه نگیریم
در Python:
isinstance(1, bool)

نتیجه:
False

و:
isinstance(0, bool)

هم:
False

بنابراین اگر قانون ما این باشد که مقدار باید واقعاً Boolean باشد:
value = 1

if isinstance(value, bool):
print("Valid")
else:
print("Invalid")

خروجی:
Invalid

🔹 اعتبارسنجی چند تنظیم
settings = {
"notifications": True,
"dark_mode": False,
"auto_save": True
}

required_keys = [
"notifications",
"dark_mode",
"auto_save"
]

valid = True

for key in required_keys:

if key not in settings:
valid = False
break

if not isinstance(settings[key], bool):
valid = False
break

if valid:
print("Settings are valid")
else:
print("Invalid settings")

در این مثال هم وجود کلید و هم نوع مقدار بررسی می‌شود.
🧠 الگوی اعتبارسنجی Boolean
برای یک مقدار Boolean:
کلید وجود دارد؟
↓
مقدار bool است؟
↓
True یا False

برخلاف رشته یا عدد، برای Boolean معمولاً محدوده یا طول نداریم؛ مهم این است که مقدار واقعاً از نوع bool باشد.
🎯 تمرین
داده زیر را اعتبارسنجی کن:
settings = {
"notifications": True,
"dark_mode": False,
"auto_update": True
}

قوانین:
notifications → کلید اجباری و bool
dark_mode → کلید اجباری و bool
auto_update → کلید اجباری و bool

اگر همه معتبر بودند:
Settings are valid

در غیر این صورت:
Invalid settings

پست ۱۶۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۶۲
اعتبارسنجی None و مقدارهای اختیاری در JSON
در پست قبل مقدارهای True و False را اعتبارسنجی کردیم.
حالا می‌خواهیم با یک حالت مهم دیگر آشنا شویم:
None

در JSON، مقدار معادل None با null نمایش داده می‌شود:
null

🔹 None چیست؟
None یعنی در Python مقداری وجود ندارد.
مثلاً:
email = None

print(email)

خروجی:
None

برای بررسی آن بهتر است از is استفاده کنیم:
if email is None:
print("Email is not set")

🔹 تفاوت None با رشته خالی
این دو مقدار یکسان نیستند:
email1 = None
email2 = ""

None یعنی مقدار وجود ندارد، اما "" یک رشته خالی است.
مثلاً:
print(email1 is None)
print(email2 is None)

خروجی:
True
False

🔹 مقدار اختیاری
گاهی یک فیلد اجباری نیست.
مثلاً:
user = {
"username": "Ali",
"email": None
}

در اینجا username اجباری است، اما email می‌تواند None باشد.
می‌توانیم این حالت را بررسی کنیم:
email = user.get("email")

if email is None:
print("Email is optional")
elif isinstance(email, str):
print("Valid email")
else:
print("Invalid email")

🔹 None یا مقدار معتبر؟
فرض کنیم age اختیاری است، اما اگر مقدار داشته باشد باید int باشد:
age = user.get("age")

if age is None:
print("Age is not provided")

elif isinstance(age, int) and not isinstance(age, bool):
print("Valid age")

else:
print("Invalid age")

پس دو حالت معتبر داریم:
None       → معتبر، چون اختیاری است
25 → معتبر
"25" → نامعتبر
True → نامعتبر

⚠️ تفاوت get() با بررسی کلید
این دو حالت را دقت کن:
user = {
"username": "Ali"
}

اگر بنویسیم:
print(user.get("email"))

نتیجه:
None

اما نمی‌دانیم آیا:
کلید email وجود ندارد؟
یا کلید وجود دارد و مقدارش واقعاً None است؟
برای تشخیص این دو حالت:
if "email" not in user:
print("Email key is missing")

elif user["email"] is None:
print("Email is explicitly None")

این تفاوت در اعتبارسنجی JSON مهم است.
🔥 مثال کامل
user = {
"username": "Ali",
"email": None,
"age": 25
}

if "username" not in user:
print("Missing username")

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

elif "email" in user and user["email"] is not None:
if not isinstance(user["email"], str):
print("Invalid email")
else:
print("User is valid")

else:
print("User is valid")

در این مثال:
username → اجباری
email → اختیاری
age → می‌تواند بررسی جداگانه داشته باشد

🧠 خلاصه
Python       JSON
None → null
True → true
False → false

برای بررسی None:
value is None

و:
value is not None

همچنین باید تفاوت این سه حالت را بدانیم:
کلید وجود ندارد
کلید وجود دارد و مقدار None است
کلید وجود دارد و مقدار واقعی دارد

🎯 تمرین
داده زیر را اعتبارسنجی کن:
user = {
"username": "Ali",
"phone": None
}

قوانین:
username → اجباری و باید str باشد
phone → اختیاری
phone → اگر None بود، معتبر است
phone → اگر مقدار داشت، باید str باشد

اگر معتبر بود:
User is valid

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

پست ۱۶۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۶۳
ساخت Validator قابل استفاده مجدد برای JSON
تا اینجا یاد گرفتیم بخش‌های مختلف JSON را جداگانه اعتبارسنجی کنیم:
کلیدهای اجباری
نوع داده
مقدارهای مجاز
محدوده عددی
رشته‌ها
bool
None
لیست‌ها و داده‌های تو‌در‌تو
اما اگر برای هر فایل JSON این بررسی‌ها را دوباره بنویسیم، کد تکراری و سخت‌نگهداری می‌شود.
راه بهتر، ساختن یک تابع قابل استفاده مجدد برای اعتبارسنجی است.
🔹 یک Validator ساده
فرض کنیم مشخصات محصول این است:
product = {
"name": "Laptop",
"price": 1200
}

می‌توانیم یک تابع بسازیم:
def validate_product(product):

if not isinstance(product, dict):
return False

if "name" not in product:
return False

if "price" not in product:
return False

if not isinstance(product["name"], str):
return False

if not isinstance(product["price"], (int, float)):
return False

if isinstance(product["price"], bool):
return False

if not 1 <= product["price"] <= 10000:
return False

return True

حالا:
if validate_product(product):
print("Product is valid")
else:
print("Invalid product")

خروجی:
Product is valid

🔹 چرا تابع بهتر است؟
اگر چند محصول داشته باشیم:
products = [
{"name": "Laptop", "price": 1200},
{"name": "Mouse", "price": 25},
{"name": "Phone", "price": 850}
]

می‌توانیم همان Validator را دوباره استفاده کنیم:
for product in products:

if validate_product(product):
print("Valid")
else:
print("Invalid")

بدون اینکه قوانین اعتبارسنجی را برای هر محصول دوباره بنویسیم.
🔹 برگرداندن دلیل خطا
گاهی True یا False کافی نیست.
مثلاً بهتر است بدانیم چرا داده نامعتبر است.
def validate_product(product):

if not isinstance(product, dict):
return "Product must be a dictionary"

if "name" not in product:
return "Missing name"

if "price" not in product:
return "Missing price"

if not isinstance(product["name"], str):
return "Invalid name"

price = product["price"]

if (
not isinstance(price, (int, float))
or isinstance(price, bool)
):
return "Invalid price type"

if not 1 <= price <= 10000:
return "Invalid price range"

return None

استفاده:
error = validate_product(product)

if error is None:
print("Product is valid")
else:
print(error)

⚠️ چرا None؟
اینجا:
return None

یعنی هیچ خطایی وجود ندارد.
پس:
None              → معتبر
"Missing name" → نامعتبر
"Invalid price" → نامعتبر

این الگو در Validatorهای ساده بسیار کاربردی است.
🔥 مثال با چند محصول
products = [
{"name": "Laptop", "price": 1200},
{"name": "Mouse"},
{"name": "Phone", "price": True}
]

for product in products:

error = validate_product(product)

if error is None:
print("Valid product")
else:
print(f"Invalid product: {error}")

خروجی:
Valid product
Invalid product: Missing price
Invalid product: Invalid price type

🧠 خلاصه
به‌جای تکرار قوانین:
JSON
↓
Validator
↓
اعتبارسنجی
↓
نتیجه

Validator می‌تواند:
True / False

یا اطلاعات دقیق‌تری مثل:
None / Error Message

برگرداند.
🎯 تمرین
یک تابع به نام validate_user() بنویس:
def validate_user(user):
# کد اینجا

قوانین:
user → باید dict باشد
username → اجباری و str
age → اجباری و int
age → بین 0 تا 120

اگر معتبر بود:
None

برگرداند.
اگر نامعتبر بود، دلیل خطا را به‌صورت یک رشته برگرداند.
مثلاً:
Missing username
Invalid age
Invalid age range

پست ۱۶۳ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۶۴
جمع‌آوری چند خطای اعتبارسنجی هم‌زمان
در پست قبل Validator ساختیم که هنگام پیدا کردن اولین خطا، همان خطا را برمی‌گرداند.
اما یک مشکل وجود دارد:
اگر داده چند خطا داشته باشد، کاربر فقط اولین خطا را می‌بیند.
مثلاً:
user = {
"username": 123,
"age": 150
}

اینجا هم username اشتباه است و هم age.
بهتر است بتوانیم همه خطاها را یکجا جمع کنیم.
🔹 ساخت لیست خطاها
def validate_user(user):

errors = []

if not isinstance(user, dict):
errors.append("User must be a dictionary")
return errors

if "username" not in user:
errors.append("Missing username")

if "age" not in user:
errors.append("Missing age")

return errors

حالا:
user = {
"username": 123,
"age": 150
}

errors = validate_user(user)

print(errors)

فعلاً خروجی خالی است، چون هنوز نوع و محدوده را بررسی نکرده‌ایم.
🔹 اضافه کردن قوانین بیشتر
def validate_user(user):

errors = []

if not isinstance(user, dict):
errors.append("User must be a dictionary")
return errors

if "username" not in user:
errors.append("Missing username")

elif not isinstance(user["username"], str):
errors.append("Invalid username type")

if "age" not in user:
errors.append("Missing age")

elif not isinstance(user["age"], int) or isinstance(user["age"], bool):
errors.append("Invalid age type")

elif not 0 <= user["age"] <= 120:
errors.append("Invalid age range")

return errors

حالا:
user = {
"username": 123,
"age": 150
}

errors = validate_user(user)

for error in errors:
print(error)

خروجی:
Invalid username type
Invalid age range

هر دو خطا گزارش شدند.
🔹 بررسی معتبر بودن داده
اگر لیست خطاها خالی باشد:
if not errors:
print("User is valid")
else:
print("User is invalid")

مثلاً:
user = {
"username": "Ali",
"age": 25
}

errors = validate_user(user)

if not errors:
print("User is valid")
else:
print("User is invalid")

خروجی:
User is valid

🔹 چرا لیست خطا بهتر است؟
فرض کن فرم ثبت‌نامی داریم و کاربر سه مقدار اشتباه وارد کرده است.
روش قبلی:
Invalid username

کاربر باید اصلاح کند و دوباره برنامه را اجرا کند تا خطای بعدی را ببیند.
اما با جمع‌آوری خطاها:
Invalid username type
Invalid age range
Missing email

همه مشکلات را یکجا می‌بینیم.
⚠️ نکته مهم
در Validator بهتر است بین این دو حالت تفاوت قائل شویم:
[]        → هیچ خطایی وجود ندارد
[خطاها] → یک یا چند خطا وجود دارد

مثلاً:
errors = validate_user(user)

if errors:
print("Invalid user")
else:
print("Valid user")

لیست خالی در Python مقدار Falsy دارد.
🧠 خلاصه
به‌جای:
return "Invalid age"

می‌توانیم:
errors.append("Invalid age")

استفاده کنیم.
در پایان:
return errors

و نتیجه می‌تواند:
[] 

یا:
["Invalid username type", "Invalid age range"]

باشد.
🎯 تمرین
تابع زیر را کامل کن:
def validate_product(product):

errors = []

# قوانین اعتبارسنجی

return errors

قوانین:
name  → اجباری، str و غیرخالی
price → اجباری، عدد و بین 1 تا 10000
stock → اجباری، int و بین 0 تا 1000

برای هر خطا یک پیام جداگانه به errors اضافه کن.
اگر داده زیر را بررسی کنیم:
product = {
"name": "",
"price": -50,
"stock": 2000
}

باید هر سه خطا گزارش شوند.
پست ۱۶۴ از دوره جامع Python 🐍
1
🐍 آموزش جامع Python — پست ۱۶۵
اعتبارسنجی شرطی و وابسته در JSON
در پست قبل یاد گرفتیم چند خطای اعتبارسنجی را هم‌زمان جمع کنیم.
اما گاهی معتبر بودن یک فیلد به مقدار فیلد دیگری وابسته است.
مثلاً اگر کاربر حالت اعلان‌ها را فعال کند، باید یک email معتبر هم داشته باشد.
🔹 یک قانون وابسته
user = {
"notifications": True,
"email": "ali@example.com"
}

قانون ما:
notifications = True
→ email باید وجود داشته باشد و معتبر باشد

می‌توانیم بنویسیم:
if user.get("notifications") is True:

if "email" not in user:
print("Email is required")

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

elif not user["email"].strip():
print("Email cannot be empty")

🔹 اگر اعلان‌ها خاموش باشند
اگر:
user = {
"notifications": False
}

دیگر email اجباری نیست.
if user.get("notifications") is True:
print("Email is required")
else:
print("Email is optional")

نکته مهم این است که برای مقایسه Boolean بهتر است از:
is True

و:
is False

استفاده کنیم.
🔹 چند شرط وابسته
فرض کنیم یک محصول داریم:
product = {
"has_discount": True,
"discount": 20
}

قوانین:
has_discount = True
→ discount اجباری است
→ discount باید عدد باشد
→ discount باید بین 0 تا 100 باشد

has_discount = product.get("has_discount")
discount = product.get("discount")

if has_discount is True:

if "discount" not in product:
print("Discount is required")

elif (
not isinstance(discount, (int, float))
or isinstance(discount, bool)
):
print("Invalid discount")

elif not 0 <= discount <= 100:
print("Invalid discount range")

🔥 جمع‌آوری خطاهای وابسته
می‌توانیم این قوانین را با Validator قبلی ترکیب کنیم:
def validate_product(product):

errors = []

if not isinstance(product, dict):
return ["Product must be a dictionary"]

if "name" not in product:
errors.append("Missing name")

elif not isinstance(product["name"], str) or not product["name"].strip():
errors.append("Invalid name")

if "price" not in product:
errors.append("Missing price")

elif (
not isinstance(product["price"], (int, float))
or isinstance(product["price"], bool)
):
errors.append("Invalid price")

elif not 1 <= product["price"] <= 10000:
errors.append("Invalid price range")

if product.get("has_discount") is True:

if "discount" not in product:
errors.append("Missing discount")

elif (
not isinstance(product["discount"], (int, float))
or isinstance(product["discount"], bool)
):
errors.append("Invalid discount")

elif not 0 <= product["discount"] <= 100:
errors.append("Invalid discount range")

return errors

🔹 مثال
product = {
"name": "Laptop",
"price": 1200,
"has_discount": True
}

errors = validate_product(product)

for error in errors:
print(error)

خروجی:
Missing discount

چون:
has_discount → True

پس discount باید وجود داشته باشد.
⚠️ نکته مهم
این نوع اعتبارسنجی را Conditional Validation می‌نامیم.
یعنی:
اگر A درست بود
→ B اجباری شود

مثلاً:
has_discount = True
↓
discount required

یا:
notifications = True
↓
email required

🧠 خلاصه
در اعتبارسنجی واقعی فقط قوانین مستقل نداریم.
ممکن است یک فیلد بر اساس فیلد دیگر:
اجباری شود.
نوع مشخصی داشته باشد.
محدوده مشخصی داشته باشد.
اصلاً نباید وجود داشته باشد.
الگوی کلی:
شرط اصلی
↓
بررسی فیلد وابسته
↓
بررسی نوع
↓
بررسی مقدار

🎯 تمرین
تابع زیر را کامل کن:
def validate_user(user):

errors = []

# قوانین

return errors

قوانین:
username → اجباری و str و غیرخالی

has_phone → اجباری و bool

اگر has_phone == True:
phone → اجباری و str و غیرخالی

برای هر خطا یک پیام جداگانه به errors اضافه کن.
برای این داده:
user = {
"username": "Ali",
"has_phone": True
}

باید خطای مربوط به نبودن phone گزارش شود.
پست ۱۶۵ از دوره جامع Python 🐍
1
🔐 آموزش امنیت Telegram — پست ویژه
ربات‌هایی که با @username اطلاعات زیادی پیدا می‌کنند
احتمالاً بعضی ربات‌های Telegram را دیده‌اید که فقط با دادن:
@username

اطلاعات زیادی درباره یک کاربر نمایش می‌دهند؛ مثلاً:
نام و اطلاعات عمومی حساب
گروه‌ها و کانال‌هایی که کاربر در آن‌ها دیده شده
پیام‌های عمومی منتسب به کاربر
فعالیت‌های قبلی
اطلاعات مرتبط دیگری که درباره آن حساب جمع‌آوری شده است
این سرویس‌ها واقعاً وجود دارند و ممکن است اطلاعات قابل‌توجهی نمایش دهند.
اما سؤال مهم این است:
این اطلاعات دقیقاً از کجا آمده‌اند؟

🔹 اطلاعات چگونه جمع‌آوری می‌شود؟
یک سرویس می‌تواند اطلاعات را از منابع مختلف جمع کند:
اطلاعات عمومی Telegram
↓
گروه‌ها و کانال‌های قابل دسترسی
↓
پیام‌ها و فعالیت‌های قابل مشاهده
↓
داده‌های جمع‌آوری‌شده در گذشته
↓
منابع و دیتابیس‌های دیگر

مثلاً ممکن است یک سرویس قبلاً اطلاعات تعداد زیادی گروه و کانال را جمع‌آوری کرده باشد.
بعد وقتی شما:
@username

را وارد می‌کنید، سرویس در دیتابیس خودش جست‌وجو می‌کند و اطلاعاتی را که قبلاً درباره آن کاربر جمع شده نمایش می‌دهد.
بنابراین همیشه به این معنی نیست که اطلاعات همین لحظه از حساب کاربر گرفته شده است.
🔹 آیا Bot می‌تواند همه گروه‌های یک کاربر را پیدا کند؟
اینجا یک تفاوت مهم وجود دارد.
یک Bot معمولی صرفاً با داشتن username نمی‌تواند آزادانه فهرست تمام گروه‌هایی را که یک شخص عضو آن‌هاست دریافت کند.
Telegram برای دسترسی به اطلاعات اعضای Chat محدودیت‌هایی دارد؛ برای مثال getChatMember برای سایر کاربران به‌طور تضمینی زمانی در دسترس است که Bot در آن Chat ادمین باشد.
اما اگر یک Bot در یک گروه حضور داشته باشد و دسترسی لازم به پیام‌ها را داشته باشد، می‌تواند فعالیت‌های همان گروه را مشاهده کند.
بنابراین یک سرویس می‌تواند با جمع‌آوری اطلاعات از مکان‌های مختلف، یک پروفایل بزرگ‌تر ایجاد کند.
🔥 اما Private Chat چطور؟
این قسمت خیلی مهم است.
اگر رباتی بگوید:
«فقط username بده تا تمام Private Chatهای این شخص را نشان بدهم.»

این موضوع با قابلیت معمول Bot API فرق دارد.
یک username به‌تنهایی رمز ورود یا مجوز دسترسی به Private Chatهای شخص نیست.
Bot معمولی پیام‌هایی را که برای خودش ارسال می‌شوند و بسته به شرایط پیام‌های Chatهایی را که به آن‌ها دسترسی دارد دریافت می‌کند؛ این با دسترسی آزاد به تمام Private Chatهای یک حساب کاملاً متفاوت است.
پس اگر سرویسی اطلاعاتی از Private Chat نشان می‌دهد، باید منشأ آن اطلاعات را جداگانه بررسی کرد؛ ممکن است داده از تعاملات قبلی، منابع شخص ثالث، دیتابیس‌های جمع‌آوری‌شده یا منابع دیگری آمده باشد.
⚠️ Username با اطلاعات ورود فرق دارد
این دو مورد را با هم اشتباه نگیرید:
@username

با:
Login Code
2FA Password
Session
API Credentials

کاملاً متفاوت است.
داشتن username به معنی دسترسی به حساب نیست.
بنابراین برای ربات‌های ناشناس هرگز این اطلاعات را ارسال نکن:
❌ کد ورود Telegram
❌ رمز Two-Step Verification
❌ Session String
❌ فایل Session
❌ API ID / API Hash
❌ تأیید QR Login ناشناس

🔹 چرا این موضوع مهم است؟
حتی اگر هر اطلاعات به‌تنهایی عمومی باشد، کنار هم قرار گرفتن آن‌ها می‌تواند تصویر نسبتاً دقیقی از فعالیت آنلاین یک شخص ایجاد کند:
Username
↓
گروه‌های قابل مشاهده
↓
پیام‌های عمومی
↓
کانال‌ها و لینک‌های مرتبط
↓
فعالیت‌های قبلی
↓
ساخت یک پروفایل کامل‌تر

به این فرایند در حوزه امنیت و OSINT، تجمیع اطلاعات از منابع مختلف گفته می‌شود.
🛡 برای محافظت از خودمان
✅ اطلاعات حساس را در گروه‌های عمومی منتشر نکنیم.
✅ به Botهای ناشناس اعتماد نکنیم.
✅ کد ورود را برای هیچ Botی ارسال نکنیم.
✅ Session و اطلاعات احراز هویت را در اختیار دیگران قرار ندهیم.
✅ قبل از اضافه کردن Bot به گروه، دسترسی‌های آن را بررسی کنیم.
✅ روی لینک‌های ناشناس ارسال‌شده توسط Botها کلیک نکنیم.

🧠 نکته نهایی
اگر یک ربات با دادن @username اطلاعات زیادی درباره یک شخص نشان می‌دهد، این موضوع می‌تواند واقعی باشد؛ اما لزوماً به معنی این نیست که ربات مستقیماً وارد حساب Telegram شده است.
سؤال اصلی این است:
این اطلاعات از کجا جمع شده، چه مدت ذخیره شده و چه بخش‌هایی عمومی یا قابل‌دسترسی بوده‌اند؟

شناخت این موضوع یکی از بخش‌های مهم امنیت و حریم خصوصی در Telegram است.
آموزش امنیت Telegram — پست ویژه 🔐
1
🐍 آموزش جامع Python — پست ۱۶۶
کار با فایل‌های CSV در Python
تا اینجا با فایل‌های JSON کار کردیم.
حالا یکی از فرمت‌های بسیار رایج برای ذخیره داده‌های جدولی را یاد می‌گیریم:
CSV

CSV مخفف Comma-Separated Values است.
در CSV، اطلاعات معمولاً به شکل سطر و ستون ذخیره می‌شوند.
مثلاً فایل:
users.csv

می‌تواند این محتوا را داشته باشد:
name,age,city
Ali,25,Baku
Sara,22,Tehran
Reza,30,Tabriz

هر خط یک رکورد و هر مقدار بین جداکننده‌ها یک فیلد است.
🔹 خواندن CSV
Python برای کار با CSV یک ماژول داخلی به نام csv دارد:
import csv

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

for row in reader:
print(row)

خروجی:
['name', 'age', 'city']
['Ali', '25', 'Baku']
['Sara', '22', 'Tehran']
['Reza', '30', 'Tabriz']

دقت کن که هر row یک لیست است.
🔹 دسترسی به ستون‌ها
مثلاً می‌توانیم نام و سن را جدا کنیم:
import csv

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

for row in reader:
print(row[0], row[1])

خروجی:
name age
Ali 25
Sara 22
Reza 30

🔹 Header چیست؟
در بسیاری از فایل‌های CSV، اولین سطر شامل نام ستون‌هاست:
name,age,city

این سطر را Header می‌نامیم.
می‌توانیم آن را جداگانه دریافت کنیم:
import csv

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

header = next(reader)

print(header)

for row in reader:
print(row)

خروجی:
['name', 'age', 'city']
['Ali', '25', 'Baku']
['Sara', '22', 'Tehran']
['Reza', '30', 'Tabriz']

next(reader) اولین سطر را مصرف می‌کند؛ بنابراین حلقه از سطر بعدی شروع می‌شود.
⚠️ نکته مهم
مقادیر CSV معمولاً به صورت رشته (str) خوانده می‌شوند.
مثلاً:
print(type(row[1]))

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

پس اگر بخواهیم سن را به‌عنوان عدد استفاده کنیم، باید آن را تبدیل کنیم:
age = int(row[1])

🧠 خلاصه
csv.reader() → خواندن CSV
row → یک سطر از CSV
next(reader) → دریافت سطر بعدی
Header → نام ستون‌ها

CSV برای داده‌های جدولی ساده بسیار کاربردی است؛ در پست بعدی یاد می‌گیریم چگونه CSV را با نام ستون‌ها و به شکل Dictionary بخوانیم.
🎯 تمرین
فایل زیر را بساز:
students.csv

محتوای آن:
name,score
Ali,18
Sara,20
Reza,15

سپس برنامه‌ای بنویس که نام و نمره هر دانش‌آموز را چاپ کند.
خروجی مورد انتظار:
Ali 18
Sara 20
Reza 15

پست ۱۶۶ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۶۷
خواندن CSV به صورت Dictionary با DictReader
در پست قبل با csv.reader() آشنا شدیم.
مشکل reader این است که برای دسترسی به ستون‌ها باید از Index استفاده کنیم:
row[0]
row[1]

در فایل‌های بزرگ، استفاده از نام ستون‌ها خواناتر است.
برای این کار از:
csv.DictReader

استفاده می‌کنیم.
🔹 مثال ساده
فرض کنیم فایل users.csv این محتوا را دارد:
name,age,city
Ali,25,Baku
Sara,22,Tehran
Reza,30,Tabriz

حالا:
import csv

with open("users.csv", "r", encoding="utf-8") as file:
reader = csv.DictReader(file)

for row in reader:
print(row["name"], row["age"], row["city"])

خروجی:
Ali 25 Baku
Sara 22 Tehran
Reza 30 Tabriz

🔹 row چیست؟
هر row یک Dictionary است:
{
"name": "Ali",
"age": "25",
"city": "Baku"
}

بنابراین به‌جای:
row[0]

می‌توانیم بنویسیم:
row["name"]

این روش مخصوصاً برای فایل‌های CSV با ستون‌های زیاد خواناتر است.
🔹 تبدیل مقدارها
همان‌طور که در csv.reader() دیدیم، مقدارها معمولاً str هستند.
مثلاً:
age = int(row["age"])

حالا می‌توانیم عملیات عددی انجام دهیم:
import csv

with open("users.csv", "r", encoding="utf-8") as file:
reader = csv.DictReader(file)

for row in reader:
age = int(row["age"])

if age >= 25:
print(row["name"])

خروجی:
Ali
Reza

🔹 چرا DictReader مفید است؟
مقایسه کن:
row[0]
row[1]
row[2]

با:
row["name"]
row["age"]
row["city"]

روش دوم مشخص‌تر است و اگر ترتیب ستون‌ها تغییر کند، کار با داده‌ها ساده‌تر می‌شود.
⚠️ نکته مهم
اگر نام ستونی را اشتباه بنویسیم:
print(row["username"])

و چنین ستونی در CSV وجود نداشته باشد، با خطای:
KeyError

مواجه می‌شویم.
پس نام ستون‌ها باید با Header فایل مطابقت داشته باشد.
🧠 خلاصه
csv.reader()      → هر سطر به صورت List
csv.DictReader() → هر سطر به صورت Dictionary

row[0] → دسترسی با Index
row["name"] → دسترسی با نام ستون

🎯 تمرین
فایل زیر را بساز:
products.csv

محتوا:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,70,0

برنامه‌ای بنویس که فقط محصولاتی را چاپ کند که موجودی آن‌ها بیشتر از 0 است.
خروجی مورد انتظار:
Laptop 1200
Mouse 25

پست ۱۶۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۶۸
نوشتن داده در CSV با csv.writer و DictWriter
تا اینجا یاد گرفتیم چگونه فایل CSV را بخوانیم.
حالا می‌خواهیم اطلاعات جدید را در یک فایل CSV ایجاد و ذخیره کنیم.
برای این کار می‌توانیم از:
csv.writer

یا:
csv.DictWriter

استفاده کنیم.
🔹 نوشتن با csv.writer
import csv

with open("users.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)

writer.writerow(["name", "age", "city"])
writer.writerow(["Ali", 25, "Baku"])
writer.writerow(["Sara", 22, "Tehran"])

فایل users.csv:
name,age,city
Ali,25,Baku
Sara,22,Tehran

در این روش هر سطر را به صورت List می‌نویسیم.
🔹 writerow()
با:
writer.writerow(...)

یک سطر به فایل اضافه می‌کنیم.
مثلاً:
writer.writerow(["Reza", 30, "Tabriz"])

🔹 writerows()
برای نوشتن چند سطر:
users = [
["Ali", 25, "Baku"],
["Sara", 22, "Tehran"],
["Reza", 30, "Tabriz"]
]

with open("users.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)

writer.writerow(["name", "age", "city"])
writer.writerows(users)

🔹 DictWriter
اگر داده‌ها را به صورت Dictionary داشته باشیم، DictWriter خواناتر است:
import csv

users = [
{"name": "Ali", "age": 25, "city": "Baku"},
{"name": "Sara", "age": 22, "city": "Tehran"}
]

with open("users.csv", "w", newline="", encoding="utf-8") as file:
fieldnames = ["name", "age", "city"]

writer = csv.DictWriter(
file,
fieldnames=fieldnames
)

writer.writeheader()
writer.writerows(users)

خروجی فایل:
name,age,city
Ali,25,Baku
Sara,22,Tehran

اینجا:
writer.writeheader()

نام ستون‌ها را می‌نویسد.
و:
writer.writerows(users)

Dictionaryها را به سطرهای CSV تبدیل می‌کند.
🔥 تفاوت writer و DictWriter
با csv.writer:
writer.writerow(["Ali", 25, "Baku"])

داده‌ها به صورت List نوشته می‌شوند.
با csv.DictWriter:
writer.writerow({
"name": "Ali",
"age": 25,
"city": "Baku"
})

داده‌ها به صورت Dictionary نوشته می‌شوند.
پس:
csv.writer
→ مناسب داده‌های List

csv.DictWriter
→ مناسب داده‌های Dictionary

اگر داده‌های برنامه‌ات از قبل به شکل Dictionary باشند، DictWriter معمولاً خواناتر و مدیریت آن راحت‌تر است.
🔹 چرا newline=""؟
هنگام کار با csv بهتر است فایل را با:
newline=""

باز کنیم.
این کار باعث می‌شود ماژول csv مدیریت خطوط فایل را به شکل صحیح انجام دهد و از ایجاد خطوط خالی اضافی در بعضی محیط‌ها جلوگیری شود.
⚠️ w و a را اشتباه نکن
"w"

محتوای قبلی فایل را هنگام نوشتن جایگزین می‌کند.
اما:
"a"

برای اضافه کردن داده به انتهای فایل است.
مثلاً:
with open("users.csv", "a", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["Reza", 30, "Tabriz"])

🧠 خلاصه
csv.writer()       → نوشتن List
csv.DictWriter() → نوشتن Dictionary

writerow() → یک سطر
writerows() → چند سطر

writeheader() → نوشتن Header

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

🎯 تمرین
فایل زیر را ایجاد کن:
products.csv

و اطلاعات زیر را با DictWriter ذخیره کن:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,70,10

سعی کن از:
writeheader()
writerows()

استفاده کنی.
پست ۱۶۸ از دوره جامع Python 🐍