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 — پست ۱۷۵
کنترل Quote در CSV با quoting
در پست قبل دیدیم که Python چگونه داده‌هایی را که شامل کاما یا " هستند، در CSV مدیریت می‌کند.
حالا می‌توانیم مشخص کنیم چه زمانی مقدارها داخل Quote قرار بگیرند.
برای این کار از پارامتر:
quoting

استفاده می‌کنیم.
🔹 csv.QUOTE_MINIMAL
حالت پیش‌فرض csv است.
Python فقط زمانی از Quote استفاده می‌کند که لازم باشد.
import csv

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

writer = csv.writer(
file,
quoting=csv.QUOTE_MINIMAL
)

writer.writerow(["Laptop", 1200])
writer.writerow(["Mouse", "Fast, wireless"])

خروجی:
Laptop,1200
Mouse,"Fast, wireless"

چون ردیف اول به Quote نیاز ندارد، اما ردیف دوم شامل کاماست.
🔹 csv.QUOTE_ALL
با این حالت، تمام مقدارها داخل Quote نوشته می‌شوند:
import csv

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

writer = csv.writer(
file,
quoting=csv.QUOTE_ALL
)

writer.writerow(["Laptop", 1200])
writer.writerow(["Mouse", "Fast, wireless"])

خروجی:
"Laptop","1200"
"Mouse","Fast, wireless"

🔹 csv.QUOTE_NONNUMERIC
در این حالت مقدارهای متنی Quote می‌شوند و مقدارهای عددی بدون Quote نوشته می‌شوند:
import csv

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

writer = csv.writer(
file,
quoting=csv.QUOTE_NONNUMERIC
)

writer.writerow(["Laptop", 1200])

خروجی:
"Laptop",1200

🔹 csv.QUOTE_NONE
در این حالت Python به‌صورت معمول Quote اضافه نمی‌کند.
اگر داده شامل delimiter باشد، باید آن را به شکل مناسب Escape کنیم؛ در غیر این صورت ممکن است خطا ایجاد شود.
برای CSVهای معمولی، معمولاً نیازی به استفاده از این حالت نداریم.
🧠 مقایسه
QUOTE_MINIMAL
→ فقط وقتی لازم باشد Quote می‌گذارد.

QUOTE_ALL
→ همه مقدارها را Quote می‌کند.

QUOTE_NONNUMERIC
→ مقدارهای غیرعددی را Quote می‌کند.

QUOTE_NONE
→ Quote خودکار را غیرفعال می‌کند.

⚠️ نکته مهم
در بیشتر پروژه‌ها لازم نیست quoting را دستی تنظیم کنیم.
حالت پیش‌فرض:
csv.QUOTE_MINIMAL

برای بسیاری از فایل‌های CSV کافی است.
تنظیم quoting زمانی مفید است که فرمت خروجی CSV مشخص و اجباری باشد.
🎯 تمرین
کد زیر را تغییر بده تا تمام مقدارهای CSV داخل Quote نوشته شوند:
import csv

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

writer = csv.writer(file)

writer.writerow(["Laptop", 1200])
writer.writerow(["Mouse", 25])

فایل خروجی باید شبیه این باشد:
"Laptop","1200"
"Mouse","25"

پست ۱۷۵ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۷۶
مدیریت فایل‌های CSV با DictReader و DictWriter در یک برنامه کامل
تا اینجا یاد گرفتیم:
CSV را بخوانیم.
داده جدید اضافه کنیم.
داده‌ها را فیلتر کنیم.
رکوردها را ویرایش و حذف کنیم.
delimiter و quoting را مدیریت کنیم.
حالا یک برنامه کوچک می‌سازیم که چند عملیات را روی یک فایل CSV انجام دهد.
🔹 ساخت فایل اولیه
ابتدا یک فایل به نام:
products.csv

می‌سازیم:
import csv

products = [
{"name": "Laptop", "price": 1200, "stock": 5},
{"name": "Mouse", "price": 25, "stock": 20},
{"name": "Keyboard", "price": 70, "stock": 10}
]

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

writer = csv.DictWriter(
file,
fieldnames=["name", "price", "stock"]
)

writer.writeheader()
writer.writerows(products)

🔹 خواندن اطلاعات
حالا فایل را می‌خوانیم:
with open(
"products.csv",
"r",
encoding="utf-8"
) as file:

reader = csv.DictReader(file)

for product in reader:
print(product)

هر ردیف به شکل یک Dictionary در اختیار ما قرار می‌گیرد.
مثلاً:
{
'name': 'Laptop',
'price': '1200',
'stock': '5'
}

⚠️ توجه کن که مقدارهای CSV هنگام خواندن معمولاً str هستند.
🔹 تبدیل مقدارهای عددی
اگر بخواهیم روی قیمت محاسبه انجام دهیم:
with open(
"products.csv",
"r",
encoding="utf-8"
) as file:

reader = csv.DictReader(file)

for product in reader:
price = float(product["price"])
stock = int(product["stock"])

print(
product["name"],
price,
stock
)

🔹 فیلتر کردن محصولات
مثلاً فقط محصولاتی را نمایش دهیم که موجود هستند:
with open(
"products.csv",
"r",
encoding="utf-8"
) as file:

reader = csv.DictReader(file)

for product in reader:
if int(product["stock"]) > 0:
print(product["name"])

خروجی:
Laptop
Mouse
Keyboard

🔹 یک نکته مهم درباره reader
این دو روش را با هم اشتباه نکن:
csv.reader()

هر ردیف را به شکل List می‌دهد:
["Laptop", "1200", "5"]

اما:
csv.DictReader()

هر ردیف را به شکل Dictionary می‌دهد:
{
"name": "Laptop",
"price": "1200",
"stock": "5"
}

برای فایل‌هایی که ستون‌های مشخص دارند، DictReader معمولاً خواندن و کار با داده‌ها را واضح‌تر می‌کند.
🧠 خلاصه
DictReader
↓
CSV → Dictionary

DictWriter
↓
Dictionary → CSV

و اگر مقدار عددی باشد:
int(product["stock"])
float(product["price"])

چون CSV خودش نوع داده‌های Python را حفظ نمی‌کند و مقادیر را هنگام خواندن معمولاً به صورت متن در اختیار ما قرار می‌دهد.
🎯 تمرین
برنامه‌ای بنویس که:
فایل products.csv را با DictReader بخواند.
فقط محصولاتی را نمایش دهد که:
قیمت کمتر از 100 دارند.
موجودی بیشتر از 0 دارند.
قیمت را با float() و موجودی را با int() تبدیل کند.
با این داده‌ها:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,70,10
Monitor,300,0

خروجی باید شامل:
Mouse
Keyboard

باشد.
پست ۱۷۶ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۱۷۷
مدیریت خطاهای تبدیل داده در CSV
در CSV تقریباً همه مقدارها هنگام خواندن به شکل str در اختیار ما قرار می‌گیرند.
مثلاً:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,unknown,10

اگر بخواهیم price را به float تبدیل کنیم:
price = float(product["price"])

برای مقدار:
unknown

با خطای:
ValueError

مواجه می‌شویم.
🔹 استفاده از try و except
می‌توانیم تبدیل داده را داخل try قرار دهیم:
import csv

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

reader = csv.DictReader(file)

for product in reader:

try:
price = float(product["price"])
stock = int(product["stock"])

print(
product["name"],
price,
stock
)

except ValueError:
print(
f"Invalid data: {product['name']}"
)

