رفتن به محتوا
پایتون از صفرپایتون در کار واقعیدرس ۷ از ۱۰

خطا، ماژول و pip

برنامه‌ای که با خطا نمی‌میرد: try و except و else و finally، گرفتن خطای مشخص، raise برای خطای خودت؛ ماژول‌های آماده‌ی پایتون (math، random، datetime، os، sys، subprocess)، ساختن ماژول خودت و if __name__ == "__main__"، و نصب کتابخانه با pip داخل محیط مجازی venv روی Ubuntu.

  • حدود ۲۰ دقیقه مطالعه
  • ۹ مثال اجراشده
  • ۳ تمرین با جواب
  • مقدماتی تا متوسط
  • به‌روزرسانی: ۲۹ مهر ۱۴۰۵

توی این درس یاد می‌گیری برنامه‌ات با اولین ورودی غلط یا فایل گم‌شده نمیرد: با try و except خطا را می‌گیری و کار معقولی می‌کنی، با finally کاری را که حتماً باید انجام شود تضمین می‌کنی، و با raise خودت خطا اعلام می‌کنی. بعد سراغ ماژول‌ها می‌روی: هزاران تابع آماده که همراه پایتون می‌آیند (math، random، datetime، os، sys، subprocess)، و یاد می‌گیری کد خودت را در یک فایل جدا بگذاری و import کنی. در آخر کتابخانه‌های بیرونی را با pip داخل یک محیط مجازی (venv) نصب می‌کنی؛ روشی که روی Ubuntu جدید تنها راه درست است.

مسئله: یک ورودی غلط، کل برنامه را می‌کشد

Section titled “مسئله: یک ورودی غلط، کل برنامه را می‌کشد”

در درس متغیرها دیدی int("بیست") با ValueError برنامه را متوقف کرد. در یک اسکریپت شبانه که ۵۰۰ فایل را پردازش می‌کند، یک فایل خراب نباید باعث شود ۴۹۹ فایل دیگر پردازش نشوند. و از طرف دیگر، لازم نیست هر چیزی را خودت از صفر بنویسی: کسی قبلاً تابع جذر، تاریخ، ارسال درخواست HTTP و خواندن ایمیل را نوشته است.

تشبیه: تور ایمنی و جعبه‌ابزار

Section titled “تشبیه: تور ایمنی و جعبه‌ابزار”

try/except مثل تور ایمنی زیر بندباز است: بندباز (کد) کارش را می‌کند؛ اگر افتاد (خطا)، تور او را می‌گیرد و نمایش ادامه پیدا می‌کند، به‌جای اینکه همه‌چیز تمام شود. ماژول مثل جعبه‌ابزار است: لازم نیست خودت چکش بسازی؛ جعبه را باز می‌کنی (import) و ابزار را برمی‌داری. pip هم فروشگاه ابزار است، و venv کارگاه جدای هر پروژه که ابزارهای یک پروژه با پروژه‌ی دیگر قاطی نشود.

اگر کد داخل try خطا ندهد، else اجرا می‌شود؛ اگر خطا بدهد و نوعش با except جور باشد، except اجرا می‌شود؛ finally در هر حال اجرا می‌شود.
double.py
text = input("یک عدد: ")
try:
n = int(text)
print("دو برابرش:", n * 2)
except ValueError:
print(f"«{text}» عدد نیست!")
print("برنامه ادامه داد.")
صفحه (جواب: ۱۲)
یک عدد: ۱۲
دو برابرش: 24
برنامه ادامه داد.
صفحه (جواب: دوازده)
یک عدد: دوازده
«دوازده» عدد نیست!
برنامه ادامه داد.

پایتون کد داخل try را اجرا می‌کند. اگر خطایی از نوع ValueError رخ داد، بقیه‌ی try را رها می‌کند و بلوک except را اجرا می‌کند؛ بعد برنامه ادامه می‌دهد. اسم خطا را از همان خط آخر پیام‌های خطا (درس‌های قبل) برمی‌داری.

