Skip to content

9. واردات‌ها

خطایی که در نسخه ۱ تمرین عملی رخ داد، ما را بر آن می‌دارد تا نقش عبارت [import] را با جزئیات بیشتری بررسی کنیم.

Image

9.1. اسکریپت‌های [import_01]

اسکریپت [imported] توسط اسکریپت‌های مختلف (که به آن‌ها ماژول نیز گفته می‌شود) وارد خواهد شد:

Image


# ماژول وارد شده
# این دستور هر بار که ماژول وارد می‌شود اجرا خواهد شد
print("2")
# متغیر متعلق به ماژول واردشده
x=4

یک ماژول هنگام وارد شدن اجرا می‌شود. بنابراین، وقتی ماژول [imported] وارد می‌شود:

  • خط ۳ نمایش داده خواهد شد؛
  • متغیر x در خط ۵ مقداردهی خواهد شد؛

اسکریپت [main_01] به شرح زیر است:


# یک ماژول واردشده اجرا می‌شود
import imported
# استفاده از متغیر x از ماژول واردشده
print(imported.x)
  • خط ۲: ماژول [imported] وارد می‌شود. این باعث اجرای آن می‌شود:
    • مقدار ۲ نمایش داده خواهد شد؛
    • متغیر x با مقدار 4 ایجاد می‌شود؛
  • خط ۴: متغیر x از ماژول واردشده استفاده می‌شود؛

در PyCharm، خطایی گزارش می‌شود:

در [1]، PyCharm نشان می‌دهد که ماژول [imported] را شناسایی نمی‌کند. از نظر فنی، این بدان معناست که پوشه‌ای که ماژول [imported] در آن قرار دارد، در مسیر پایتون PyCharm نیست. مسیر پایتون (Python Path) مجموعه‌ای از دایرکتوری‌ها است که در آن ماژول‌های واردشده جستجو می‌شوند. برای حل این مشکل، کافی است دایرکتوری حاوی ماژول [imported] – در این مورد، دایرکتوری [import/01] – را به [Sources root]: مشخص کنید:

Image

Image

پس از این، پوشه [import/01] به مسیر پایتون PyCharm اضافه می‌شود و خطا از بین می‌رود:

Image

  • در [1]، رنگ پوشه [01] تغییر کرده است؛
  • در [2-3]، دیگر خطایی وجود ندارد؛

نتایج اجرای برنامه به شرح زیر است:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_01.py
2
4

Process finished with exit code 0

توضیحات

  • خط ۲ نتیجه اجرای ماژول واردشده است؛
  • خط ۳ مقدار متغیر x را از ماژول واردشده نمایش می‌دهد؛

نکته کلیدی که باید از این مثال برداشت کرد این است که یک ماژول (یا اسکریپت) واردشده اجرا می‌شود.

اسکریپت [main_02] به شرح زیر است:


# متغیر x از ماژول واردشده وارد می‌شود
from imported import x
# نمایش داده می‌شود
print(x)
  • در خط ۲، نحو متفاوتی برای وارد کردن [from module import objet1, objet2, …] مشاهده می‌کنیم. در اینجا، ما متغیر [imported.x] را وارد می‌کنیم. با این نحو، متغیر x به متغیری درون اسکریپت [main_02] تبدیل می‌شود. دیگر نیازی به پیش‌افزودن نام ماژول آن، [imported]، نیست؛
  • خط ۴: متغیر x از [main_02] نمایش داده می‌شود؛

نتایج اجرای کد به شرح زیر است:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_02.py
2
4

Process finished with exit code 0

اسکریپت [main_03] به شرح زیر است:


# همه چیز قابل مشاهده از ماژول واردشده وارد می‌شود
from imported import *
# استفاده از متغیر x از ماژول واردشده
print(x)

علامت [import *] در خط ۲ به این معنی است که تمام اشیاء قابل مشاهده از ماژول وارد شده (متغیرها، توابع) وارد می‌شوند.

نتایج به شرح زیر است:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_03.py
2
4

Process finished with exit code 0

اسکریپت [main_04] به شرح زیر است:


#متغیر x از ماژول واردشده وارد می‌شود
# و آن را به y تغییر نام می‌دهیم
from imported import x as y
#متغیر y نمایش داده می‌شود
print(y) 

خط ۳ نشان می‌دهد که می‌توانید یک شیء را از ماژول واردشده فراخوانی کرده و یک نام مستعار برای آن تعیین کنید. در اینجا، متغیر [imported.x] به متغیر [main_04.y] تبدیل می‌شود. نتایج همانند قبل است.

9.2. اسکریپت [import_02]

Image

ماژول واردشده [module1.py] به شرح زیر است:


# یک تابع
def f1():
    print("f1")

ماژول واردشده تابعی را تعریف می‌کند که سناریوی رایجی است.

اسکریپت [main_01] به شرح زیر است:


# وارد کردن
import module1
# f1 را اجرا کنید
module1.f1()
  • خط ۲: ماژول وارد شده است. در آستانهٔ اجرا قرار دارد. در اینجا، هیچ چیزی نمایش داده نمی‌شود؛
  • خط ۴: تابع [f1] از ماژول واردشده اجرا می‌شود؛

نتایج اجرای آن به شرح زیر است:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/02/main_01.py
f1

Process finished with exit code 0

توجه: برای جلوگیری از گزارش خطای PyCharm در مورد وارد کردن در خط ۲، پوشه‌ای که حاوی [module1] است باید در داخل پوشه [Sources Root] که در داخل PyCharm قرار دارد، قرار داده شود:

Image

در [1]، پوشه [02] که داخل [Sources Root] قرار داشت، آبی شده است. شایان ذکر است که خطای گزارش‌شده در اینجا مانع از اجرای صحیح اسکریپت‌ها نمی‌شود. این به این دلیل است که وقتی اسکریپت [main_0x] اجرا می‌شود، پوشهٔ آن اسکریپت به‌طور خودکار به مسیر پایتون (Python Path) اضافه می‌شود. در نتیجه، [module1] پیدا می‌شود. از این پس، هرگاه یک پوشه در اسکرین‌شات به رنگ آبی ظاهر شود، به این معنی است که به [Sources Root] از PyCharm اضافه شده است.

اسکریپت [main_02] به شرح زیر است:


# وارد کردن
from module1 import f1
# اجرای f1
f1()
  • خط ۲ تابع [f1] را از ماژول [module1] وارد می‌کند؛
  • در خط ۴، تابع f1 استفاده می‌شود؛

نتایج با نتایج اسکریپت [main_01] یکسان است.

9.3. اسکریپت‌های [import_03]

Image

توجه: [03] در اسکریپت‌های [Sources Root] پروژه گنجانده شده است.

اسکریپت‌های جدید ماژول [module2] را وارد می‌کنند که در همان پوشه آن‌ها نیست.

اسکریپت [module2] به شرح زیر است:


# یک تابع
def f2():
    print("f2")

بنابراین اسکریپت تابعی به نام [f2] تعریف می‌کند.

اسکریپت [main_01] به شرح زیر است:


# وارد کردن ماژول Class2
import dir1.module2
#اجرای f2
dir1.module2.f2()
  • خط ۲: یک نشانه‌گذاری ویژه برای مشخص کردن محل ماژول [module2] استفاده می‌شود. [dir1.module2] باید به‌عنوان مسیر [dir1/module2] خوانده شود: برای یافتن [module2]، از پوشه‌ای که حاوی اسکریپت فعلی [main_01] است شروع کنید، سپس به [dir1] بروید، جایی که [module2] را پیدا خواهید کرد. در اینجا مهم است به خاطر داشته باشید که نقطه شروع مسیر، پوشه‌ای است که اسکریپت واردکننده را در خود دارد؛
  • خط ۴: برای اجرای تابع [f2] از [module2];

نتایج به شرح زیر است:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/03/main_01.py
f2

Process finished with exit code 0

خط ۲: نتیجه تابع [f2].

اسکریپت [main_02] به شرح زیر است:


#وارد کردن ماژول dir1.module2 که نام آن تغییر یافته است
import dir1.module2 as module2
#اجرای f2
module2.f2()

