الوحدة 3 — الدوالّ والوحدات وتنظيم الكود
يبدأ كود التحليل دائماً نصّاً خطّياً — ولا بأس بذلك. لكن ما إن تتكرّر معالجة أو يتجاوز مشروع بعد الظهر، تصبح الدوالّ والوحدات الفارقَ بين عمل قابل لإعادة الاستخدام وملفّ من 800 سطر يخشى كاتبه نفسه فتحه.
تشريح دالّة نظيفة
def missing_rate(column, alert_threshold=0.2):
"""تحسب نسبة القيم الناقصة في عمود.
تعيد صفّاً (rate, alert) حيث alert تساوي True إذا تجاوزت
النسبة alert_threshold.
"""
rate = column.isna().mean()
return rate, rate > alert_threshold
أربعة قرارات تصميم في هذه الأسطر الثمانية، كلّها قابلة للتعميم.
اسم يقول ما تفعله — فعل أو اسم دقيق، لا process_data. إن استلزم الاسم واو عطف (نظّف_واحفظ)، فالدالّة تفعل شيئين: قسّمها.
قيمة افتراضية للوسيط الثانوي: الاستدعاء الشائع يبقى بسيطاً، والحالة الخاصة تبقى ممكنة.
سلسلة توثيق (docstring) في السطر الأول: جملة عن الدور وجملة عن القيمة المرجعة. هي ما يعرضه help(), وهي التوثيق الذي لا ينفصل عن الكود أبداً لأنّه يعيش داخله.
إرجاع صريح. الدالّة التي تطبع (print) بدل أن تعيد (return) غير قابلة للاستخدام لاحقاً: لا يمكن اختبار نتيجتها ولا تمريرها إلى بقية الخطّ.
الوسائط الموضعية والمسمّاة
يتيح بايثون الاستدعاء بالموضع أو بالاسم أو بمزيج:
read_csv("sales.csv", ";", "utf-8", True) # غير مقروء
read_csv("sales.csv", sep=";", encoding="utf-8", header=True) # واضح
القاعدة العملية: بعد وسيطين، سمِّ الوسائط عند الاستدعاء. مكتبات البيانات تفرض ذلك عملياً: استدعاء حقيقي لـpd.read_csv يصفّ بسهولة خمسة وسائط مسمّاة، وهذا بالضبط ما يبقيه مقروءاً.
توقيعا *args و**kwargs (عدد متغيّر من الوسائط الموضعية / المسمّاة) يُقرآن أكثر ممّا يُكتبان يومياً: هما يفسّران قبول دوالّ الرسم في Matplotlib عشرات الخيارات دون إعلانها واحدة واحدة.
نطاق المتغيّرات: المحلّي أولاً
المتغيّرات المُنشأة داخل دالّة محلّية: تولد عند الاستدعاء وتموت عند الإرجاع. تستطيع الدالّة قراءة متغيّر عامّ، أمّا تعديله فيستلزم الكلمة global — وهي فكرة سيّئة دائماً تقريباً.
الدالّة التي تقرأ متغيّرات عامّة (df، config…) تعمل في الدفتر الذي وُلدت فيه ولا مكان غيره. الانضباط الذي يغيّر كلّ شيء: كلّ ما تحتاجه الدالّة يدخل عبر وسائطها؛ وكلّ ما تنتجه يخرج عبر قيمتها المرجعة. هذا المبدأ يجعل الكود قابلاً للاختبار والنقل — ويفكّك نصف مشكلات الدفاتر في الوحدة 10.
الوحدات والاستيراد: إعادة الاستخدام بلا نسخ ولصق
كلّ ملفّ .py وحدة قابلة للاستيراد. مشروع بيانات نمطي يُهيكَل هكذا:
project/
├── cleaning.py # دوالّ التحضير
├── visualization.py # دوالّ الرسم
├── analysis.ipynb # الدفتر الذي يستخدمها
└── requirements.txt # الاعتماديات (الوحدة 9)
# داخل analysis.ipynb
from cleaning import missing_rate, normalize_columns
import visualization as viz
أعراف الاستيراد في المنظومة تكاد تكون طقوساً — واحترامها يجعل كودك مألوفاً فوراً لأيّ قارئ:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
ممارستان يجب تجنّبهما: from module import * (لا أحد يعرف مصدر الأسماء بعدها) والاستيراد وسط الملفّ (كلّه في الأعلى: المكتبة القياسية أولاً، ثم مكتبات الطرف الثالث، ثم المحلّية).
كتلة if __name__ == "__main__"
هذا الحارس، الحاضر في كلّ نصّ جادّ، يفصل استخدامين للملفّ نفسه:
# cleaning.py
def normalize_columns(df):
...
if __name__ == "__main__":
# يعمل فقط عبر: python cleaning.py
# لا عند الاستيراد من الدفتر
df = pd.read_csv("raw.csv")
print(normalize_columns(df).head())
من دونه، كان الاستيراد سينفّذ كود الاختبار — بما فيه تحميل الملفّ. ومعه، يكون الملفّ مكتبةً قابلة للاستيراد ونصّاً قابلاً للتنفيذ في آن.
معالجة الأخطاء بلا إخفائها
try:
df = pd.read_csv(path)
except FileNotFoundError:
print(f"ملفّ غائب: {path} — تحقّق من نقطة تركيب البيانات")
raise
القاعدتان اللتان تجنّبان الفخاخ الكلاسيكية: التقط الاستثناء الدقيق (لا except: عارية أبداً، فهي تبتلع أيضاً الأخطاء الحقيقية ومقاطعات لوحة المفاتيح)، ولا تُخرِس خطأً أبداً — عالج الحالة أو أعد الرفع بـraise. خطّ المعالجة الذي يواصل على بيانات نصف محمّلة ينتج نتائج خاطئة بمظهر سليم تماماً.
الخلاصة
- الدالّة: دور واحد، مدخلات عبر الوسائط، مخرج عبر
return، سلسلة توثيق من جملة أو جملتين. - وسائط مسمّاة ما إن يتجاوز الاستدعاء وسيطين؛ قيم افتراضية للخيارات.
- المشروع = وحدات
.pyيستوردها الدفتر؛ أعرافnpوpdوpltوsns؛ الاستيرادات في الأعلى. if __name__ == "__main__"يفصل استخدام المكتبة عن استخدام النصّ.- التقط الاستثناءات الدقيقة، ولا تبتلع خطأً في صمت.
الوحدة التالية، قلب الحساب العلمي: NumPy ومصفوفاته والتوجيه الذي يحلّ محلّ الحلقات.