مثال ۲: دوباره بپرس تا درست جواب بدهد

Section titled “مثال ۲: دوباره بپرس تا درست جواب بدهد”

ترکیب while True (درس حلقه) و try:

ask_age.py
while True:
try:
age = int(input("سن: "))
break
except ValueError:
print("لطفاً فقط عدد بنویس.")
print("سن ثبت شد:", age)
صفحه (جواب‌ها: بیست، بعد 20)
سن: بیست
لطفاً فقط عدد بنویس.
سن: 20
سن ثبت شد: 20

break فقط وقتی اجرا می‌شود که int موفق شده باشد. این الگو را برای هر ورودی کاربر استفاده کن.

config.py
def read_config(path):
try:
with open(path, encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print(f"هشدار: {path} نیست؛ از تنظیمات پیش‌فرض استفاده می‌کنم.")
return ""
except PermissionError:
print(f"خطا: اجازه‌ی خواندن {path} را ندارم.")
return ""
print(repr(read_config("missing.conf")))
print(repr(read_config("/etc/shadow")))
خروجی
هشدار: missing.conf نیست؛ از تنظیمات پیش‌فرض استفاده می‌کنم.
''
خطا: اجازه‌ی خواندن /etc/shadow را ندارم.
''

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

divide.py
def divide(a, b):
try:
result = a / b
except ZeroDivisionError as e:
print("خطا:", e)
return None
else:
print("تقسیم موفق بود")
return result
finally:
print("(این همیشه اجرا می‌شود)")
print(divide(10, 2))
print(divide(1, 0))
خروجی
تقسیم موفق بود
(این همیشه اجرا می‌شود)
5.0
خطا: division by zero
(این همیشه اجرا می‌شود)
None
  • as e خود خطا را در e می‌گذارد تا پیامش را چاپ کنی یا در لاگ بنویسی.
  • else فقط وقتی اجرا می‌شود که try بدون خطا تمام شده باشد.
  • finally همیشه اجرا می‌شود، حتی وقتی return زده شده؛ برای کارهای «جمع‌وجور کردن» (بستن اتصال، پاک کردن فایل موقت). همان کاری که trap EXIT در دوره‌ی Bash می‌کند.
⚡ بررسی سریع

کدام بلوک حتی وقتی داخل try دستور return اجرا شده، باز هم اجرا می‌شود؟

مثال ۵: اعلام خطای خودت، raise

Section titled “مثال ۵: اعلام خطای خودت، raise”

گاهی ورودی از نظر پایتون مشکلی ندارد ولی از نظر منطق برنامه غلط است:

port.py
def set_port(port):
if not 1 <= port <= 65535:
raise ValueError(f"پورت {port} خارج از بازه‌ی ۱ تا ۶۵۵۳۵ است")
return port
print(set_port(8080))
print(set_port(70000))
خروجی
8080
Traceback (most recent call last):
File "/home/ali/py/port.py", line 7, in <module>
print(set_port(70000))
^^^^^^^^^^^^^^^
File "/home/ali/py/port.py", line 3, in set_port
raise ValueError(f"پورت {port} خارج از بازه‌ی ۱ تا ۶۵۵۳۵ است")
ValueError: پورت 70000 خارج از بازه‌ی ۱ تا ۶۵۵۳۵ است

raise یک خطا پرتاب می‌کند؛ هر کسی که set_port را صدا زده می‌تواند با try/except ValueError آن را بگیرد. تابع نباید خودش تصمیم بگیرد برنامه چه کند؛ فقط باید واضح بگوید چه چیزی غلط است. (1 <= port <= 65535 هم یعنی «بین ۱ و ۶۵۵۳۵»؛ پایتون مقایسه‌ی زنجیره‌ای را قبول می‌کند.)

مثال ۶: ماژول‌های آماده

Section titled “مثال ۶: ماژول‌های آماده”

پایتون با صدها ماژول می‌آید (به آن «کتابخانه‌ی استاندارد» می‌گویند):

stdlib.py
import math
import random
from datetime import datetime, timedelta
print(math.sqrt(16), math.pi, math.ceil(2.1))
random.seed(42)
print(random.randint(1, 6), random.choice(["web1", "web2", "db1"]))
now = datetime(2026, 10, 21, 14, 30)
print(now.strftime("%Y-%m-%d %H:%M"))
print((now + timedelta(days=7)).date())
خروجی
4.0 3.141592653589793 3
6 web1
2026-10-21 14:30
2026-10-28
شکل import استفاده
import math math.sqrt(16): اسم ماژول جلوی هر تابع
from datetime import datetime datetime(...): مستقیم، بدون اسم ماژول
import numpy as np np....: اسم کوتاه (رایج برای کتابخانه‌های بزرگ)
  • random.seed(42) عددهای تصادفی را تکرارپذیر می‌کند (برای همین خروجی بالا هر بار یکی است). بدون آن، هر اجرا عدد دیگری می‌دهد. برای رمز و توکن هرگز از random استفاده نکن؛ ماژول secrets برای همین ساخته شده (دوره‌ی امنیت).
  • strftime تاریخ را با قالب دلخواه به متن تبدیل می‌کند (%Y سال، %m ماه، %d روز، %H:%M ساعت)، همان قالب‌های دستور date +%F لینوکس. برای «الان»: datetime.now().

مثال ۷: پایتون و سیستم‌عامل

Section titled “مثال ۷: پایتون و سیستم‌عامل”
system.py
import os
import sys
import platform
print(os.getcwd())
print(os.environ.get("HOME"), os.environ.get("NOPE", "تعریف نشده"))
print(sys.argv)
print(platform.system(), platform.python_version())
Terminal window
python3 system.py a b
خروجی
/home/ali/py
/home/ali تعریف نشده
['system.py', 'a', 'b']
Linux 3.12.3
  • os.getcwd() همان pwd است و os.environ متغیرهای محیطی (درس متغیرهای محیطی در دوره‌ی لینوکس).
  • sys.argv لیست آرگومان‌های خط فرمان است: عضو اول اسم اسکریپت، بقیه هر چه بعدش نوشته‌ای. با این، اسکریپت‌هایت مثل دستورهای لینوکس آرگومان می‌گیرند (تمرین سخت).

و برای اجرای یک دستور لینوکس از داخل پایتون، subprocess:

disk.py
import subprocess
r = subprocess.run(["df", "-h", "/"], capture_output=True, text=True)
print(r.returncode)
print(r.stdout.splitlines()[0])
خروجی
0
Filesystem Size Used Avail Use% Mounted on

دستور به شکل لیست داده می‌شود (هر کلمه یک عضو)، returncode همان کد خروج ($?) است و stdout خروجی دستور به شکل متن.

هر فایل .py یک ماژول است. در یک پوشه دو فایل بساز:

tools.py
"""ابزارهای کوچک من."""
def human_size(n):
for unit in ["B", "KB", "MB", "GB"]:
if n < 1024:
return f"{n:.1f} {unit}"
n /= 1024
return f"{n:.1f} TB"
if __name__ == "__main__":
print(human_size(123456789))
main.py
import tools
from tools import human_size
print(tools.human_size(2048))
print(human_size(5_000_000_000))
Terminal window
python3 tools.py
python3 main.py
خروجی
117.7 MB
2.0 KB
4.7 GB
  • import tools فایل tools.py کنار main.py را پیدا می‌کند و تابع‌هایش را در دسترس می‌گذارد.
  • if __name__ == "__main__": یعنی «این بلوک فقط وقتی اجرا شود که این فایل مستقیم اجرا شده، نه وقتی import شده». برای همین python3 tools.py خط تست را چاپ کرد ولی import tools در main.py آن را چاپ نکرد. این الگو را در هر فایلی که هم ابزار است و هم اسکریپت می‌بینی.
  • 5_000_000_000 همان پنج میلیارد است؛ _ فقط برای خوانایی عددهای بزرگ است.

مثال ۹: کتابخانه‌های بیرونی، venv و pip

Section titled “مثال ۹: کتابخانه‌های بیرونی، venv و pip”

خیلی از ابزارهای معروف (مثل requests برای HTTP، یا flask در درس API) همراه پایتون نیستند و باید از PyPI (مخزن کتابخانه‌های پایتون) نصب شوند. روی Ubuntu 24.04 اگر مستقیم pip install بزنی:

خروجی
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.

Ubuntu اجازه نمی‌دهد کتابخانه‌ها را روی پایتونِ خود سیستم نصب کنی، چون ممکن است ابزارهای سیستمی را خراب کنی. راه درست، محیط مجازی (virtual environment) است: یک پوشه‌ی جدا برای هر پروژه با پایتون و کتابخانه‌های خودش. یک بار بسته‌اش را نصب کن:

Terminal window
sudo apt install python3-venv python3-pip

حالا در پوشه‌ی پروژه:

Terminal window
python3 -m venv .venv
source .venv/bin/activate
which python
خروجی
(.venv) ali@pc:~/py$ which python
/home/ali/py/.venv/bin/python

بعد از activate، پرامپت با (.venv) شروع می‌شود و python و pip مال همین محیط‌اند (در محیط فعال، python بدون ۳ هم کار می‌کند). حالا نصب آزاد است:

نمونه (به اینترنت یا میرور PyPI نیاز دارد)
pip install requests
pip freeze > requirements.txt
خروجی (نمونه، کوتاه‌شده)
Collecting requests
Downloading requests-2.32.3-py3-none-any.whl (64 kB)
...
Successfully installed certifi-2024.8.30 charset-normalizer-3.4.0 idna-3.10 requests-2.32.3 urllib3-2.2.3

pip freeze > requirements.txt فهرست دقیق کتابخانه‌ها و نسخه‌هایشان را در یک فایل می‌نویسد؛ هر کس دیگری (یا سرور) با pip install -r requirements.txt همان محیط را می‌سازد. برای خروج از محیط: deactivate.

پشت پرده: خطا از کجا بالا می‌آید؟

Section titled “پشت پرده: خطا از کجا بالا می‌آید؟”

وقتی خطایی رخ می‌دهد، پایتون یک شیء خطا می‌سازد و آن را به بالا پرتاب می‌کند: اول در همان تابع دنبال except جور می‌گردد؛ اگر نبود، تابع تمام می‌شود و خطا به تابعی که صدایش زده می‌رسد، و همین‌طور بالا می‌رود. اگر تا بالای برنامه هیچ exceptی آن را نگرفت، پایتون Traceback (فهرست همه‌ی تابع‌هایی که خطا از آن‌ها رد شد) را چاپ می‌کند و برنامه می‌میرد. برای همین در مثال ۵، خطای set_port در Traceback دو خط داشت: جایی که raise شد و جایی که صدا زده شد.

نوع‌های خطا هم خانواده دارند: FileNotFoundError و PermissionError هر دو زیرمجموعه‌ی OSError هستند، و تقریباً همه‌چیز زیرمجموعه‌ی Exception. پس except OSError: هر دو را می‌گیرد. ولی except Exception: (یا بدتر، except: خالی) همه‌چیز را می‌گیرد، از جمله باگ‌های خودت مثل غلط تایپی (NameError)؛ برای همین فقط خطاهایی را بگیر که انتظارشان را داری.

و import چطور ماژول را پیدا می‌کند؟ در فهرست پوشه‌های sys.path به ترتیب می‌گردد: اول پوشه‌ی اسکریپت، بعد پوشه‌های کتابخانه‌ی استاندارد، بعد کتابخانه‌های نصب‌شده (در venv، پوشه‌ی .venv/lib/python3.12/site-packages). اولین فایلی که با آن اسم پیدا کند برنده است (اشتباهات رایج ۴).

ساختار کار
try: ... except X: ... گرفتن خطای نوع X
except (X, Y): گرفتن هر کدام از چند نوع
except X as e: گرفتن و نگه داشتن خود خطا
else: فقط اگر خطایی نبود
finally: همیشه
raise ValueError("پیام") اعلام خطا
خطای رایج کی
ValueError مقدار نامناسب (int("x"))
TypeError نوع نامناسب ("a" + 1)
KeyError / IndexError کلید یا اندیس نیست
FileNotFoundError / PermissionError فایل نیست / اجازه نیست
ZeroDivisionError تقسیم بر صفر
ModuleNotFoundError ماژول نصب نیست یا اسمش غلط است
دستور کار
python3 -m venv .venv ساختن محیط مجازی
source .venv/bin/activate / deactivate فعال / غیرفعال کردن
pip install اسم نصب کتابخانه (در محیط فعال)
pip freeze > requirements.txt ثبت نسخه‌ها
pip install -r requirements.txt نصب از فهرست
try:
n = int(input("عدد: "))
except:
print("خطا!")

این حتی Ctrl+C (KeyboardInterrupt) و غلط‌های تایپی خودت را هم می‌بلعد و پیدا کردن باگ را غیرممکن می‌کند. راه‌حل: همیشه نوع خطا را بنویس: except ValueError:.

۲) گذاشتن کد زیاد داخل try