اگر یکی از مقدارها قابل تبدیل نباشد، برنامه متوقف نمی‌شود و except اجرا می‌شود.
🔹 مثال با داده نامعتبر
فرض کنیم CSV این باشد:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,unknown,10
Monitor,300,0

خروجی:
Laptop 1200.0 5
Mouse 25.0 20
Invalid data: Keyboard
Monitor 300.0 0

پس فقط رکورد نامعتبر رد می‌شود و پردازش بقیه داده‌ها ادامه پیدا می‌کند.
🔹 مدیریت جداگانه قیمت و موجودی
گاهی بهتر است بدانیم کدام ستون مشکل دارد:
try:
price = float(product["price"])
except ValueError:
print(f"Invalid price: {product['name']}")
continue

try:
stock = int(product["stock"])
except ValueError:
print(f"Invalid stock: {product['name']}")
continue

در اینجا:
continue

باعث می‌شود پردازش همین رکورد متوقف شود و حلقه سراغ رکورد بعدی برود.
🔹 بررسی مقدار خالی
ممکن است مقدار یک ستون کاملاً خالی باشد:
name,price,stock
Laptop,,5

در این حالت:
float("")

باعث ValueError می‌شود.
می‌توانیم قبل از تبدیل بررسی کنیم:
price_text = product["price"].strip()

if not price_text:
print(f"Price is missing: {product['name']}")
continue

try:
price = float(price_text)
except ValueError:
print(f"Invalid price: {product['name']}")
continue

⚠️ نکته مهم
برای داده‌های CSV فقط این کافی نیست:
price = float(product["price"])

چون داده ممکن است:
خالی
متنی
اشتباه

باشد.
بهتر است هنگام ورود داده به برنامه، آن را اعتبارسنجی و تبدیل کنیم.
🧠 الگوی پیشنهادی
برای CSV می‌توانیم این الگو را داشته باشیم:
CSV
↓
خواندن داده
↓
بررسی وجود مقدار
↓
تبدیل نوع
↓
مدیریت ValueError
↓
استفاده از داده معتبر

مثلاً:
try:
price = float(product["price"])
except ValueError:
print("Invalid price")
continue

🎯 تمرین
فایل زیر را در نظر بگیر:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,unknown,10
Monitor,,15
Phone,800,invalid

برنامه‌ای بنویس که:
CSV را با DictReader بخواند.
price را به float تبدیل کند.
stock را به int تبدیل کند.
خطاهای ValueError را مدیریت کند.
رکوردهای نامعتبر را رد کند.
فقط محصولات معتبر را چاپ کند.
خروجی معتبر باید شامل:
Laptop
Mouse

باشد.
پست ۱۷۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۷۸
تشخیص خودکار ساختار CSV با csv.Sniffer
گاهی نمی‌دانیم یک فایل CSV با چه جداکننده‌ای ساخته شده است.
ممکن است فایل با:
,

یا:
;

یا:
|

جدا شده باشد.
در چنین شرایطی می‌توانیم از:
csv.Sniffer

استفاده کنیم.
🔹 تشخیص delimiter
فرض کنیم فایل ما این باشد:
name;price;stock
Laptop;1200;5
Mouse;25;20
Keyboard;70;10

اما نمی‌دانیم جداکننده ; است.
می‌توانیم از Sniffer استفاده کنیم:
import csv

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

sample = file.read(1024)

dialect = csv.Sniffer().sniff(
sample,
delimiters=",;|"
)

print(dialect.delimiter)

خروجی:
;

Python توانسته جداکننده را از محتوای فایل تشخیص دهد.
🔹 استفاده از جداکننده تشخیص‌داده‌شده
حالا می‌توانیم همان dialect را به DictReader بدهیم:
import csv

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

sample = file.read(1024)

dialect = csv.Sniffer().sniff(
sample,
delimiters=",;|"
)

file.seek(0)

reader = csv.DictReader(
file,
dialect=dialect
)

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

خروجی:
Laptop
Mouse
Keyboard

🔹 چرا file.seek(0)؟
در این قسمت:
sample = file.read(1024)

ما ۱۰۲۴ کاراکتر از فایل را می‌خوانیم.
مکان فعلی فایل دیگر ابتدای فایل نیست.
بنابراین:
file.seek(0)

مکان خواندن را به ابتدای فایل برمی‌گرداند.
بعد می‌توانیم فایل را کامل با DictReader بخوانیم.
🔹 تشخیص Header
Sniffer قابلیت دیگری هم دارد.
می‌توانیم از:
has_header()

استفاده کنیم:
import csv

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

sample = file.read(1024)

has_header = csv.Sniffer().has_header(sample)

print(has_header)

ممکن است خروجی:
True

باشد.
یعنی Sniffer تشخیص داده که احتمالاً فایل دارای Header است.
⚠️ نکته مهم
Sniffer همیشه نمی‌تواند با اطمینان کامل ساختار فایل را تشخیص دهد.
مثلاً اگر فایل خیلی کوچک یا نامنظم باشد، ممکن است تشخیص اشتباه انجام شود.
پس:
csv.Sniffer()

یک ابزار تشخیص احتمالی است، نه تضمینی.
اگر ساختار CSV را از قبل می‌دانیم، بهتر است مستقیماً بنویسیم:
reader = csv.DictReader(
file,
delimiter=";"
)

🧠 خلاصه
csv.Sniffer().sniff()
↓
تشخیص Dialect / delimiter

file.seek(0)
↓
بازگشت به ابتدای فایل

csv.Sniffer().has_header()
↓
تشخیص احتمالی وجود Header

🎯 تمرین
فایل زیر را در نظر بگیر:
name|price|stock
Laptop|1200|5
Mouse|25|20
Keyboard|70|10

برنامه‌ای بنویس که:
فایل را باز کند.
با csv.Sniffer().sniff() جداکننده را تشخیص دهد.
فایل را با seek(0) به ابتدا برگرداند.
با DictReader اطلاعات را بخواند.
نام محصولات را چاپ کند.
خروجی:
Laptop
Mouse
Keyboard

پست ۱۷۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۷۹
مثال کامل و مقاوم در برابر خطا برای CSV
حالا چند مفهوم CSV را در یک برنامه واقعی ترکیب می‌کنیم:
بررسی وجود فایل
تشخیص delimiter
بررسی Header
تبدیل price و stock
مدیریت خطاهای تبدیل
رد کردن رکوردهای نامعتبر
🔹 فایل نمونه
name;price;stock
Laptop;1200;5
Mouse;25;20
Keyboard;unknown;10
Monitor;300;invalid
Phone;800;15

🔹 برنامه کامل
import csv
from pathlib import Path


file_path = Path("products.csv")

try:
if not file_path.exists():
raise FileNotFoundError("File not found")

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

sample = file.read(1024)

if not sample.strip():
raise ValueError("CSV file is empty")

try:
dialect = csv.Sniffer().sniff(
sample,
delimiters=",;|"
)
except csv.Error:
print("Could not detect delimiter")
raise SystemExit

file.seek(0)

reader = csv.DictReader(
file,
dialect=dialect
)

required = {"name", "price", "stock"}

if not required.issubset(
set(reader.fieldnames or [])
):
print("Required columns are missing")
raise SystemExit

for row_number, product in enumerate(
reader,
start=2
):
try:
name = product["name"].strip()
price = float(product["price"])
stock = int(product["stock"])

if not name or price < 0 or stock < 0:
raise ValueError("Invalid data")

print(
f"{name} - ${price} - Stock: {stock}"
)

except (ValueError, TypeError):
print(
f"Invalid row: {row_number}"
)

