بنية مشروع Python, P2C

Ismail Mebsout
٢٣ أكتوبر ٢٠٢٤
٥ دقائق
جدول المحتويات

بصفتي مبرمجاً، أرى أنّ البرمجة فنّ. وعند العمل على مشروع معقّد، توجد خطوات كثيرة يجب اتّباعها لتطوير كود متّسق ومتين ومستدام يمكن لمساهمين آخرين قراءته واستئنافه:

Project's E2E steps
  • أولاً، من الضروري فهم المشكلة من أجل تلبية الحاجة الصحيحة
  • يمكن تقسيم المشروع إلى مشاريع فرعية تُسهّل المهمّة وخاصةً التعاون
  • الـ pipeline الخاص بك هو نتيجة مباشرة لمشاريعك الفرعية
  • ينبغي أن تتّبع بنية الكود التجزئة ذاتها بحيث يكون لديك نفس الـ Pipeline والمنطق التقني
  • يمكن اتّباع خطوات صغيرة في كل جزء من أجل تطوير كودك بفعّالية

في هذا المقال، سنرى كيف ننتقل من حاجة عمل إلى كود python وظيفيّ كامل يسهل استيعابه.

الملخّص كالتالي:

  1. مثال المشروع
  2. تنظيم المشروع
  3. الـ Imports في Python

مثال المشروع

من باب التوضيح، سنعتبر حاجة العمل التالية:

بصفتي مدير طريق سريع، أودّ تنفيذ عدّ يوميّ للمركبات التي تستخدم مساراً معيّناً. ولتلبية هذه الحاجة، تمّ تكليف فريق data science بالمشروع وقرّر استخدام كاميرا ثابتة وعدّ العدد الفريد من لوحات الترخيص.
(اقتراح ضمن اقتراحات أخرى)

يمكن النظر إلى هذه الفكرة باعتبارها تسلسلاً لعدّة خطوات (تجزئة مبسَّطة ضمن تجزئات أخرى):

  • كشف المركبة
  • كشف لوحة الترخيص
  • OCR لوحة الترخيص

وبالتالي، الـ pipeline التالي:

Technical pipeline

تنظيم المشروع

بالنظر إلى الـ pipeline أعلاه، يمكن للمرء تنظيم الكود كما يلي:

|--data/ #useful to store temporary files for instance
|--Tests/ #hosts the functional unit testings of your code/api
|--notebooks/ #helpful for testing and developping and debugging
|--|--develop.ipynb
|--weights/ #weights are kept a part for easier manipulation
|--counthighwaypy/ #folder hosting your entire code
|--|--detectvehicle/ #1st brick of your pipeline
|--|--|--detect_vehicle_main.py
|--|--|--detect_vehicle_utils.py
|--|--|--detect_vehicle_conf.py
|--|--|--Tests/ #independent unit testings relative to 1st brick
|--|--detectlicenseplate/ #2nd brick of your pipeline
|--|--|--licence_plate_main.py
|--|--|--licence_plate_utils.py
|--|--|--licence_plate_conf.py
|--|--|--Tests/ #independent unit testings relative to 1st brick
|--|--ocrlicenseplate/ #3rd brick of your pipeline
|--|--|--ocr_license_main.py
|--|--|--ocr_license_utils.py
|--|--|--ocr_license_conf.py
|--|--|--Tests/ #independent unit testings relative to 1st brick

|--|--utils.py
|--|--conf.py #! very  important file (see below)
|--|--main.py # orchestrator of the different bricks
|--|--Tests/ #E2E technical unit testings
+--README.md
+--app.py #hosts your API and calls the main.py
+--packages.txt #python environment
+--launch_tests.sh #functional unit testings
+--pytest.ini
+--Dockerfile

كما ذُكر في القسم السابق، تتّبع بنية المستودع المنطق ذاته داخل الـ pipeline.

  • تستضيف كلّ brick:
    + ملفّ utils: يحتوي على كل الدوال المساعدة لـ brick الخاصّة بك
    + ملفّ conf: يحتوي على كل المتغيّرات الثابتة (أسماء المتغيرات والمجلّدات وقيم الـ hyper-parameters …)
    + ملفّ main: يستضيف عادةً دالة واحدة تجمع كل دوال الخطوات الصغيرة في ملفّ utils
    + مجلّد Tests: يحتوي على unit testings تتيح تقييم الـ regressions والتحسينات الخاصة بـ brick المعنية بشكل مستقلّ عن الـ bricks الأخرى. وهو مبدأ أساسي يتيح debugging أسرع وأكثر فعالية.
  • عند العمل على خوارزميات machine learning، من الأفضل تخزين الـ weights المحتملة في جذر المشروع لأنّها قد تُستبدل كثيراً خلال التطوير.
  • يمكن تجربة الميزات الجديدة بسهولة باستخدام notebooks. وبالنظر إلى البنية، يجب أن يستضيف كلٌّ منها كود Python التالي للقدرة على "رؤية" واستيراد الـ module counthighwaypy:

import os
import sys
sys.path.insert(0, os.path.abspath("../")) #visible parents folders
from counthighwaypy import ...
### your code
  • داخل الـ module counthighwaypy، من المهمّ وجود ملفّ utils وملفّ conf وملفّ main الذي ينسّق الـ bricks المختلفة دون نسيان Tests الـ E2E
  • ملفّ conf أساسيّ جداً لأنّه يحدّد جذر المشروع ومختلف الـ sub-modules والـ directories. ويمكن كتابته كما يلي:
import os
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.realpath(__file__), "../.."))
##### directories
DATA = os.path.join(PROJECT_ROOT, "data/")
NOTEBOOK= os.path.join(PROJECT_ROOT, "notebooks")
SRC = os.path.join(PROJECT_ROOT, "counthighwaypy/")
WEIGHTS = os.path.join(PROJECT_ROOT, "weights/")
MODULE_DETECT_VEHICLE = os.path.join(SRC, "detectvehicle/")
MODULE_DETECT_LICENCE_PLATE = os.path.join(SRC, "detectlicenseplate/")
MODULE_OCR_LICENSE_PLATE = os.path.join(SRC, "ocrlicenseplate/")
  • يُغلّف ملفّ app مشروعك ضمن API يمكن للمستخدمين والخدمات الأخرى استهلاكه
  • تُوضع ملفّات إضافية مثل packages.txt وpytest.ini وDockerfile في جذر المشروع

بمجرّد ضبط بنية الكود، من الأفضل تطوير كل brick بصيغة مستقلّة بشكل منفصل عن غيرها. ومع ذلك، إليك بعض الإرشادات التي يمكنك اتّباعها:

  • اضبط صيغة الـ input والـ output لكلّ brick، حيث يكون مخرج الـ brick i هو مدخل الـ brick i+1
  • اكتب هيكل الكود (دوال فارغة) بطريقة بسيطة بحيث تفهم فوراً عند قراءته ما يفعله النصّ
  • لا تنسَ signatures والتعليقات
  • استخدم أداة versioning للكود، git مثلاً، لتعاون أكثر فعّالية
  • حافظ على نظافة كودك باستخدام code formatters وcode linters
  • للتعاون عبر الفرق، اعرض كودك/حزمتك كـ API قابل للاستهلاك

الـ Imports في Python

منذ Python 3.3، يُعتبر المجلّد folderame module (دون الحاجة إلى ملفّ __init__.py) ويمكن ببساطة استيراده في ملفّ Python، طالما أنّه مرئيّ أي على المستوى ذاته من الشجرة، باستخدام:

import foldername

لنفترض أنّ لدينا البنية التالية:

|--FOLDER1/ |--|--file1.py|--FOLDER2/ |--|--file2.py|--main.py
  • في main.py يمكننا
import FOLDER1.file1
import FOLDER2.file2
  • لاستيراد file2 في file1:
import os
import sys
#make FOLDER2 visible to file1 (one step up in the tree)
sys.path.insert(0, os.path.abspath("../"))
from FOLDER2 import file2

في مشروع Python معقّد، وللحفاظ على اتّساق الـ imports، يُنصح بأن تبدأها جميعاً من مصدر كودك. في حالتنا، ابدأ كلّ الـ imports في أيّ ملفّ .py بـ:

from counthighwaypy.xxx.xxx import xxx

الخاتمة

هناك طرق أخرى لهيكلة مشروع Python الخاصّ بك، لكنّي أرى أنّ الطريقة الموصوفة في هذا المقال سهلة الفهم وبسيطة الاتّباع. ويمكن تطبيقها أيضاً على لغات غير Python.

أتمنّى أن تكون قد استمتعت بقراءة هذا المقال وأن يساعدك على تنظيم عملك بشكل أفضل في المستقبل.
جميع التعليقات والاقتراحات مرحَّب بها!

بصفتي مبرمجاً، أرى أنّ البرمجة فنّ. وعند العمل على مشروع معقّد، توجد خطوات كثيرة يجب اتّباعها لتطوير كود متّسق ومتين ومستدام يمكن لمساهمين آخرين قراءته واستئنافه:

ابقَ على تواصل

هل لديك سؤال؟ يسعدنا أن نسمع منك.