Section titled “۲) گذاشتن کد زیاد داخل try”

اگر بیست خط را در try بگذاری، معلوم نیست except ValueError کدام خط را گرفته. راه‌حل: فقط خطی که ممکن است خطا بدهد را در try بگذار؛ بقیه را در else یا بعد از کل بلوک.

خروجی
ModuleNotFoundError: No module named 'requests'

یا نصبش نکرده‌ای، یا در محیطی نصب کرده‌ای که الان فعال نیست. راه‌حل: source .venv/bin/activate و بعد pip install requests؛ با which python مطمئن شو پایتون محیط را اجرا می‌کنی.

۴) اسم فایل خودت، هم‌نام یک ماژول

Section titled “۴) اسم فایل خودت، هم‌نام یک ماژول”

اگر فایلت را random.py یا json.py بنامی، import random در هر فایل آن پوشه فایل تو را می‌آورد (چون پوشه‌ی اسکریپت اول جستجو می‌شود) و خطاهای عجیبی مثل AttributeError: module 'random' has no attribute 'randint' می‌گیری. راه‌حل: اسم‌های اختصاصی (my_random_test.py)، و پاک کردن پوشه‌ی __pycache__ اگر ساخته شده.

۵) python3 -m venv روی Ubuntu تازه خطا می‌دهد

