Skip to content

9. عمليات الاستيراد

الخطأ الذي واجهناه في الإصدار 1 من تمرين التطبيق يدفعنا إلى استكشاف دور عبارة [import] بمزيد من التعمق.

Image

9.1. البرامج النصية [import_01]

سيتم استيراد البرنامج النصي [imported] بواسطة برامج نصية مختلفة (تسمى أيضًا وحدات):

Image

1
2
3
4
5
#  imported module
#  this instruction will be executed each time the module is imported
print("2")
#  variable belonging to the imported module
x=4

يتم تنفيذ الوحدة النمطية عند استيرادها. لذا، عند استيراد الوحدة النمطية [المستوردة]:

  • سيظهر ناتج السطر 3؛
  • سيتم تعيين قيمة للمتغير x في السطر 5؛

النص البرمجي [main_01] هو كما يلي:

1
2
3
4
#  an imported module is executed
import imported
#  use of the x variable of the imported module
print(imported.x)
  • السطر 2: يتم استيراد الوحدة النمطية [imported]. سيؤدي ذلك إلى تنفيذها:
    • سيتم عرض القيمة 2؛
    • يتم إنشاء المتغير x بالقيمة 4؛
  • السطر 4: يتم استخدام المتغير x من الوحدة المستوردة؛

في PyCharm، يتم الإبلاغ عن خطأ:

في [1]، يشير PyCharm إلى أنه لا يتعرف على الوحدة النمطية [imported]. من الناحية الفنية، هذا يعني أن المجلد الذي يحتوي على الوحدة النمطية [imported] غير موجود في مسار Python في PyCharm. مسار Python هو مجموعة المجلدات التي يتم البحث فيها عن الوحدات النمطية المستوردة. لحل هذه المشكلة، ما عليك سوى تعيين المجلد الذي يحتوي على الوحدة النمطية [المستوردة] كمجلد [Root Sources]، وهو في هذه الحالة المجلد [import/01]:

Image

Image

بعد هذه الخطوة، تتم إضافة المجلد [import/01] إلى مسار Python في 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

تعليقات

  • السطر 2 هو نتيجة تنفيذ الوحدة المستوردة؛
  • السطر 3 يعرض قيمة المتغير x من الوحدة المستوردة؛

النقطة الأساسية المستفادة من هذا المثال هي المفهوم المهم المتمثل في أن الوحدة النمطية (أو البرنامج النصي) المستوردة يتم تنفيذها.

النص البرمجي [main_02] هو كما يلي:

1
2
3
4
#  import the x variable from the imported module
from imported import x
#  we display it
print(x)
  • يستخدم السطر 2 صيغة استيراد مختلفة [from module import object1, object2, …]. هنا، نستورد المتغير [imported.x]. باستخدام هذه الصيغة، يصبح المتغير x متغيرًا في البرنامج النصي [main_02]. لم نعد بحاجة إلى إرفاق اسم الوحدة النمطية [imported] به؛
  • السطر 4: نطبع المتغير 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] هو كما يلي:

1
2
3
4
#  import everything visible in the imported module
from imported import *
#  use the x variable of the imported module
print(x)

تعني صيغة [import *] في السطر 2 أننا نستورد جميع الكائنات المرئية من الوحدة المستوردة (المتغيرات، الدوال).

والنتائج هي كما يلي:

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] هو كما يلي:

1
2
3
4
5
#  import the x variable from the imported module
#  and rename it y
from imported import x as y
#  display variable y
print(y) 

يُظهر السطر 3 أنه يمكننا استيراد كائن من الوحدة المستوردة وإعطائه اسمًا مستعارًا. هنا، تصبح المتغير [imported.x] المتغير [main_04.y]. النتائج هي نفسها كما في السابق.

9.2. النص البرمجي [import_02]

Image

الوحدة المستوردة [module1.py] هي كما يلي:

1
2
3
#  a function
def f1():
    print("f1")

تُعرّف الوحدة المستوردة دالة، وهو سيناريو شائع.

النص البرمجي [main_01] هو كما يلي:

1
2
3
4
#  import
import module1
#  execution f1
module1.f1()
  • السطر 2: يتم استيراد الوحدة النمطية. سيتم تنفيذها. هنا، لا يتم عرض أي شيء؛
  • السطر 4: يتم تنفيذ الدالة [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 من الإبلاغ عن خطأ في الاستيراد في السطر 2، يجب وضع المجلد الذي يحتوي على [module1] في [Root Directories] في PyCharm:

Image

في [1]، يتحول لون المجلد [02] الموجود في دليل [Root Sources] إلى اللون الأزرق. لاحظ أن الخطأ المبلغ عنه لا يمنع البرامج النصية من العمل بشكل صحيح هنا. في الواقع، عند تنفيذ البرنامج النصي [main_0x]، تتم إضافة مجلد البرنامج النصي تلقائيًا إلى مسار Python. ونتيجة لذلك، يتم العثور على [module1]. من الآن فصاعدًا، عندما يظهر مجلد باللون الأزرق في لقطة شاشة، فهذا يعني أنه تم وضعه في [Root Sources] في PyCharm.

النص البرمجي [main_02] هو كما يلي:

1
2
3
4
#  import
from module1 import f1
#  execution f1
f1()
  • يستورد السطر 2 الدالة [f1] من الوحدة النمطية [module1]؛
  • في السطر 4، يتم استخدام الدالة f1؛

النتائج مطابقة لتلك الخاصة بالنص البرمجي [main_01].

9.3. البرامج النصية [import_03]

Image

ملاحظة: [03] موجود في [Root Sources] للمشروع.

ستقوم البرامج النصية الجديدة باستيراد الوحدة النمطية [module2]، التي لا توجد في نفس المجلد الذي توجد فيه.

النص البرمجي [module2] هو كما يلي:

1
2
3
#  a function
def f2():
    print("f2")

وبالتالي، فإن البرنامج النصي يُعرّف دالة [f2].

النص البرمجي [main_01] هو كما يلي:

1
2
3
4
#  class2 module import
import dir1.module2
#  execution f2
dir1.module2.f2()
  • السطر 2: نستخدم ترميزًا خاصًا للإشارة إلى كيفية العثور على الوحدة النمطية [module2]. يجب قراءة [dir1.module2] على أنها المسار [dir1/module2]: للعثور على [module2]، ابدأ من مجلد البرنامج النصي الحالي [main_01]، ثم انتقل إلى [dir1]، وستجد هناك [module2]. ضع في اعتبارك أن نقطة البداية للمسار هي مجلد البرنامج النصي الذي يقوم بالاستيراد؛
  • السطر 4: لتنفيذ الدالة [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

السطر 2، نتيجة دالة [f2].

النص البرمجي [main_02] هو كما يلي:

1
2
3
4
#  import module dir1.module2 and rename it
import dir1.module2 as module2
#  execution f2
module2.f2()

في السطر 2، نقوم بإعادة تسمية الوحدة النمطية [dir1.module] لتبسيط الكود في السطر 4.

النص البرمجي [main_03] هو كما يلي:

1
2
3
4
#  import function f2 from module dir1.module2
from dir1.module2 import f2
#  execution f2
f2()

هذه المرة، في السطر 2، نقوم باستيراد الدالة [f2] فقط، والتي تصبح بعد ذلك دالة في البرنامج النصي [main_03] (السطر 4).

تعمل جميع هذه البرامج النصية في بيئة PyCharm بنفس الكفاءة التي تعمل بها في وحدة تحكم Python. والسبب هو أن دليل البرنامج النصي الذي يتم تنفيذه —وهو هنا الدليل [03]— هو جزء من مسار Python في كلتا الحالتين. ونتيجة لذلك، يتم العثور على الدليل [dir1/module2].

9.4. البرامج النصية [import_04]

Image

هنا، تم وضع المجلدين [dir1] و [dir2] في [Root Sources] لمشروع PyCharm.

الوحدة النمطية الأولى التي تم استيرادها هي [module3]:

1
2
3
#  a function
def f3():
    print("f3")

الوحدة الثانية التي تم استيرادها هي [module4]:

1
2
3
4
5
6
from module3 import f3

#  a function
def f4():
    f3()
    print("f4")
  • السطر 1: نستورد الدالة [f3] من [module3]. وهنا، يكون [module3] مرئيًا لأننا وضعنا دليله [dir1] في [Root Sources]؛
  • الأسطر 4–6: نُعرّف دالة [f4] تستدعي الدالة [f3] من [module3]؛

النص البرمجي الرئيسي [main_01] هو كما يلي:

1
2
3
4
#  import module4
from module4 import f4
#  execution f4
f4()
  • السطر 2: استيراد الوحدة النمطية [module4]. هذا مرئي لأننا وضعنا دليلها [dir2] في [Root Sources] في PyCharm؛
  • السطر 4: تنفيذ الدالة [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] في محطة Python (وحدة التحكم):

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 مسار Python الخاص بـ PyCharm أو [Root Sources]. لديها مسار Python خاص بها. في هذا المسار، يوجد دائمًا مجلد البرنامج النصي الذي يتم تنفيذه، وهو في هذه الحالة البرنامج النصي [main_01]. وبالتالي فهي تعرف مجلد [import/04]. في البرنامج النصي المنفذ، تجد السطر:


from module4 import f4

يبحث مترجم Python عن [module4] في مجلدات مسار Python الخاص به. ومع ذلك، لا يوجد [module4] في [import/04]—الذي يقع بالفعل في مسار Python—ولكن في [import/04/dir2 الذي لا يقع فيه. ومن هنا يأتي الخطأ.

إذن لدينا مشكلة واجهناها من قبل: قد يتعطل البرنامج النصي الذي يعمل بشكل صحيح في PyCharm في محطة Python. هذه مشكلة متكررة سنحتاج إلى حلها.

9.5. البرامج النصية [import_05]

Image

ملاحظة: تمت إضافة مجلدي [dir1] و [dir2] إلى مسار Python. لاحظ أن هناك تعارضًا بالفعل هنا: سيتم العثور على [module3] و [module4] في موقعين في مسار Python في PyCharm:

  • في [import/04/dir1] و [import/05/dir1] لـ [module3]؛
  • في [import/04/dir2] و[import/05/dir2] لـ [module4]؛

يمكننا بعد ذلك إزالة [import/04/dir1] و [import/04/dir2] من [Root Sources] لمشروع PyCharm. اتضح أن [import/05/dir1] هنا هو نسخة من [import/04/dir1] (وينطبق الأمر نفسه على [dir2])، لذا لا توجد مشكلة. ومع ذلك، تجدر الإشارة إلى أنه داخل PyCharm نفسه، يجب الانتباه إلى قائمة المجلدات في [Root Sources] لتجنب التعارضات.

يصبح البرنامج النصي [main_01] كما يلي:

import sys
#  modify sys.path to include folders
#  containing the classes to be imported
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
#  import module4
from module4 import f4
#  execution f4
f4()

نحن نحاول حل مشكلة مسار Python. نريد مسارًا يعمل في PyCharm بنفس الكفاءة التي يعمل بها في محطة Python. للقيام بذلك، سنقوم بإعداده بأنفسنا.

  • الأسطر 4–6: نضيف الدلائل [., ./dir1, ./dir2] إلى مسار Python. لكي يعمل هذا، يجب أن يكون الدليل الحالي أثناء التشغيل هو الدليل [import/05]. سيكون هذا صحيحًا في PyCharm ولكنه ليس صحيحًا بالضرورة في محطة Python، كما سنرى؛
  • السطر 8: نقوم باستيراد [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

الآن، في محطة Python:


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

السطر 1، دليل التنفيذ هو [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'
  • السطر 2: عند تنفيذ [main_01]، لم نعد في المجلد [import/05] بل في [import]. ومع ذلك، كتبنا:

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

يؤدي هذا إلى إضافة المجلدات [import، import/dir1، import/dir2] إلى مسار Python، وهو ما لا نريده على الإطلاق. لاحظ أن إضافة مجلدات غير موجودة (import/dir1، import/dir2) إلى مسار Python لا تسبب أخطاء.

لقد أحرزنا تقدمًا، لكن هذا لا يكفي. نحتاج إلى إضافة مسارات مطلقة إلى مسار Python، وليس مسارات نسبية.

البرنامج النصي [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] هي قائمة الدلائل التي سيتم إضافتها إلى مسار Python. لاحظ أننا استخدمنا هنا مسارات مطلقة بدلاً من مسارات نسبية.

يستخدم البرنامج النصي [main_02] ملف [config.json] على النحو التالي:

import codecs
import json
import sys

#  configuration file
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
#  reading the json configuration file
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

#  modification of sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

#  import module4
from module4 import f4
#  execution f4
f4()
  • السطر 6: لاحظ أننا استخدمنا المسار المطلق لملف التكوين؛
  • السطران 8-9: يتم قراءة ملف التكوين. يتم إنشاء قاموس [config] (السطر 9) من محتوياته؛
  • الأسطر 11-13: تتم إضافة عناصر المصفوفة [config['dependencies']] إلى مسار Python. لاحظ أنه نظرًا لاستخدامنا أسماء مجلدات مطلقة في [config.json]، فإننا نضيف أسماء مطلقة إلى مسار Python؛
  • السطر 16: يتم استيراد [module4]. يجب العثور عليه لأن [dir2] موجود الآن في مسار Python؛

يؤدي التنفيذ إلى نفس النتائج التي تم الحصول عليها في [main_02]، باستثناء أن البرنامج النصي يستمر في العمل حتى عندما لا يكون دليل التنفيذ هو [import/05] بعد الآن:


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

السطر 1، دليل التنفيذ هو [import].

لقد أحرزنا تقدماً. لقد رأينا:

  • أنه كان علينا إنشاء مسار Python بأنفسنا؛
  • أنه كان علينا تضمين المسارات المطلقة لجميع المجلدات التي تحتوي على الوحدات النمطية التي يستوردها التطبيق؛

ومع ذلك، فإن وضع المسارات المطلقة في البرامج النصية ليس حلاً. فبمجرد نقل المشروع إلى موقع آخر، يتوقف عن العمل. نحتاج إلى إيجاد طريقة أخرى.

9.6. البرامج النصية [import_06]

Image

ملاحظة: تم وضع المجلدات [06، dir1، dir2] في [Root Sources] لمشروع PyCharm. المجلدان [dir1، dir2] مطابقان لتلك الموجودة في الأمثلة السابقة.

ملف [config.json] هو كما يلي:


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

نقدم نوعين من المسارات:

  • المسارات المطلقة، الأسطر 7–8؛
  • المسارات النسبية، الأسطر 3–6. هذه المسارات نسبية بالنسبة إلى الجذر الموجود في السطر 2. وبالتالي، عندما ينتقل المشروع إلى موقع جديد، لا يلزم تعديل سوى هذا السطر؛

يستخدم البرنامج النصي [utils.py] ملف [config.json] ويبني مسار Python:

#  imports
import codecs
import json
import os
import sys

#  application configuration
def config_app(config_filename: str) -> dict:
    #  config_filename: name of configuration file
    #  we let the exceptions rise

    #  using the configuration file
    with codecs.open(config_filename, "r", "utf-8") as file:
        config = json.load(file)
    #  add dependencies to sys.path
    rootDir = config['rootDir']
    #  add the project's relative dependencies to the syspath
    for directory in config['relativeDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, f"{rootDir}/{directory}")
    #  we add the project's absolute dependencies to the syspath
    for directory in config['absoluteDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, directory)
    #  return the configuration dictionary
    return config

#  executed script file
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • السطر 8: تستقبل الدالة [config_app] اسم ملف التكوين كمعلمة؛
  • الأسطر 12–14: يُستخدم ملف التكوين لإنشاء قاموس [config]؛
  • السطر 20: [sys.path] هي قائمة الدلائل الموجودة في مسار Python؛
  • الأسطر 17–20: تُضاف التبعيات النسبية من ملف التكوين إلى مسار Python. تُضاف إلى بداية قائمة [sys.path]، السطر 20. وذلك لأن Python، عند البحث عن وحدة نمطية، يستكشف الدلائل الموجودة في [sys.path] بالترتيب. ومع ذلك، في هذا المستند، ستوجد الوحدات النمطية التي تحمل الأسماء نفسها في دلائل مختلفة داخل [sys.path]. من خلال وضع تبعيات التطبيق في بداية مصفوفة [sys.path]، نضمن البحث عنها قبل الدلائل الأخرى في [sys.path] التي قد تحتوي على وحدات نمطية تحمل الأسماء نفسها؛
  • الأسطر 21–24: تتم إضافة التبعيات المطلقة لملف التكوين إلى مسار Python؛
  • السطر 26: يتم إرجاع تكوين التطبيق؛
  • السطران 29 و30: تُرجع الدالة [get_scriptdir] المسار المطلق للدليل الذي يحتوي على البرنامج النصي قيد التشغيل حاليًا (الدليل الذي يوجد فيه استدعاء الدالة)؛

النص البرمجي الرئيسي [main] هو كما يلي:

#  imports
import sys

from utils import config_app


def affiche_path(msg: str):
    #  message
    print(f"{msg}------------------------------")
    #  sys.path
    for path in sys.path:
        print(path)


#  hand -------------
try:
    #  the sys.path is configured
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    #  import module4
    from module4 import f4
    #  execution f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • السطر 4: يتم استيراد الدالة [config_app]. لاحظ أنه نظرًا لوجود [utils] و [main] في نفس الدليل، فإن هذا [import] يعمل دائمًا. وذلك لأن دليل البرنامج النصي الرئيسي يُضاف تلقائيًا إلى مسار Python؛
  • الأسطر 7–12: تعرض الدالة [display_path] قائمة المجلدات الموجودة في مسار Python؛
  • السطر 19: يتم تكوين التطبيق. لاحظ أن المسار المطلق لملف التكوين يتم تمريره إلى دالة [config_app]. بعد هذا البيان، يتم إعادة بناء مسار Python؛
  • السطر 22: نقوم باستيراد [module4]. وبفضل إعادة بناء مسار Python، سيتم العثور على هذه الوحدة النمطية؛
  • السطر 24: يتم تنفيذ الدالة [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

تعليقات

  • الأسطر 2–13: مسار Python في PyCharm. ويشمل جميع المجلدات الموجودة في [Root Sources] بالمشروع؛
  • الأسطر 14–29: مسار Python الذي تم إنشاؤه بواسطة الدالة [config_app]. تحتوي الأسطر 15–16 على التبعيتين اللتين أضفناهما؛
  • الأسطر 22–27: الدلائل النظامية لمترجم Python الذي نفذ البرنامج النصي؛
  • الأسطر 28–29: يستمر التنفيذ بشكل طبيعي؛

الآن، لنعد إلى السياق الذي تسبب سابقًا في حدوث خطأ في وقت التشغيل:

  • افتح محطة Python؛
  • انتقل إلى دليل آخر غير الذي يحتوي على البرنامج النصي الذي تم تنفيذه؛

(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

هذه المرة يعمل (السطران 20–21). لاحظ أن [sys.path] لا يحتوي على نفس الدلائل كما هو الحال عند التشغيل في PyCharm.

9.7. البرامج النصية [import_07]

نقوم بتحسين الحل السابق بطريقتين:

  • نستبدل ملف التكوين [config.json] بنص برمجي [config.py]. في الواقع، يمثل ملف JSON مشكلة كبيرة: لا يمكن إضافة تعليقات عليه. يمكن استبدال قاموس [config.json] بقاموس Python، الذي يتميز بإمكانية إضافة التعليقات عليه؛
  • نستخدم وحدة نمطية مرئية لجميع مشاريع Python على الجهاز؛

9.7.1. تثبيت وحدة على مستوى الجهاز

Image

أعلاه، نقوم بإنشاء مجلد [packages/myutils] في مشروع PyCharm (الأسماء لا تهم).

النص البرمجي [myutils.py] هو كما يلي:

#  imports
import sys
import os


def set_syspath(absolute_dependencies: list):
    #  absolute_dependencies: a list of absolute folder names

    #  add the project's absolute dependencies to the syspath
    for directory in absolute_dependencies:
        #  we check the existence of the file
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            #  an exception is lifted
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            #  add the folder at the beginning of the syspath
            sys.path.insert(0, directory)
  • الأسطر 6–18: تقوم الدالة [set_syspath] بإنشاء مسار Python Path باستخدام قائمة الدلائل التي تم تمريرها إليها كمعلمة؛
  • الأسطر 12–15: نتحقق من وجود الدليل المراد إضافته إلى مسار Python؛

فيما يلي نص البرنامج النصي [__init.py__] (يجب أن يسبق الاسم ويليه شرطان سفليان؛ وهذا أمر إلزامي):


from .myutils import set_syspath

نقوم باستيراد الدالة [set_syspath] من البرنامج النصي [myutils]. تشير التسمية [.myutils] إلى المسار [./myutils]، مما يعني أن البرنامج النصي [myutils] موجود في نفس الدليل الذي يوجد فيه [__init.py]. كان بإمكاننا استخدام الترميز [myutils]. ومع ذلك، سنقوم بإنشاء وحدة [myutils] على مستوى الجهاز. ونتيجة لذلك، سيصبح الترميز [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|). النقاط المهمة هنا هي كما يلي:

  • السطر 3: اسم الوحدة النمطية التي يتم إنشاؤها؛
  • السطر 4: إصدار الوحدة النمطية؛
  • السطر 5: وصفها؛
  • السطران 7-8: مؤلف الوحدة النمطية؛

لتثبيت هذه الوحدة على مستوى الجهاز، اتبع الخطوات التالية:

Image

ثم، في محطة Python، اكتب الأمر التالي [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

    #  absolute name of the configuration script folder
    script_dir = os.path.dirname(os.path.abspath(__file__))
    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        #  local files
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • السطر 1: تتولى الدالة [configure] إدارة تكوين التطبيق؛
  • الأسطر 7–10: القاموس الذي كان موجودًا سابقًا في [config.json]؛
  • السطور 9-10: نظرًا لأننا في برنامج نصي، يمكننا الوصول مباشرةً إلى أسماء المجلدات المطلقة [dir1، dir2]؛
  • الأسطر 12–14: نستخدم دالة [set_syspath] من وحدة [myutils] التي أنشأناها للتو لتعيين مسار Python للتكوين؛
  • السطر 20: نُرجع قاموس تكوين التطبيق. هنا، يكون فارغًا؛

9.7.3. البرنامج النصي [main.py]

النص البرمجي الرئيسي [main] هو كما يلي:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from module4 import f4

#  hand -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • الأسطر 2–4: نقوم بتكوين التطبيق باستخدام الوحدة النمطية [config.py]. يمكن الوصول إليها لأنها موجودة في نفس الدليل الذي يوجد فيه البرنامج النصي الرئيسي. ومع ذلك، فإن دليل البرنامج النصي الرئيسي يكون دائمًا جزءًا من مسار Python؛
  • وبحلول الوقت الذي نصل فيه إلى السطر 6، يكون مسار Python قد تم إنشاؤه ليشمل مجلد الوحدة النمطية [module4]. وبالتالي يمكننا استيرادها في السطر 7؛
  • الأسطر 10–15: كل ما تبقى هو تنفيذ الدالة [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

في محطة Python خارج دليل البرنامج النصي الرئيسي، تكون النتائج كما يلي:

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] التي تخدم غرضين:
    • إنشاء مسار Python للتطبيق. للقيام بذلك، يسرد [config.py] جميع المجلدات التي تحتوي على الوحدات النمطية التي يستخدمها التطبيق ويقوم بإنشاء مسار Python باستخدام مساراتها المطلقة؛
    • إنشاء قاموس [config] لتكوين التطبيق؛

نطبق هذا النهج على النسخة الثانية من التمرين العملي. كما تذكرون، كانت النسخة الأولى تعمل في بيئة PyCharm ولكنها لم تعمل في محطة Python. كانت المشكلة ناجمة عن مسار Python.