خط ۲: ماژول [dir1.module] برای ساده‌سازی نوشتن خط ۴ نام‌گذاری مجدد شد.

اسکریپت [main_03] به شرح زیر است:


#وارد کردن تابع f2 از ماژول dir1.module2
from dir1.module2 import f2
#اجرای f2
f2()

این بار، در خط ۲، ما فقط تابع [f2] را وارد می‌کنیم، که سپس به تابع اسکریپت [main_03] (خط ۴) تبدیل می‌شود.

تمام این اسکریپت‌ها در زمینه PyCharm به همان خوبی که در کنسول پایتون کار می‌کنند، عمل می‌کنند. دلیل این است که در هر دو حالت، دایرکتوری اسکریپت در حال اجرا – در این مورد، دایرکتوری [03] – بخشی از مسیر پایتون (Python Path) است. در نتیجه، دایرکتوری [dir1/module2] پیدا می‌شود.

9.4. اسکریپت‌های [import_04]

Image

در اینجا، پوشه‌های [dir1] و [dir2] در داخل پوشه [Sources Root] پروژه PyCharm قرار داده شده‌اند.

اولین ماژول واردشده [module3] است:


# یک تابع
def f3():
    print("f3")

ماژول دوم واردشده [module4] است:


from module3 import f3

#یک تابع
def f4():
    f3()
    print("f4")
  • در خط ۱، تابع [f3] از [module3] وارد شده است. در اینجا، [module3] قابل مشاهده است زیرا پوشه آن، [dir1 در داخل [Sources Root] قرار داده شده است؛
  • خطوط ۴–۶: ما تابع [f4] را تعریف می‌کنیم که تابع [f3] را از [module3] فراخوانی می‌کند؛

اسکریپت اصلی [main_01] به شرح زیر است:


# وارد کردن ماژول4
from module4 import f4
#اجرای f4
f4()
  • خط ۲: ماژول [module4] وارد می‌شود. این امر به این دلیل قابل مشاهده است که پوشه آن، [dir2 در داخل پوشه [Sources Root] از PyCharm قرار داده شده است؛
  • خط ۴: اجرای تابع [f4] از [module4];

نتایج اجرای [main_01] در داخل PyCharm به شرح زیر است:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/04/main_01.py
f3
f4

Process finished with exit code 0

اکنون، بیایید [main_01] را در یک ترمینال (کنسول) پایتون اجرا کنیم:

Image

نتایج به شرح زیر است:

1
2
3
4
5
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\04>python main_01.py
Traceback (most recent call last):
  File "main_01.py", line 2, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'

چه اتفاقی افتاد؟ ترمینال پایتون از مسیر پایتون (Python Path) یا از [Sources Root] یا PyCharm اطلاعی ندارد. این پایتون پات (Python Path) مخصوص به خود را دارد. این پات همیشه شامل دایرکتوری اسکریپت در حال اجرا است – در این مورد، اسکریپت [main_01]. بنابراین دایرکتوری [import/04] را شناسایی می‌کند. در اسکریپت در حال اجرا، خط زیر را پیدا می‌کند:


from module4 import f4

تفسیرگر پایتون برای [module4] در دایرکتوری‌های موجود در مسیر پایتون خود جستجو می‌کند. با این حال، [module4] در [import/04] – که در واقع در مسیر پایتون (Python Path) قرار دارد – یافت نمی‌شود، بلکه در [import/04/dir2] یافت می‌شود که در مسیر پایتون قرار ندارد. از این رو این خطا رخ می‌دهد.

بنابراین با مشکلی مواجه هستیم که قبلاً هم با آن روبرو شده‌ایم: اسکریپتی که در PyCharm به درستی اجرا می‌شود، ممکن است هنگام اجرا در یک ترمینال پایتون از کار بیفتد. این یک مشکل مکرر است که باید آن را حل کنیم.

9.5. اسکریپت‌های [import_05]

Image

توجه: دایرکتوری‌های [dir1] و [dir2] به مسیر پایتون (Python Path) اضافه شده‌اند. شایان ذکر است که در اینجا یک تداخل وجود دارد: [module3] و [module4] برای PyCharm در دو مکان در مسیر پایتون یافت خواهند شد:

  • در [import/04/dir1] و [import/05/dir1] برای [module3]؛
  • در [import/04/dir2] و [import/05/dir2] برای [module4]؛

سپس می‌توانیم [import/04/dir1] و [import/04/dir2] را از فایل‌های [Sources Root] در پروژه PyCharm استخراج کنیم. مشخص می‌شود که در این مورد، [import/05/dir1] کپی‌ای از [import/04/dir1] است (همین موضوع در مورد [dir2] نیز صدق می‌کند) و بنابراین مشکلی وجود ندارد. با این حال، باید توجه داشت که در خود PyCharm باید در لیست پوشه‌های [Sources Root] دقت شود تا از تداخل‌ها جلوگیری گردد.

اسکریپت [main_01] به صورت زیر درمی‌آید:


import sys
# ویرایش sys.path برای شامل کردن پوشه‌ها
#شامل کلاس‌هایی است که باید وارد شوند
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
#وارد کردن ماژول ۴
from module4 import f4
#کلید F4 را فشار دهید
f4()

ما در تلاشیم تا مشکل مسیر پایتون را حل کنیم. می‌خواهیم مسیری داشته باشیم که درست همان‌طور که در ترمینال پایتون کار می‌کند، در PyCharm نیز به خوبی عمل کند. برای این کار، خودمان آن را اصلاح خواهیم کرد.

  • خطوط ۴–۶: ما دایرکتوری‌های [., ./dir1, ./dir2] را به مسیر پایتون اضافه می‌کنیم. برای اینکه این کار انجام شود، دایرکتوری جاری در زمان اجرا باید دایرکتوری [import/05] باشد. این مورد در PyCharm صدق می‌کند اما لزوماً در یک ترمینال پایتون صدق نمی‌کند، همان‌طور که خواهیم دید؛
  • خط ۸: ما [module4] را وارد می‌کنیم. با توجه به کاری که انجام داده‌ایم، باید در [./dir2] یافت شود؛

اجرای کد در PyCharm نتایج زیر را می‌دهد:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/main_01.py
f3
f4

Process finished with exit code 0

اکنون، در یک ترمینال پایتون:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4

خط ۱: دایرکتوری اجرای برنامه [import/05] است.

حالا بیایید یک سطح به عقب در درخت دایرکتوری [import/05] برگردیم:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>cd ..

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_01.py
Traceback (most recent call last):
  File "05/main_01.py", line 8, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'
  • خط ۲: وقتی [main_01] اجرا می‌شود، دیگر در پوشه [import/05] نیستیم بلکه در [import] هستیم. با این حال، ما نوشتیم:

sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")

این کار پوشه‌های [import, import/dir1, import/dir2] را به مسیر پایتون اضافه می‌کند که اصلاً آنچه ما می‌خواهیم نیست. توجه داشته باشید که افزودن پوشه‌هایی که وجود ندارند (import/dir1, import/dir2) به مسیر پایتون هیچ خطایی ایجاد نمی‌کند.

ما پیشرفت کرده‌ایم، اما این کافی نیست. ما باید مسیرهای مطلق را، نه مسیرهای نسبی، به مسیر پایتون اضافه کنیم.

اسکریپت [main_02] نسخه‌ای از [main_01] است که از یک فایل پیکربندی [config.json] استفاده می‌کند:


{
  "dependencies": [
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir1",
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir2"
  ]
}

مقدار کلید [dependencies] فهرست دایرکتوری‌هایی است که باید به مسیر پایتون اضافه شوند. توجه داشته باشید که در اینجا از مسیرهای مطلق به جای مسیرهای نسبی استفاده شده است.

اسکریپت [main_02] از فایل [config.json] به شرح زیر استفاده می‌کند:


import codecs
import json
import sys

# فایل پیکربندی
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
# خواندن فایل پیکربندی JSON
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

# تغییر sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

# وارد کردن ماژول4
from module4 import f4
#اجرای f4
f4()

  • خط ۶: توجه کنید که از مسیر مطلق فایل پیکربندی استفاده شده است؛
  • خطوط ۸–۹: فایل پیکربندی خوانده می‌شود. یک دیکشنری به نام [config] (خط ۹) از محتویات آن ساخته می‌شود؛
  • خطوط ۱۱–۱۳: عناصر آرایه [config['dependencies']] به مسیر پایتون اضافه می‌شوند. توجه داشته باشید که از آنجا که نام‌های مطلق پوشه در [config.json] مشخص شده‌اند، نام‌های مطلق به مسیر پایتون اضافه می‌شوند؛
  • خط 16: [module4] وارد می‌شود. این فایل باید پیدا شود، زیرا [dir2] اکنون در مسیر پایتون قرار دارد؛

اجرای اسکریپت نتایج مشابهی با [main_02] تولید می‌کند، با این تفاوت که اسکریپت حتی زمانی که دایرکتوری اجرای برنامه دیگر [import/05] نیست، به اجرا ادامه می‌دهد:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4

خط ۱: دایرکتوری اجرای برنامه [import] است.

ما پیشرفت کرده‌ایم. ما دیده‌ایم که:

  • که لازم بود مسیر پایتون را خودمان بسازیم؛
  • که باید مسیرهای مطلق تمام پوشه‌هایی را که ماژول‌های واردشده توسط برنامه در آن‌ها قرار دارند، وارد کنیم؛

با این حال، درج مسیرهای مطلق در اسکریپت‌ها راه‌حل مناسبی نیست. به محض انتقال پروژه به مکان دیگری، کار نمی‌کند. باید راه دیگری پیدا کنیم.

9.6. اسکریپت‌های [import_06]

Image

توجه: پوشه‌های [06, dir1, dir2] در داخل پوشه‌های [Sources Root] پروژه PyCharm قرار داده شده‌اند. پوشه‌های [dir1, dir2] با آنهایی که در مثال‌های قبلی هستند یکسان هستند.

فایل [config.json] به شرح زیر است:


{
  "rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
  "relativeDependencies": [
    "dir1",
    "dir2"
  ],
  "absoluteDependencies": [
   ]
}

ما دو نوع مسیر را معرفی می‌کنیم:

  • مسیرهای مطلق، خطوط ۷–۸؛
  • مسیرهای نسبی، خطوط ۳–۶. این‌ها نسبت به ریشه در خط ۲ هستند. بنابراین، وقتی پروژه به مکان دیگری منتقل شود، تنها همین خط نیاز به اصلاح دارد؛

اسکریپت [utils.py] از فایل [config.json] استفاده می‌کند و مسیر پایتون را می‌سازد:


#وارداتی
import codecs
import json
import os
import sys

# پیکربندی برنامه
def config_app(config_filename: str) -> dict:
    # config_filename: نام فایل پیکربندی
    # اجازه می‌دهد استثناءها منتقل شوند

    # پردازش فایل پیکربندی
    with codecs.open(config_filename, "r""utf-8"as file:
        config = json.load(file)
    # افزودن وابستگی‌ها به sys.path
    rootDir = config['rootDir']
    # افزودن وابستگی‌های نسبی پروژه به syspath
    for directory in config['relativeDependencies']:
        #وابستگی به ابتدای syspath اضافه می‌شود
        sys.path.insert(0f"{rootDir}/{directory}")
    #وابستگی‌های مطلق پروژه به syspath اضافه می‌شوند
    for directory in config['absoluteDependencies']:
        #وابستگی به ابتدای syspath اضافه می‌شود
        sys.path.insert(0, directory)
    # واژه‌نامه پیکربندی ایجاد می‌شود
    return config

# دایرکتوری اسکریپت اجرا شده
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • خط ۸: تابع [config_app] نام فایل پیکربندی را به‌عنوان پارامتر می‌پذیرد؛
  • خطوط ۱۲–۱۴: فایل پیکربندی برای ایجاد دیکشنری [config] استفاده می‌شود؛
  • خط ۲۰: [sys.path] فهرست دایرکتوری‌ها در مسیر پایتون است؛
  • خطوط 17–20: وابستگی‌های نسبی از فایل پیکربندی به مسیر پایتون اضافه می‌شوند. آنها در خط 20 به ابتدای آرایه [sys.path] اضافه می‌شوند. این به این دلیل است که وقتی پایتون به دنبال یک ماژول می‌گردد، دایرکتوری‌های موجود در [sys.path] را به ترتیب جستجو می‌کند. با این حال، در این سند، ماژول‌هایی با نام‌های یکسان در دایرکتوری‌های مختلف درون دایرکتوری [sys.path] قرار خواهند گرفت. با قرار دادن وابستگی‌های برنامه در ابتدای آرایه [sys.path]، اطمینان حاصل می‌کنیم که این موارد قبل از سایر دایرکتوری‌های موجود در [sys.path] که ممکن است ماژول‌هایی با نام‌های یکسان داشته باشند، جستجو شوند؛
  • خطوط 21–24: وابستگی‌های مطلق فایل پیکربندی به مسیر پایتون (Python Path) اضافه می‌شوند؛
  • خط ۲۶: پیکربندی برنامه بازگردانده می‌شود؛
  • خطوط ۲۹–۳۰: تابع [get_scriptdir] مسیر مطلق دایرکتوری حاوی اسکریپت در حال اجرا (همان دایرکتوری که تابع در آن فراخوانی شده است) را برمی‌گرداند؛

اسکریپت اصلی، [main به شرح زیر است:


# واردات‌ها
import sys

from utils import config_app


def affiche_path(msg: str):
    # پیام
    print(f"{msg}------------------------------")
    # sys.path
    for path in sys.path:
        print(path)


# main -------------
try:
    # sys.path پیکربندی شده است
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    #ماژول4 در حال وارد شدن است
    from module4 import f4
    #F4 اجرا
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • خط ۴: تابع [config_app] وارد می‌شود. توجه داشته باشید که از آنجایی که [utils] و [main] در یک پوشه قرار دارند، این [import] همیشه کار می‌کند. این به این دلیل است که پوشه اسکریپت اصلی به طور خودکار به مسیر پایتون (Python Path) اضافه می‌شود؛
  • خطوط ۷–۱۲: تابع [affiche_path] لیست پوشه‌ها را در مسیر پایتون نمایش می‌دهد؛
  • خط ۱۹: برنامه پیکربندی شده است. توجه داشته باشید که مسیر مطلق فایل پیکربندی به تابع [config_app] ارسال می‌شود. پس از این دستور، مسیر پایتون بازسازی شده است؛
  • خط ۲۲: [module4] وارد می‌شود. به لطف بازسازی مسیر پایتون، این ماژول پیدا خواهد شد؛
  • خط ۲۴: تابع [f4] اجرا می‌شود؛

در زمینه PyCharm، نتایج اجرای آن به شرح زیر است:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared

C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared
….
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

Process finished with exit code 0

توضیحات

  • خطوط ۲–۱۳: مسیر پایتون برای PyCharm. این مسیر شامل تمام پوشه‌های موجود در پروژه [Sources Root] است؛
  • خطوط 14–29: مسیر پایتون ساخته‌شده توسط تابع [config_app]. خطوط 15–16 شامل دو وابستگی هستند که ما اضافه کردیم؛
  • خطوط ۲۲–۲۷: دایرکتوری‌های سیستمی تفسیرگر پایتون که اسکریپت را اجرا کرده است؛
  • خطوط ۲۸–۲۹: اجرا به‌طور عادی ادامه می‌یابد؛

اکنون، بیایید به کنتکستی بازگردیم که پیش‌تر باعث خطای زمان اجرا شده بود:

  • یک ترمینال پایتون را باز کنید؛
  • به دایرکتوری دیگری غیر از دایرکتوری حاوی اسکریپت در حال اجرا بروید؛

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

این بار کار می‌کند (خطوط ۲۰–۲۱). توجه کنید که [sys.path] شامل همان دایرکتوری‌ها نیست که وقتی اسکریپت تحت PyCharm اجرا می‌شود.

9.7. اسکریپت‌های [import_07]

ما راه‌حل قبلی را به دو روش بهبود می‌دهیم:

  • ما فایل پیکربندی [config.json] را با یک اسکریپت [config.py] جایگزین می‌کنیم. دلیل این کار این است که فایل jSON مشکل قابل توجهی دارد: نمی‌توان روی آن نظرگذاری کرد. واژه‌نامه [config.json] را می‌توان با یک واژه‌نامه پایتون جایگزین کرد که این مزیت را دارد که امکان درج نظر (comment) را فراهم می‌کند؛
  • ما از ماژولی استفاده می‌کنیم که برای همه پروژه‌های پایتون روی ماشین قابل مشاهده است؛

9.7.1. نصب یک ماژول در سطح ماشین

Image

در بالا، ما یک پوشه به نام [packages/myutils] را در داخل پروژه PyCharm ایجاد می‌کنیم (این نام‌ها بی‌اهمیت هستند).

اسکریپت [myutils.py] به شرح زیر است:


# واردات‌ها
import sys
import os


def set_syspath(absolute_dependencies: list):
    #absolute_dependencies: فهرستی از نام‌های مطلق پوشه‌ها

    #وابستگی‌های مطلق پروژه به syspath اضافه می‌شوند
    for directory in absolute_dependencies:
        #بررسی می‌کند که آیا پوشه وجود دارد یا خیر
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            #یک استثنا ایجاد می‌شود
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            #پوشه به ابتدای syspath اضافه می‌شود
            sys.path.insert(0, directory)
  • خطوط ۶–۱۸: تابع [set_syspath] یک مسیر پایتون ایجاد می‌کند که از لیست پوشه‌هایی که به عنوان پارامتر به آن داده شده است استفاده می‌کند؛
  • خطوط ۱۲–۱۵: بررسی می‌کنیم که پوشه‌ای که باید به مسیر پایتون اضافه شود، وجود دارد؛

اسکریپت [__init.py__] (با دو زیرخط تکی قبل و بعد از نام؛ این قرارداد نام‌گذاری اجباری است) به شرح زیر است:


from .myutils import set_syspath

ما تابع [set_syspath] را از اسکریپت [myutils] وارد می‌کنیم. نشانه [.myutils] به مسیر [./myutils] اشاره دارد، به این معنی که اسکریپت [myutils] در همان پوشه‌ای قرار دارد که [__init.py] در آن قرار دارد. می‌توانستیم از notation [myutils] استفاده کنیم. با این حال، ما قصد داریم یک ماژول با دامنهٔ ماشین به نام [myutils] ایجاد کنیم. در نتیجه، notation [from myutils import set_syspath] مبهم خواهد شد. آیا این به وارد کردن اسکریپت [myutils] از پوشهٔ جاری اشاره دارد یا به اسکریپت در مقیاس ماشین [myutils]؟ نشانه‌گذاری [.myutils] این ابهام را برطرف می‌کند.

اسکریپت [setup.py] (در اینجا نیز نام ثابت است) به شرح زیر است:


from setuptools import setup

setup(name='myutils',
      version='0.1',
      description='Utilitaire fixant le Python Path',
      url='#',
      author='st',
      author_email='st@gmail.com',
      license='MIT',
      packages=['myutils'],
      zip_safe=False)

در این اسکریپت، ما مدولی را که قصد داریم ایجاد کنیم، توصیف می‌کنیم. در اینجا، آن را به‌صورت محلی ایجاد خواهیم کرد. با این حال، از همین فرآیند برای ایجاد یک مدول توزیع‌شده رسمی (به |pypi| مراجعه کنید) نیز استفاده می‌شود. نکات کلیدی در اینجا به شرح زیر است:

  • خط ۳: نام ماژول در حال ساخت؛
  • خط ۴: نسخهٔ ماژول؛
  • خط ۵: توضیح آن؛
  • خطوط ۷–۸: نویسنده ماژول؛

برای نصب این ماژول با دامنه کل ماشین، مراحل زیر را دنبال کنید:

Image

سپس، در ترمینال پایتون، دستور زیر را تایپ کنید: [pip install .]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
  Attempting uninstall: myutils
    Found existing installation: myutils 0.1
    Uninstalling myutils-0.1:
      Successfully uninstalled myutils-0.1
    Running setup.py install for myutils ... done
Successfully installed myutils-0.1

از این پس، هر اسکریپتی روی ماشین می‌تواند ماژول [myutils] را بدون اینکه در کد منبع پروژه گنجانده شود، وارد کند.

9.7.2. اسکریپت [config.py]

Image

اسکریپت [config.py] برنامه را پیکربندی می‌کند:


def configure():
    import os

    # مسیر مطلق پوشهٔ اسکریپت پیکربندی
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # مسیرهای مطلق پوشه‌هایی که باید به syspath اضافه شوند
    absolute_dependencies = [
        # پوشه‌های محلی
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    # به‌روزرسانی syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # پیکربندی بازگردانده می‌شود
    return {}
  • خط ۱: تابع [configure] برنامه را پیکربندی می‌کند؛
  • خطوط ۷–۱۰: دیکشنری‌ای که قبلاً در [config.json] بود؛
  • خطوط ۹–۱۰: از آنجا که این یک اسکریپت است، می‌توانیم مستقیماً از نام‌های مطلق پوشه‌ها استفاده کنیم ([dir1, dir2]);
  • خطوط ۱۲–۱۴: ما از تابع [set_syspath] در ماژول [myutils] که همین حالا ایجاد کرده‌ایم، برای تعریف مسیر پایتون برای پیکربندی استفاده می‌کنیم؛
  • خط ۲۰: ما دیکشنری پیکربندی برنامه را بازمی‌گردانیم. در اینجا، خالی است؛

9.7.3. اسکریپت [main.py]

اسکریپت اصلی، [main به شرح زیر است:


# راه‌اندازی برنامه
import config

config = config.configure()

#مسیر سیستم پیکربندی شده است – اکنون می‌توانیم واردات را انجام دهیم
from module4 import f4

# اصلی -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • خطوط ۲–۴: برنامه با استفاده از ماژول [config.py] پیکربندی می‌شود. این ماژول در دسترس است زیرا در همان پوشه‌ای قرار دارد که اسکریپت اصلی در آن است. پوشه اسکریپت اصلی همیشه بخشی از مسیر پایتون (Python Path) است؛
  • تا زمانی که به خط ۶ می‌رسیم، مسیر پایتون (Python Path) برای شامل کردن پوشه‌ای که ماژول [module4] را در خود دارد، ساخته شده است. بنابراین می‌توانیم این ماژول را در خط ۷ وارد (import) کنیم؛
  • خطوط ۱۰–۱۵: تنها کاری که باقی مانده فراخوانی تابع [f4] است؛

نتایج اجرای PyCharm به شرح زیر است:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/07/main.py
f3
f4
done

Process finished with exit code 0

در یک ترمینال پایتون، خارج از پوشهٔ اسکریپت اصلی، نتایج به شرح زیر است:

1
2
3
4
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 07/main.py
f3
f4
done

از این پس، ما همیشه برای راه‌اندازی یک برنامه از همین رویه پیروی خواهیم کرد:

  • وجود یک اسکریپت به نام [config.py] در پوشهٔ اسکریپت اصلی. این اسکریپت شامل تابع [configure] است که دو هدف را دنبال می‌کند:
    • مسیر پایتون را برای برنامه بسازید. برای این کار، [config.py] تمام پوشه‌هایی را که حاوی ماژول‌های مورد استفاده برنامه هستند فهرست می‌کند و مسیر پایتون را با استفاده از مسیرهای مطلق آن‌ها می‌سازد؛
    • ساخت فرهنگ لغت [config] پیکربندی برنامه؛

ما این رویکرد را برای نسخه دوم تمرین عملی به کار می‌بریم. همان‌طور که به یاد داریم، نسخه ۱ در محیط PyCharm کار می‌کرد اما در ترمینال پایتون کار نمی‌کرد. مشکل از مسیر پایتون نشأت می‌گرفت.