Section titled “۵) python3 -m venv روی Ubuntu تازه خطا می‌دهد”
خروجی
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
apt install python3.12-venv

راه‌حل: همان که می‌گوید: sudo apt install python3-venv.

تمرینآسان

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

دیدن جواب
safe_div.py
try:
a = float(input("عدد اول: "))
b = float(input("عدد دوم: "))
print("حاصل:", a / b)
except ValueError:
print("فقط عدد بنویس.")
except ZeroDivisionError:
print("تقسیم بر صفر ممکن نیست.")
صفحه (جواب‌ها: 10 و 0)
عدد اول: 10
عدد دوم: 0
تقسیم بر صفر ممکن نیست.
تمرینمتوسط

تمرین اصلی: تابع load_servers(path) بنویس که یک فایل JSON (لیستی از سرورها، مثل درس قبل) بخواند. اگر فایل نبود، لیست خالی برگرداند و هشدار بدهد؛ اگر JSON خراب بود (json.JSONDecodeError)، پیام خطا را با شماره‌ی خط چاپ کند و لیست خالی برگرداند. با یک فایل سالم، یک فایل ناموجود و یک فایل خراب امتحانش کن.

دیدن جواب
load.py
import json
def load_servers(path):
try:
with open(path, encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
print(f"هشدار: {path} پیدا نشد")
return []
except json.JSONDecodeError as e:
print(f"خطا: {path} خط {e.lineno}: {e.msg}")
return []
with open("good.json", "w", encoding="utf-8") as f:
f.write('[{"name": "web1"}]')
with open("bad.json", "w", encoding="utf-8") as f:
f.write('[{"name": "web1",}]')
print(load_servers("good.json"))
print(load_servers("none.json"))
print(load_servers("bad.json"))
خروجی
[{'name': 'web1'}]
هشدار: none.json پیدا نشد
[]
خطا: bad.json خط 1: Expecting property name enclosed in double quotes
[]

ویرگول اضافه بعد از آخرین عضو در JSON مجاز نیست (برخلاف پایتون). خطای JSONDecodeError خودش lineno و msg دارد.

تمرینسخت

یک ابزار خط فرمان بساز: size.py که مسیر یک فایل را از sys.argv بگیرد و اندازه‌اش را با human_size (مثال ۸، import از tools.py) چاپ کند. اگر آرگومان داده نشد، راهنما چاپ کند و با کد خروج ۱ تمام شود؛ اگر فایل نبود، خطای خوانا بدهد و با کد ۲ تمام شود. با echo $? کد خروج را بسنج.

دیدن جواب
size.py
import sys
from pathlib import Path
from tools import human_size
if len(sys.argv) < 2:
print("استفاده: python3 size.py <مسیر فایل>")
sys.exit(1)
path = Path(sys.argv[1])
try:
print(f"{path}: {human_size(path.stat().st_size)}")
except FileNotFoundError:
print(f"خطا: {path} وجود ندارد")
sys.exit(2)
Terminal window
python3 size.py
echo $?
python3 size.py /usr/bin/python3.12
python3 size.py /no/file
echo $?
خروجی
استفاده: python3 size.py <مسیر فایل>
1
/usr/bin/python3.12: 7.6 MB
خطا: /no/file وجود ندارد
2

sys.exit(کد) برنامه را با آن کد خروج تمام می‌کند؛ پس این اسکریپت در Bash مثل هر دستور دیگری با && و || (درس ترکیب دستورها) کار می‌کند. اندازه‌ی فایل پایتون روی سیستم تو ممکن است فرق کند.

آزمونک
  1. کدام شکل except بهتر است؟

  2. بلوک else در try کی اجرا می‌شود؟

  3. raise ValueError("...") چه می‌کند؟

  4. if __name__ == "__main__": یعنی…

  5. روی Ubuntu 24.04 pip install خطای externally-managed-environment می‌دهد. راه درست؟

  6. برای ساختن توکن امن کدام ماژول؟

  • try + except نوع خطای مشخص را می‌گیرد و برنامه ادامه می‌دهد؛ else اگر خطایی نبود؛ finally همیشه؛ as e خود خطا.
  • raise برای اعلام خطای منطقی؛ هرگز except خالی.
  • import ماژول / from ماژول import چیز؛ کتابخانه‌ی استاندارد: math، random، datetime، os، sys (argv، exit)، subprocess، json، csv، pathlib.
  • هر فایل .py یک ماژول است؛ if __name__ == "__main__": برای کد اجرای مستقیم.
  • کتابخانه‌ی بیرونی: python3 -m venv .venv، source .venv/bin/activate، pip install؛ requirements.txt برای تکرارپذیری.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
try: ... except ValueError: ...گرفتن یک خطای مشخص
except OSError as e: print(e)گرفتن و دیدن پیام خطا
finally:همیشه اجرا شود
raise ValueError("پیام")اعلام خطا
from datetime import datetimeوارد کردن از ماژول
sys.argv / sys.exit(1)آرگومان‌ها / خروج با کد
subprocess.run([...], capture_output=True, text=True)اجرای دستور لینوکس
if __name__ == "__main__":فقط در اجرای مستقیم
python3 -m venv .venv && source .venv/bin/activateمحیط مجازی
pip install x / pip freeze > requirements.txtنصب و ثبت کتابخانه‌ها