except FileNotFoundError:
print("File not found")

except PermissionError:
print("Permission denied")

except UnicodeDecodeError:
print("Invalid file encoding")

except csv.Error as error:
print(f"CSV error: {error}")

🔹 نتیجه
رکوردهای معتبر پردازش می‌شوند:
Laptop - $1200.0 - Stock: 5
Mouse - $25.0 - Stock: 20
Phone - $800.0 - Stock: 15

و رکوردهای خراب فقط گزارش می‌شوند:
Invalid row: 4
Invalid row: 5

برنامه به خاطر یک رکورد خراب متوقف نمی‌شود.
🧠 الگوی کلی
فایل
↓
بررسی ساختار
↓
خواندن CSV
↓
تبدیل نوع داده
↓
اعتبارسنجی
↓
مدیریت خطا
↓
استفاده از داده معتبر

⚠️ اگر delimiter را از قبل می‌دانیم، بهتر است به‌جای Sniffer مستقیماً آن را مشخص کنیم:
csv.DictReader(file, delimiter=";")

🎯 تمرین
کد را تغییر بده تا ستون category را هم بررسی کند و اگر خالی بود، رکورد را نامعتبر اعلام کند.
پست ۱۷۹ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۰
تبدیل CSV به JSON
تا اینجا با خواندن، نوشتن، ویرایش، حذف و اعتبارسنجی داده‌های CSV آشنا شدیم.
حالا می‌خواهیم داده‌های CSV را به JSON تبدیل کنیم.
این کار زمانی مفید است که داده را از یک فایل جدولی به ساختاری مناسب برای برنامه‌ها و APIها منتقل کنیم.
🔹 فایل CSV
فرض کنیم products.csv این باشد:
name,price,stock
Laptop,1200,5
Mouse,25,20
Keyboard,70,10

ابتدا CSV را با DictReader می‌خوانیم:
import csv

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

reader = csv.DictReader(file)

products = list(reader)

حالا:
print(products)

تقریباً:
[
{'name': 'Laptop', 'price': '1200', 'stock': '5'},
{'name': 'Mouse', 'price': '25', 'stock': '20'},
{'name': 'Keyboard', 'price': '70', 'stock': '10'}
]

⚠️ توجه کن که مقدارها هنوز str هستند.
🔹 تبدیل نوع داده
قبل از تبدیل به JSON بهتر است نوع داده‌های عددی را اصلاح کنیم:
for product in products:
product["price"] = float(product["price"])
product["stock"] = int(product["stock"])

حالا می‌توانیم داده را به JSON تبدیل کنیم:
import json

json_data = json.dumps(
products,
indent=4,
ensure_ascii=False
)

print(json_data)

خروجی:
[
{
"name": "Laptop",
"price": 1200.0,
"stock": 5
},
{
"name": "Mouse",
"price": 25.0,
"stock": 20
}
]

🔹 ذخیره JSON در فایل
می‌توانیم نتیجه را مستقیماً در یک فایل ذخیره کنیم:
with open(
"products.json",
"w",
encoding="utf-8"
) as file:

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

حالا یک فایل به نام:
products.json

داریم.
⚠️ مدیریت خطا
اگر CSV شامل مقدار نامعتبر باشد:
name,price,stock
Laptop,unknown,5

تبدیل:
float("unknown")

باعث ValueError می‌شود.
پس در برنامه واقعی بهتر است:
try:
product["price"] = float(product["price"])
product["stock"] = int(product["stock"])

except ValueError:
print("Invalid product data")

را در نظر بگیریم.
🧠 خلاصه
CSV
↓
DictReader
↓
Dictionary
↓
تبدیل نوع داده
↓
JSON
↓
json.dump()

🎯 تمرین
فایل products.csv را به products.json تبدیل کن.
شرایط:
name به صورت str
price به صورت float
stock به صورت int
رکوردهای دارای داده نامعتبر نادیده گرفته شوند.
پست ۱۸۰ از دوره جامع Python 🐍
🤖 معرفی چند پروژه و ابزار جالب در حوزه AI
اگر به دنبال پروژه‌های جدید و متن‌باز در حوزه هوش مصنوعی هستید، این دو مورد را هم می‌توانید بررسی کنید:
🔹 JEV
🌐 وب‌سایت:
typesafe.ai
JEV یک پروژه/پلتفرم در حوزه AI است که می‌توانید برای آشنایی بیشتر با قابلیت‌ها و نحوه استفاده از آن، وب‌سایت رسمی آن را بررسی کنید.
🔗 لینک:
https://typesafe.ai
🔹 LAYA
یک پروژه متن‌باز مرتبط با اجرای مدل‌های هوش مصنوعی و اکوسیستم MLX.
💻 GitHub:
Laya — GitHub
🤗 Hugging Face:
Laya — Hugging Face
اگر به مدل‌های AI، اجرای مدل‌ها و پروژه‌های متن‌باز علاقه دارید، بررسی Repository و فایل‌های پروژه می‌تواند اطلاعات بیشتری درباره نحوه استفاده و قابلیت‌های آن ارائه دهد.
📌 لینک‌ها
🔹 JEV:
typesafe.ai
🔹 LAYA — GitHub:
mizorewww/laya-mlx
🔹 LAYA — Hugging Face:
aac6fef
اگر پروژه‌های AI متن‌باز و ابزارهای جدید هوش مصنوعی را دنبال می‌کنید، این موارد می‌توانند برای بررسی و یادگیری جالب باشند. 🤖
🐍 آموزش جامع Python — پست ۱۸۱
آشنایی با دیتابیس SQLite در Python
تا اینجا اطلاعات را در فایل‌هایی مثل:
CSV
JSON

ذخیره و مدیریت کردیم.
اما وقتی حجم داده بیشتر شود، جست‌وجو، ویرایش و حذف اطلاعات در فایل‌ها می‌تواند سخت‌تر شود.
اینجاست که Database وارد می‌شود.
یکی از ساده‌ترین دیتابیس‌ها برای شروع در Python، SQLite است.
🔹 SQLite چیست؟
SQLite یک دیتابیس سبک و فایل‌محور است.
برخلاف بعضی دیتابیس‌ها، برای شروع نیاز نداریم یک Server جداگانه نصب و اجرا کنیم.
Python نیز ماژول استاندارد:
sqlite3

را در اختیارمان قرار می‌دهد.
🔹 ساخت اولین دیتابیس
import sqlite3

connection = sqlite3.connect("users.db")

print("Database created")

connection.close()

با اجرای این کد، فایل:
users.db

ایجاد می‌شود.
اگر فایل از قبل وجود داشته باشد، SQLite همان دیتابیس را باز می‌کند.
🔹 Connection چیست؟
این خط:
connection = sqlite3.connect("users.db")

یک Connection به دیتابیس ایجاد می‌کند.
از طریق این Connection می‌توانیم:
دستور SQL اجرا کنیم
داده اضافه کنیم
داده بخوانیم
داده تغییر دهیم
داده حذف کنیم

در پایان نیز بهتر است Connection را ببندیم:
connection.close()

🔹 ساخت Table
برای ذخیره اطلاعات، ابتدا یک جدول ایجاد می‌کنیم.
import sqlite3

connection = sqlite3.connect("users.db")

cursor = connection.cursor()

cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
)
""")

connection.commit()
connection.close()

در اینجا جدول:
users

ساخته می‌شود.
و سه ستون دارد:
id    → INTEGER
name → TEXT
age → INTEGER

🔹 cursor چیست؟
با:
cursor = connection.cursor()

یک Cursor ایجاد می‌کنیم.
Cursor برای اجرای دستورات SQL استفاده می‌شود:
cursor.execute(...)

مثلاً:
cursor.execute("SELECT * FROM users")

⚠️ commit() را فراموش نکن
بعد از تغییرات دیتابیس معمولاً باید تغییرات را ثبت کنیم:
connection.commit()

مثلاً هنگام:
INSERT
UPDATE
DELETE

استفاده از commit() اهمیت دارد.
🧠 خلاصه
sqlite3.connect()
↓
Connection
↓
Cursor
↓
cursor.execute()
↓
connection.commit()
↓
connection.close()

در پست بعد یاد می‌گیریم چگونه با INSERT اطلاعات را داخل SQLite ذخیره کنیم.
🎯 تمرین
برنامه‌ای بنویس که:
یک دیتابیس به نام school.db ایجاد کند.
جدولی به نام students بسازد.
جدول شامل این ستون‌ها باشد:
id     → INTEGER PRIMARY KEY
name → TEXT
score → REAL

از:
CREATE TABLE IF NOT EXISTS

استفاده کن.
پست ۱۸۱ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۲
درج چند رکورد با executemany()
در پست قبل یاد گرفتیم با INSERT یک رکورد را داخل SQLite ذخیره کنیم.
اما اگر بخواهیم چند رکورد را هم‌زمان اضافه کنیم، استفاده از چندین execute() کار چندان مناسبی نیست.
برای این کار می‌توانیم از:
executemany()

استفاده کنیم.
🔹 مثال ساده
فرض کنیم جدول زیر را داریم:
import sqlite3

connection = sqlite3.connect("users.db")
cursor = connection.cursor()

cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
)
""")

حالا چند کاربر داریم:
users = [
("Ali", 25),
("Sara", 22),
("Reza", 30)
]

می‌توانیم همه را با یک دستور درج کنیم:
cursor.executemany(
"INSERT INTO users (name, age) VALUES (?, ?)",
users
)

connection.commit()

🔹 executemany() چگونه کار می‌کند؟
این کد:
cursor.executemany(
"INSERT INTO users (name, age) VALUES (?, ?)",
users
)

عبارت SQL را برای هر عضو users اجرا می‌کند:
("Ali", 25)
("Sara", 22)
("Reza", 30)

یعنی تقریباً مثل این است:
cursor.execute(
"INSERT INTO users (name, age) VALUES (?, ?)",
("Ali", 25)
)

cursor.execute(
"INSERT INTO users (name, age) VALUES (?, ?)",
("Sara", 22)
)

cursor.execute(
"INSERT INTO users (name, age) VALUES (?, ?)",
("Reza", 30)
)

اما executemany() برای درج مجموعه‌ای از رکوردها مناسب‌تر و خواناتر است.
🔹 مثال کامل
import sqlite3

connection = sqlite3.connect("users.db")
cursor = connection.cursor()

cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
)
""")

users = [
("Ali", 25),
("Sara", 22),
("Reza", 30)
]

cursor.executemany(
"INSERT INTO users (name, age) VALUES (?, ?)",
users
)

connection.commit()
connection.close()

بعد از اجرای برنامه، سه رکورد به جدول اضافه می‌شود.
🔹 استفاده از ?
همانند execute() بهتر است مقدارها را با Parameterized Query ارسال کنیم:
"INSERT INTO users (name, age) VALUES (?, ?)"

و داده‌ها جداگانه قرار بگیرند:
users = [
("Ali", 25),
("Sara", 22)
]

این روش هم خواناتر است و هم از قرار دادن مستقیم مقدارها داخل SQL جلوگیری می‌کند.
🔹 executemany() با lastrowid
یک نکته مهم:
cursor.lastrowid

برای درج چند رکورد، روش مناسبی برای دریافت ID همه رکوردهای جدید نیست.
اگر به ID رکوردهای ایجادشده نیاز دارید، بهتر است بعداً آن‌ها را با یک Query مناسب از دیتابیس دریافت کنید.
⚠️ نکته مهم
ساختار داده‌ها باید با تعداد ?ها هماهنگ باشد.
مثلاً:
"VALUES (?, ?)"

یعنی هر رکورد باید دو مقدار داشته باشد:
("Ali", 25)

اما این اشتباه است:
("Ali", 25, "Extra")

چون تعداد مقادیر با Placeholderها هماهنگ نیست.
🧠 خلاصه
execute()      → اجرای یک عملیات
executemany() → اجرای یک عملیات برای چند مجموعه داده

الگوی اصلی:
cursor.executemany(
"INSERT INTO table_name (column1, column2) VALUES (?, ?)",
data
)

🎯 تمرین
جدول زیر را در نظر بگیر:
students

با ستون‌های:
id     → INTEGER PRIMARY KEY
name → TEXT
score → REAL

با استفاده از executemany() این سه دانش‌آموز را اضافه کن:
students = [
("Ali", 18.5),
("Sara", 19.0),
("Reza", 17.25)
]

در پایان commit() را اجرا کن.
پست ۱۸۲ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۳
خواندن اطلاعات از SQLite با SELECT
تا اینجا یاد گرفتیم چگونه اطلاعات را با INSERT و executemany() داخل SQLite ذخیره کنیم.
حالا می‌خواهیم اطلاعات ذخیره‌شده را بخوانیم.
برای این کار از دستور SQL زیر استفاده می‌کنیم:
SELECT

🔹 خواندن همه رکوردها
فرض کنیم جدول students را داریم:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute("SELECT * FROM students")

rows = cursor.fetchall()

for row in rows:
print(row)

connection.close()

مثلاً خروجی:
(1, 'Ali', 18.5)
(2, 'Sara', 19.0)
(3, 'Reza', 17.25)

هر رکورد به شکل یک tuple برگردانده می‌شود.
🔹 fetchall() چیست؟
بعد از اجرای:
cursor.execute("SELECT * FROM students")

می‌توانیم با:
rows = cursor.fetchall()

تمام رکوردهای نتیجه را دریافت کنیم.
مثلاً:
[
(1, 'Ali', 18.5),
(2, 'Sara', 19.0),
(3, 'Reza', 17.25)
]

🔹 خواندن فقط یک رکورد
اگر فقط یک رکورد می‌خواهیم، می‌توانیم از:
row = cursor.fetchone()

استفاده کنیم:
cursor.execute("SELECT * FROM students")

row = cursor.fetchone()

print(row)

خروجی:
(1, 'Ali', 18.5)

fetchone() هر بار یک رکورد از نتیجه را برمی‌گرداند.
🔹 دریافت چند رکورد با fetchmany()
اگر تعداد مشخصی رکورد بخواهیم:
cursor.execute("SELECT * FROM students")

rows = cursor.fetchmany(2)

print(rows)

مثلاً:
[(1, 'Ali', 18.5), (2, 'Sara', 19.0)]

🔹 انتخاب ستون‌های مشخص
لازم نیست همیشه از * استفاده کنیم.
مثلاً فقط نام و نمره:
cursor.execute(
"SELECT name, score FROM students"
)

rows = cursor.fetchall()

for name, score in rows:
print(name, score)

خروجی:
Ali 18.5
Sara 19.0
Reza 17.25

⚠️ نکته مهم
SELECT اطلاعات را تغییر نمی‌دهد.
یعنی:
SELECT → خواندن
INSERT → اضافه کردن
UPDATE → تغییر دادن
DELETE → حذف کردن

🧠 خلاصه
execute("SELECT ...")
↓
fetchone()
fetchmany()
fetchall()

سه روش اصلی دریافت نتیجه:
cursor.fetchone()
cursor.fetchmany(n)
cursor.fetchall()

🎯 تمرین
از جدول students فقط name و score را دریافت کن و نام و نمره هر دانش‌آموز را چاپ کن.
جدول:
id | name | score

خروجی باید چیزی شبیه این باشد:
Ali 18.5
Sara 19.0
Reza 17.25

پست ۱۸۳ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۴
جست‌وجو و فیلتر داده‌ها در SQLite با WHERE
در پست قبل یاد گرفتیم با SELECT اطلاعات را از SQLite بخوانیم.
اما معمولاً نمی‌خواهیم همه رکوردها را دریافت کنیم.
مثلاً می‌خواهیم فقط دانش‌آموزانی را پیدا کنیم که نمره‌شان بیشتر از 18 است.
برای این کار از:
WHERE

استفاده می‌کنیم.
🔹 یک شرط ساده
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute(
"SELECT * FROM students WHERE score >= ?",
(18,)
)

rows = cursor.fetchall()

for row in rows:
print(row)

connection.close()

اگر داده‌ها این باشند:
(1, 'Ali', 18.5)
(2, 'Sara', 19.0)
(3, 'Reza', 17.25)

خروجی:
(1, 'Ali', 18.5)
(2, 'Sara', 19.0)

🔹 جست‌وجو بر اساس متن
مثلاً فقط کاربری با نام Ali:
cursor.execute(
"SELECT * FROM students WHERE name = ?",
("Ali",)
)

row = cursor.fetchone()

print(row)

نکته مهم: ( "Ali", ) یک tuple تک‌عضوی است.
🔹 استفاده از AND
می‌توانیم چند شرط داشته باشیم:
cursor.execute(
"""
SELECT * FROM students
WHERE score >= ? AND name = ?
""",
(18, "Ali")
)

در اینجا هر دو شرط باید برقرار باشند.
🔹 استفاده از OR
اگر کافی باشد یکی از شرط‌ها برقرار باشد:
cursor.execute(
"""
SELECT * FROM students
WHERE name = ? OR name = ?
""",
("Ali", "Sara")
)

در این حالت دانش‌آموزانی با نام Ali یا Sara پیدا می‌شوند.
🔹 WHERE فقط برای SELECT نیست
WHERE در دستورات دیگری مثل:
UPDATE
DELETE

هم استفاده می‌شود.
مثلاً بعداً می‌توانیم فقط نمره یک دانش‌آموز مشخص را تغییر دهیم:
UPDATE students
SET score = 20
WHERE name = 'Ali';

یا فقط یک رکورد مشخص را حذف کنیم:
DELETE FROM students
WHERE name = 'Ali';

⚠️ نکته امنیتی
مقدارهای دریافت‌شده از کاربر را مستقیماً داخل SQL قرار نده.
❌ روش نامناسب:
name = input("Name: ")

cursor.execute(
f"SELECT * FROM students WHERE name = '{name}'"
)

✅ روش مناسب:
name = input("Name: ")

cursor.execute(
"SELECT * FROM students WHERE name = ?",
(name,)
)

با ? از Parameterized Query استفاده می‌کنیم.
🧠 خلاصه
SELECT → خواندن داده
WHERE → فیلتر کردن داده
AND → هر دو شرط
OR → حداقل یکی از شرط‌ها
? → پارامتر امن

الگوی اصلی:
cursor.execute(
"SELECT * FROM students WHERE score >= ?",
(18,)
)

🎯 تمرین
از جدول students فقط دانش‌آموزانی را پیدا کن که:
score >= 18

و سپس فقط name و score آن‌ها را چاپ کن.
خروجی مثلاً:
Ali 18.5
Sara 19.0

پست ۱۸۴ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۵
مرتب‌سازی و محدود کردن نتایج با ORDER BY و LIMIT
در پست قبل یاد گرفتیم با WHERE اطلاعات موردنظرمان را فیلتر کنیم.
حالا می‌خواهیم نتیجه‌ها را:
مرتب کنیم.
تعداد نتیجه‌ها را محدود کنیم.
برای این کار از ORDER BY و LIMIT استفاده می‌کنیم.
🔹 مرتب‌سازی با ORDER BY
فرض کنیم می‌خواهیم دانش‌آموزان را بر اساس نمره مرتب کنیم:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute("""
SELECT name, score
FROM students
ORDER BY score
""")

rows = cursor.fetchall()

for row in rows:
print(row)

connection.close()

به‌صورت پیش‌فرض مرتب‌سازی صعودی (ASC) است:
17.25
18.5
19.0

🔹 مرتب‌سازی نزولی با DESC
اگر بخواهیم بالاترین نمره ابتدا نمایش داده شود:
cursor.execute("""
SELECT name, score
FROM students
ORDER BY score DESC
""")

نتیجه:
Sara 19.0
Ali 18.5
Reza 17.25

پس:
ASC  → صعودی
DESC → نزولی

🔹 مرتب‌سازی بر اساس چند ستون
می‌توانیم چند ستون را هم مشخص کنیم:
cursor.execute("""
SELECT name, score
FROM students
ORDER BY score DESC, name ASC
""")

ابتدا بر اساس score مرتب می‌شود.
اگر چند رکورد نمره یکسان داشته باشند، name برای مرتب‌سازی بعدی استفاده می‌شود.
🔹 محدود کردن نتایج با LIMIT
فرض کنیم فقط می‌خواهیم سه دانش‌آموز اول را دریافت کنیم:
cursor.execute("""
SELECT name, score
FROM students
ORDER BY score DESC
LIMIT 3
""")

در اینجا ابتدا نمره‌ها از بیشترین به کمترین مرتب می‌شوند و سپس فقط ۳ نتیجه برگردانده می‌شود.
🔥 پیدا کردن بالاترین نمره
ترکیب ORDER BY و LIMIT کاربرد زیادی دارد:
cursor.execute("""
SELECT name, score
FROM students
ORDER BY score DESC
LIMIT 1
""")

top_student = cursor.fetchone()

print(top_student)

مثلاً:
('Sara', 19.0)

🔹 LIMIT بدون ORDER BY
این کد:
SELECT * FROM students LIMIT 3

فقط سه رکورد را درخواست می‌کند؛ اما نباید فرض کنیم این سه رکورد حتماً «اولین» یا «بهترین» رکوردها هستند.
اگر ترتیب مشخصی می‌خواهیم، بهتر است ORDER BY را هم مشخص کنیم.
🧠 خلاصه
ORDER BY → مرتب‌سازی
ASC → صعودی
DESC → نزولی
LIMIT → محدود کردن تعداد نتایج

مثلاً:
cursor.execute("""
SELECT name, score
FROM students
WHERE score >= ?
ORDER BY score DESC
LIMIT 3
""", (18,))

یعنی:
1. فقط نمره‌های ۱۸ و بالاتر
2. مرتب‌سازی از بیشترین به کمترین
3. فقط ۳ نتیجه

🎯 تمرین
از جدول students، دو دانش‌آموز با بالاترین نمره را پیدا کن.
شرایط:
ORDER BY → score نزولی
LIMIT → 2

پست ۱۸۵ از دوره جامع Python 🐍
🤖 مقایسه Claude و ChatGPT در کدنویسی Python
برای یک آزمایش، دقیقاً یک مسئله و یک درخواست یکسان را به Claude و ChatGPT دادم و از هر دو خواستم راه‌حل را با Python پیاده‌سازی کنند.
در تصاویر، خروجی و کدی که هر کدام تولید کرده‌اند را می‌توانید ببینید. 👇
در این مقایسه می‌شود مواردی مثل:
🔹 ساختار و خوانایی کد
🔹 منطق حل مسئله
🔹 رعایت اصول Python
🔹 مدیریت خطاها
🔹 سادگی و قابل‌فهم بودن کد
را بررسی کرد.
هدف این مقایسه این نیست که بگوییم کدام مدل همیشه بهتر است؛ بلکه می‌خواهیم ببینیم برای یک مسئله مشخص، هر مدل چه رویکردی برای حل آن انتخاب می‌کند.
حالا شما کدها را ببینید و نظرتان را بگویید:
به‌نظر شما کدام خروجی بهتر و حرفه‌ای‌تر نوشته شده؟
🟣 Claude
🔵 ChatGPT

اولی با Claude
دومی با ChatGPT

⚠️ این تصاویر شبیه سازی شده هستند

پست مقایسه مدل های هوش مصنوعی🐍
🐍 آموزش جامع Python — پست ۱۸۶
تغییر داده‌ها در SQLite با UPDATE
تا اینجا یاد گرفتیم:
INSERT → اضافه کردن
SELECT → خواندن
WHERE → فیلتر کردن

حالا می‌خواهیم اطلاعات موجود در جدول را تغییر دهیم.
برای این کار از:
UPDATE

استفاده می‌کنیم.
🔹 تغییر یک مقدار
فرض کنیم می‌خواهیم نمره Ali را به 20 تغییر دهیم:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute(
"""
UPDATE students
SET score = ?
WHERE name = ?
""",
(20, "Ali")
)

connection.commit()
connection.close()

اینجا:
SET   → مشخص می‌کند چه مقداری تغییر کند
WHERE → مشخص می‌کند کدام رکورد تغییر کند

🔹 چرا WHERE مهم است؟
این کد:
UPDATE students
SET score = 20

می‌تواند نمره تمام دانش‌آموزان را به 20 تغییر دهد.
اما:
UPDATE students
SET score = 20
WHERE name = 'Ali'

فقط رکوردهای مطابق شرط را تغییر می‌دهد.
پس هنگام استفاده از UPDATE باید به WHERE دقت کنیم.
🔹 تغییر چند ستون
می‌توانیم هم‌زمان چند مقدار را تغییر دهیم:
cursor.execute(
"""
UPDATE students
SET name = ?, score = ?
WHERE id = ?
""",
("Ali Ahmadi", 19.5, 1)
)

connection.commit()

در اینجا name و score رکوردی که id آن 1 است تغییر می‌کنند.
🔹 تعداد رکوردهای تغییرکرده
بعد از UPDATE می‌توانیم بررسی کنیم چند رکورد تغییر کرده است:
print(cursor.rowcount)

مثلاً:
1

یعنی یک رکورد تحت تأثیر قرار گرفته است.
⚠️ نکته مهم
مقدارها را مستقیم داخل SQL قرار نده.
❌ نامناسب:
cursor.execute(
f"UPDATE students SET score = {score} WHERE name = '{name}'"
)

✅ بهتر:
cursor.execute(
"UPDATE students SET score = ? WHERE name = ?",
(score, name)
)

این روش Parameterized Query است.
🧠 خلاصه
UPDATE → تغییر داده
SET → مقدار جدید
WHERE → تعیین رکوردهای موردنظر

الگوی اصلی:
cursor.execute(
"""
UPDATE table_name
SET column = ?
WHERE condition = ?
""",
(new_value, condition_value)
)

🎯 تمرین
جدول students را در نظر بگیر.
می‌خواهیم نمره دانش‌آموزی با نام Sara را از هر مقداری که هست به 20 تغییر دهیم.
کد UPDATE را بنویس و در پایان:
connection.commit()

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

استفاده می‌کنیم.
🔹 حذف یک رکورد
فرض کنیم جدول students داریم:
id | name  | score
1 | Ali | 18
2 | Sara | 15
3 | Reza | 20

می‌توانیم رکورد مربوط به Sara را حذف کنیم:
cursor.execute(
"DELETE FROM students WHERE name = ?",
("Sara",)
)

connection.commit()

بعد از اجرا:
id | name | score
1 | Ali | 18
3 | Reza | 20

⚠️ WHERE بسیار مهم است
اگر بنویسیم:
DELETE FROM students;

هیچ WHEREای وجود ندارد.
در نتیجه تمام رکوردهای جدول حذف می‌شوند.
پس هنگام حذف رکورد، معمولاً باید مشخص کنیم دقیقاً کدام رکورد یا رکوردها باید حذف شوند:
DELETE FROM students
WHERE id = ?;

🔹 حذف بر اساس id
بهتر است وقتی شناسه یکتا داریم، حذف را بر اساس id انجام دهیم:
student_id = 3

cursor.execute(
"DELETE FROM students WHERE id = ?",
(student_id,)
)

connection.commit()

🔹 بررسی تعداد رکوردهای حذف‌شده
می‌توانیم از rowcount استفاده کنیم:
cursor.execute(
"DELETE FROM students WHERE id = ?",
(3,)
)

connection.commit()

print(cursor.rowcount)

اگر رکورد وجود داشته باشد:
1

و اگر رکوردی با این شرط پیدا نشود:
0

🔹 حذف چند رکورد
DELETE می‌تواند چند رکورد را هم حذف کند.
مثلاً تمام دانش‌آموزانی که نمره کمتر از 10 دارند:
cursor.execute(
"DELETE FROM students WHERE score < ?",
(10,)
)

connection.commit()

تمام رکوردهایی که شرط را داشته باشند حذف می‌شوند.
⚠️ از f-string برای SQL استفاده نکن
این روش مناسب نیست:
name = input("Name: ")

cursor.execute(
f"DELETE FROM students WHERE name = '{name}'"
)

به‌جای آن از Parameterized Query استفاده کن:
cursor.execute(
"DELETE FROM students WHERE name = ?",
(name,)
)

این روش امن‌تر و استانداردتر است.
🧠 خلاصه
DELETE FROM table
WHERE condition;

مثلاً:
cursor.execute(
"DELETE FROM students WHERE id = ?",
(5,)
)

نکات مهم:
DELETE        → حذف رکورد
WHERE → مشخص کردن رکوردهای موردنظر
rowcount → تعداد رکوردهای حذف‌شده
commit() → ذخیره تغییرات
? → پارامتر امن برای مقدار

🎯 تمرین
کد زیر را کامل کن تا دانش‌آموزی با id = 2 حذف شود:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute(
"DELETE FROM students WHERE ______ = ?",
(2,)
)

connection.commit()

print(cursor.rowcount)

connection.close()

پست ۱۸۷ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۸
تراکنش‌ها در SQLite | commit() و rollback()
در پست‌های قبل با INSERT، UPDATE و DELETE آشنا شدیم.
حالا باید یاد بگیریم اگر چند تغییر را با هم انجام دادیم و یکی از آن‌ها با خطا مواجه شد، چطور تغییرات را لغو کنیم.
برای این کار از مفهوم Transaction استفاده می‌کنیم.
🔹 Transaction چیست؟
Transaction یعنی چند عملیات مرتبط را به‌عنوان یک واحد در نظر بگیریم.
مثلاً:
cursor.execute(
"UPDATE students SET score = ? WHERE id = ?",
(19, 1)
)

cursor.execute(
"DELETE FROM students WHERE id = ?",
(3,)
)

connection.commit()

تا قبل از commit()، تغییرات هنوز به‌صورت نهایی ثبت نشده‌اند.
با:
connection.commit()

تغییرات تأیید و ذخیره می‌شوند.
🔹 اگر خطایی رخ دهد چه کنیم؟
می‌توانیم از rollback() استفاده کنیم:
try:
cursor.execute(
"UPDATE students SET score = ? WHERE id = ?",
(19, 1)
)

cursor.execute(
"DELETE FROM students WHERE id = ?",
(3,)
)

connection.commit()

except sqlite3.Error:
connection.rollback()

اگر یکی از عملیات‌ها با خطا مواجه شود:
UPDATE
↓
DELETE
↓
Error
↓
rollback()

تغییرات این Transaction لغو می‌شوند.
🔹 تفاوت commit() و rollback()
commit()   → تأیید و ذخیره تغییرات
rollback() → لغو تغییرات Transaction

مثلاً:
connection.commit()

یعنی:
تغییرات را نهایی کن.

اما:
connection.rollback()

یعنی:
تغییرات انجام‌شده در Transaction را برگردان.

🔥 مثال کامل
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

try:
cursor.execute(
"UPDATE students SET score = ? WHERE id = ?",
(20, 1)
)

cursor.execute(
"DELETE FROM students WHERE id = ?",
(3,)
)

connection.commit()
print("Transaction completed")

except sqlite3.Error as error:
connection.rollback()
print("Transaction failed:", error)

finally:
connection.close()

در این ساختار:
try
↓
اجرای عملیات
↓
commit()
↓
موفقیت

و در صورت خطا:
Exception
↓
rollback()
↓
لغو تغییرات

⚠️ نکته مهم
rollback() فقط تغییراتی را برمی‌گرداند که هنوز commit() نشده‌اند.
بعد از:
connection.commit()

دیگر نمی‌توانیم همان تغییرات را با rollback() به حالت قبل برگردانیم.
🔹 چه زمانی Transaction مهم است؟
وقتی چند عملیات به هم وابسته‌اند.
مثلاً در یک سیستم بانکی فرضی:
کم شدن موجودی حساب A
+
اضافه شدن موجودی حساب B

اگر عملیات اول انجام شود ولی عملیات دوم شکست بخورد، نمی‌خواهیم فقط بخشی از عملیات ثبت شود.
در چنین شرایطی Transaction اهمیت زیادی دارد.
🧠 خلاصه
Transaction
↓
چند عملیات مرتبط
↓
موفقیت → commit()
خطا → rollback()

الگوی رایج:
try:
# database operations

connection.commit()

except sqlite3.Error:
connection.rollback()

🎯 تمرین
کد زیر را کامل کن تا اگر هر یک از دو عملیات با خطا مواجه شد، تغییرات لغو شوند:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

try:
cursor.execute(
"UPDATE students SET score = ? WHERE id = ?",
(18, 1)
)

cursor.execute(
"DELETE FROM students WHERE id = ?",
(2,)
)

# تغییرات را نهایی کن

except sqlite3.Error:
# تغییرات را لغو کن

finally:
connection.close()

باید جای خالی را با:
connection.commit()

و:
connection.rollback()

تکمیل کنی.
پست ۱۸۸ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۸۹
کار با NULL در SQLite | IS NULL و IS NOT NULL
در SQLite ممکن است بعضی ستون‌ها مقدار نداشته باشند.
برای نمایش نبودن مقدار از:
NULL

استفاده می‌شود.
⚠️ NULL با 0، رشته خالی "" یا False یکی نیست.
🔹 ایجاد رکورد با NULL
فرض کنیم جدول students این ستون‌ها را دارد:
id | name | score

می‌توانیم دانش‌آموزی بدون نمره ثبت کنیم:
cursor.execute(
"INSERT INTO students (name, score) VALUES (?, ?)",
("Ali", None)
)

connection.commit()

در Python مقدار:
None

به NULL در SQLite تبدیل می‌شود.
🔹 بررسی NULL با IS NULL
برای پیدا کردن رکوردهایی که مقدار score ندارند:
cursor.execute(
"SELECT * FROM students WHERE score IS NULL"
)

students = cursor.fetchall()

for student in students:
print(student)

اینجا از:
IS NULL

استفاده می‌کنیم.
⚠️ = NULL اشتباه است
این روش درست نیست:
WHERE score = NULL

برای بررسی NULL باید بنویسیم:
WHERE score IS NULL

دلیلش این است که NULL به معنی نامشخص / نبود مقدار است و با مقایسه معمولی = بررسی نمی‌شود.
🔹 پیدا کردن مقدارهای غیر NULL
اگر بخواهیم فقط رکوردهایی را پیدا کنیم که نمره دارند:
cursor.execute(
"SELECT * FROM students WHERE score IS NOT NULL"
)

students = cursor.fetchall()

یعنی:
IS NULL     → مقدار وجود ندارد
IS NOT NULL → مقدار وجود دارد

🔹 تغییر مقدار NULL
می‌توانیم مقدار NULL را هم با UPDATE تغییر دهیم.
مثلاً:
cursor.execute(
"UPDATE students SET score = ? WHERE score IS NULL",
(10,)
)

connection.commit()

تمام دانش‌آموزانی که نمره نداشتند، نمره 10 می‌گیرند.
🔹 حذف رکوردهای دارای NULL
می‌توانیم از DELETE هم استفاده کنیم:
cursor.execute(
"DELETE FROM students WHERE score IS NULL"
)

connection.commit()

در اینجا تمام رکوردهایی که score آن‌ها NULL است حذف می‌شوند.
🧠 None در Python و NULL در SQLite
وقتی با sqlite3 کار می‌کنیم:
Python          SQLite

None → NULL

مثلاً:
value = None

cursor.execute(
"INSERT INTO students (name, score) VALUES (?, ?)",
("Sara", value)
)

در SQLite مقدار score برابر NULL خواهد بود.
⚠️ نکته مهم
این مقادیر با NULL متفاوت‌اند:
NULL
0
""
False

مثلاً:
score = 0

یعنی مقدار نمره واقعاً 0 است.
اما:
score = None

یعنی نمره‌ای ثبت نشده است.
🧠 خلاصه
None          → NULL
IS NULL → پیدا کردن مقدارهای NULL
IS NOT NULL → پیدا کردن مقدارهای غیر NULL

و مهم‌تر از همه:
WHERE column IS NULL

نه:
WHERE column = NULL

🎯 تمرین
کد زیر را کامل کن تا فقط دانش‌آموزانی که نمره ندارند نمایش داده شوند:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute(
"SELECT * FROM students WHERE score ______ NULL"
)

students = cursor.fetchall()

for student in students:
print(student)

connection.close()

کلمه مناسب را جایگزین کن:
IS

پست ۱۸۹ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۹۰
جست‌وجوی متنی در SQLite با LIKE
در پست قبل با NULL و IS NULL آشنا شدیم.
حالا می‌خواهیم یاد بگیریم چطور در SQLite به دنبال بخشی از یک متن بگردیم.
برای این کار از:
LIKE

استفاده می‌کنیم.
🔹 جست‌وجوی متن دقیق
اگر بخواهیم نام دقیق Ali را پیدا کنیم، می‌توانیم از = استفاده کنیم:
cursor.execute(
"SELECT * FROM students WHERE name = ?",
("Ali",)
)

اما اگر فقط بخشی از نام را بدانیم، LIKE کاربرد دارد.
🔹 % در LIKE
علامت % یعنی هر تعداد کاراکتر.
مثلاً:
cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("%Ali%",)
)

یعنی هر نامی که عبارت Ali را در هر جای خود داشته باشد.
مثلاً:
Ali
Alireza
Mohali

می‌توانند با این الگو مطابقت داشته باشند.
🔹 شروع شدن با یک متن
اگر بخواهیم نام‌هایی را پیدا کنیم که با Ali شروع می‌شوند:
cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("Ali%",)
)

مثلاً:
Ali
Alireza
AliReza

🔹 پایان یافتن با یک متن
اگر بخواهیم نام‌هایی را پیدا کنیم که به Ali ختم می‌شوند:
cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("%Ali",)
)

در این حالت متن موردنظر باید در انتهای مقدار باشد.
🔹 _ یعنی یک کاراکتر
در الگوی LIKE، علامت _ به معنی دقیقاً یک کاراکتر است.
مثلاً:
cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("A_i",)
)

می‌تواند با نامی مثل:
Ali
Ari
Ami

مطابقت داشته باشد.
چون _ فقط یک کاراکتر را جایگزین می‌کند.
🔥 مثال کامل
فرض کنیم جدول شامل این داده‌ها باشد:
Ali
Alireza
Sara
Reza

حالا:
cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("Ali%",)
)

students = cursor.fetchall()

for student in students:
print(student)

رکوردهایی که با Ali شروع شوند پیدا می‌شوند.
⚠️ نکته مهم: Parameterized Query
مثل همیشه بهتر است مقدار جست‌وجو را با ? به Query بدهیم:
name = input("Search name: ")

cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
(f"%{name}%",)
)

نه اینکه مقدار کاربر را مستقیماً داخل SQL قرار دهیم.
🧠 الگوهای مهم LIKE
%Ali%   → Ali در هر جای متن
Ali% → متن با Ali شروع شود
%Ali → متن با Ali تمام شود
A_i → بین A و i دقیقاً یک کاراکتر

مثلاً:
LIKE "%python%"

یعنی متن python می‌تواند در هر جای مقدار قرار داشته باشد.
🎯 تمرین
کد زیر را کامل کن تا تمام دانش‌آموزانی که نامشان با A شروع می‌شود نمایش داده شوند:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

cursor.execute(
"SELECT * FROM students WHERE name LIKE ?",
("____",)
)

students = cursor.fetchall()

for student in students:
print(student)

connection.close()

الگوی مناسب را بنویس تا نام‌هایی که با A شروع می‌شوند پیدا شوند.
پست ۱۹۰ از دوره جامع Python 🐍
🐍 آموزش جامع Python — پست ۱۹۱
جست‌وجوی چند مقدار با IN در SQLite
در پست قبل با LIKE یاد گرفتیم چطور بخشی از یک متن را جست‌وجو کنیم.
حالا می‌خواهیم ببینیم چطور می‌توانیم بررسی کنیم یک ستون، یکی از چند مقدار مشخص باشد.
برای این کار از:
IN

استفاده می‌کنیم.
🔹 مثال ساده
فرض کنیم می‌خواهیم دانش‌آموزانی را پیدا کنیم که نامشان Ali یا Sara است:
cursor.execute(
"SELECT * FROM students WHERE name IN (?, ?)",
("Ali", "Sara")
)

students = cursor.fetchall()

for student in students:
print(student)

یعنی:
name = "Ali"
یا
name = "Sara"

🔹 مقایسه با OR
بدون IN می‌توانستیم بنویسیم:
cursor.execute(
"""
SELECT * FROM students
WHERE name = ? OR name = ?
""",
("Ali", "Sara")
)

اما IN برای این نوع جست‌وجو خواناتر است:
cursor.execute(
"SELECT * FROM students WHERE name IN (?, ?)",
("Ali", "Sara")
)

🔹 استفاده با چند مقدار
می‌توانیم بیشتر از دو مقدار هم داشته باشیم:
cursor.execute(
"SELECT * FROM students WHERE name IN (?, ?, ?)",
("Ali", "Sara", "Reza")
)

در اینجا فقط رکوردهایی انتخاب می‌شوند که name یکی از این سه مقدار باشد.
🔹 استفاده با عدد
IN فقط برای رشته‌ها نیست.
مثلاً می‌توانیم چند id مشخص را پیدا کنیم:
cursor.execute(
"SELECT * FROM students WHERE id IN (?, ?, ?)",
(1, 3, 5)
)

یعنی:
id = 1
یا
id = 3
یا
id = 5

🔹 ساختن Placeholderها به‌صورت پویا
گاهی تعداد مقادیر از قبل مشخص نیست.
مثلاً:
names = ["Ali", "Sara", "Reza"]

نمی‌توانیم به‌صورت ثابت بنویسیم:
IN (?, ?, ?)

اگر تعداد نام‌ها تغییر کند.
می‌توانیم Placeholderها را بسازیم:
names = ["Ali", "Sara", "Reza"]

placeholders = ",".join("?" for _ in names)

query = f"""
SELECT * FROM students
WHERE name IN ({placeholders})
"""

cursor.execute(query, names)

در اینجا:
names = 3 مقدار
↓
?, ?, ?

ساخته می‌شود.
⚠️ خود مقادیر همچنان با ? به Query داده می‌شوند.
🔹 NOT IN
اگر بخواهیم رکوردهایی را پیدا کنیم که مقدارشان در لیست موردنظر نباشد، از:
NOT IN

استفاده می‌کنیم.
مثلاً:
cursor.execute(
"SELECT * FROM students WHERE name NOT IN (?, ?)",
("Ali", "Sara")
)

یعنی:
دانش‌آموزانی را پیدا کن که نامشان نه

Ali
باشد و نه

Sara
.

🔥 مثال کاربردی
فرض کنیم فقط می‌خواهیم دانش‌آموزان سه کلاس مشخص را بررسی کنیم:
class_names = ["Python", "Django", "Flask"]

placeholders = ",".join("?" for _ in class_names)

cursor.execute(
f"""
SELECT * FROM courses
WHERE name IN ({placeholders})
""",
class_names
)

courses = cursor.fetchall()

این روش زمانی مفید است که تعداد مقادیر جست‌وجو متغیر باشد.
⚠️ نکته مهم
برای مقدارهای متغیر، از این روش استفاده نکن:
query = f"""
SELECT * FROM students
WHERE name IN ({names})
"""

مقدارهای کاربر را مستقیماً داخل SQL قرار نده.
روش درست:
placeholders = ",".join("?" for _ in names)

cursor.execute(
f"SELECT * FROM students WHERE name IN ({placeholders})",
names
)

🧠 خلاصه
IN      → یکی از چند مقدار
NOT IN → هیچ‌کدام از چند مقدار

مثلاً:
WHERE id IN (?, ?, ?)

یا:
WHERE name NOT IN (?, ?)

و برای تعداد متغیر:
placeholders = ",".join("?" for _ in values)

🎯 تمرین
کد زیر را کامل کن تا فقط دانش‌آموزانی که id آن‌ها 1، 3 یا 5 است نمایش داده شوند:
import sqlite3

connection = sqlite3.connect("school.db")
cursor = connection.cursor()

ids = [1, 3, 5]

placeholders = ",".join("?" for _ in ids)

cursor.execute(
f"""
SELECT * FROM students
WHERE id ______ ({placeholders})
""",
ids
)

students = cursor.fetchall()

for student in students:
print(student)

connection.close()

کلمه مناسب را جایگزین کن:
IN

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