🧠 مفاهيم بايثون المتقدمة

🧠 مفاهيم بايثون المتقدمة

الأدوات التي تميز المبرمج المحترف — كودك سيصبح أقصر وأسرع وأكثر أناقة.

⚡ Lambda 🎯 *args / **kwargs 🎨 Decorators ⚙️ Generators 📐 Comprehensions 🔗 map / filter / reduce 🗝️ Context Managers 🏷️ Type Hints

١. Lambda — دوال مجهولة الهوية

lambda هي دالة مختصرة في سطر واحد — بدون اسم، بدون def، مثالية للعمليات البسيطة.

🐢 الطريقة العادية
def double(x):
    return x * 2

print(double(5))  # 10
⚡ Lambda
double = lambda x: x * 2

print(double(5))  # 10
Python — Lambda في الواقع
# lambda متعددة المعاملات
add   = lambda a, b: a + b
greet = lambda name: f"مرحباً {name}!"
print(add(3, 7))         # 10
print(greet("خليل"))  # مرحباً خليل!

# ترتيب قائمة بمعيار مخصص
students = [
    {"name": "خليل",  "grade": 18},
    {"name": "فاطمة", "grade": 19},
    {"name": "أحمد",  "grade": 14},
]
# ترتيب تنازلي حسب الدرجة
sorted_s = sorted(students, key=lambda s: s["grade"], reverse=True)
for s in sorted_s:
    print(f"{s['name']}: {s['grade']}")

# ترتيب نصوص بطول الكلمة
words = ["بايثون", "سهل", "ومفيد", "جداً"]
print(sorted(words, key=lambda w: len(w)))

🧠 سؤال سريع

ما الناتج؟ f = lambda x, y: x ** y; print(f(2, 3))

6
5
8
23
✅ صحيح! 2 ** 3 = 2³ = 8
❌ خطأ. ** هي عملية الأس (power) — 2³ = 8

٢. *args و **kwargs — دوال مرنة

*args تقبل عدداً غير محدود من المعاملات. **kwargs تقبل أزواج مفتاح-قيمة.

Python — *args
# *args — قائمة من القيم
def total(*numbers):
    print(f"استقبلت: {numbers}")    # tuple
    return sum(numbers)

print(total(1, 2))               # 3
print(total(1, 2, 3, 4, 5))    # 15
print(total(*[10, 20, 30]))      # فك القائمة → 60

# دالة تطبع أي شيء بأي فاصل
def my_print(*items, sep=" | "):
    print(sep.join(str(i) for i in items))

my_print("خليل", 14, "مراكش")       # خليل | 14 | مراكش
my_print(1, 2, 3, sep=" → ")       # 1 → 2 → 3
Python — **kwargs
# **kwargs — قاموس من المعاملات
def create_profile(**info):
    print("=== بطاقة التعريف ===")
    for key, val in info.items():
        print(f"  {key}: {val}")

create_profile(name="خليل", age=14, city="مراكش", hobby="برمجة")

# الجمع بين الثلاثة
def mixed(required, *args, **kwargs):
    print(f"مطلوب : {required}")
    print(f"args  : {args}")
    print(f"kwargs: {kwargs}")

mixed("مرحبا", 1, 2, 3, lang="python", version=3)
# مطلوب : مرحبا
# args  : (1, 2, 3)
# kwargs: {'lang': 'python', 'version': 3}

٣. Decorators — تعديل الدوال بأناقة

الـ decorator هو دالة تلف دالة أخرى وتضيف لها سلوكاً دون تعديل كودها. يُعبّر عنه بـ @.

Python — كيف يعمل Decorator
# decorator بسيط
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"⏳ جاري تشغيل: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"✅ انتهى: {func.__name__}")
        return result
    return wrapper

@my_decorator
def greet(name):
    print(f"مرحباً {name}!")

greet("خليل")
# ⏳ جاري تشغيل: greet
# مرحباً خليل!
# ✅ انتهى: greet

Decorators المفيدة في العمل

Python — Decorators احترافية
import time
from functools import wraps

# ١. قياس وقت التنفيذ
def timer(func):
    @wraps(func)   # يحافظ على اسم الدالة
    def wrapper(*args, **kwargs):
        t      = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"⏱️ {func.__name__}: {time.perf_counter()-t:.4f}s")
        return result
    return wrapper

# ٢. التحقق من الإدخال
def validate_positive(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        for a in args:
            if isinstance(a, (int, float)) and a < 0:
                raise ValueError(f"يجب أن تكون الأرقام موجبة! وصلني: {a}")
        return func(*args, **kwargs)
    return wrapper

# ٣. Retry تلقائي
def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*a, **kw):
            for i in range(times):
                try:
                    return func(*a, **kw)
                except Exception as e:
                    print(f"محاولة {i+1}/{times} فشلت: {e}")
            raise Exception(f"فشل بعد {times} محاولات")
        return wrapper
    return decorator

# استخدام
@timer
@validate_positive
def sqrt_of(n):
    import math
    return math.sqrt(n)

print(sqrt_of(144))  # 12.0

🧠 سؤال سريع

في بايثون، @decorator فوق دالة يعني:

تعليق (comment) على الدالة
func = decorator(func) — يلف الدالة بدالة أخرى
استيراد مكتبة للدالة
جعل الدالة خاصة (private)
✅ ممتاز! @timer يعادل تماماً: my_func = timer(my_func)
❌ خطأ. الـ decorator يلف الدالة، أي @timer يعادل func = timer(func).

٤. Generators — توليد البيانات بكفاءة

الـ generator يولّد العناصر واحدة بواحدة عند الطلب دون تحميل كل شيء في الذاكرة — مفيد مع ملايين السجلات.

Python — Generator Function
# دالة عادية vs Generator
# العادية — تُعيد كل شيء دفعة (استهلاك ذاكرة)
def squares_list(n):
    return [x**2 for x in range(n)]

# Generator — تُعيد واحدة بواحدة (yield)
def squares_gen(n):
    for x in range(n):
        yield x**2      # yield بدل return

# الاستخدام
for sq in squares_gen(5):
    print(sq, end=" ")   # 0 1 4 9 16

# Generator Expression (مثل list comprehension لكن بقوسين)
gen = (x**2 for x in range(1000000))
print(next(gen))   # 0
print(next(gen))   # 1
print(next(gen))   # 4

# مثال حقيقي: قراءة ملف ضخم
def read_large_file(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.strip()
# يقرأ سطراً بسطر دون تحميل الملف كله — يعمل مع ملفات GB!

# Fibonacci generator لا نهائي
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print([next(fib) for _ in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

٥. Comprehensions — كود مختصر وسريع

Python — الأنواع الأربعة
# ١. List Comprehension
evens    = [x for x in range(20) if x % 2 == 0]
upper    = [w.upper() for w in ["python", "is", "great"]]
nested   = [(x, y) for x in range(3) for y in range(3)]

# ٢. Dict Comprehension
names    = ["خليل", "أحمد", "سارة"]
lengths  = {n: len(n) for n in names}
# {'خليل': 4, 'أحمد': 4, 'سارة': 4}

squares_d = {x: x**2 for x in range(1, 6)}
# {1:1, 2:4, 3:9, 4:16, 5:25}

# ٣. Set Comprehension
unique   = {x**2 for x in [-3, -2, 0, 2, 3]}
# {0, 4, 9}  — لا تكرار

# ٤. Generator Expression
total    = sum(x**2 for x in range(1000000))  # كفؤ جداً

# مثال متقدم: تفليح JSON
raw = [
    {"name":"خليل", "grade":18, "active":True},
    {"name":"أحمد",  "grade":9,  "active":False},
    {"name":"سارة",  "grade":16, "active":True},
]
# استخرج أسماء الناجحين النشيطين
top = [s["name"] for s in raw
       if s["active"] and s["grade"] >= 10]
print(top)   # ['خليل', 'سارة']

٦. map، filter، reduce

Python — الدوال الوظيفية
from functools import reduce

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map — طبّق دالة على كل عنصر
doubled = list(map(lambda x: x * 2, nums))
print(doubled)  # [2, 4, 6, 8, ...]

# filter — احتفظ بالعناصر المطابقة
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens)   # [2, 4, 6, 8, 10]

# reduce — اجمع كل العناصر في قيمة واحدة
product = reduce(lambda x, y: x * y, nums)
print(product)  # 10! = 3628800

# دمج map + filter
data = ["  مرحبا  ", "  بايثون  ", "  رائع  "]
clean = list(map(str.strip, data))
print(clean)   # ['مرحبا', 'بايثون', 'رائع']

# Tip: في بايثون الحديث يُفضّل Comprehensions على map/filter
evens2 = [x for x in nums if x % 2 == 0]  # أوضح

٧. Context Managers — with

الـ with يضمن تنفيذ كود التنظيف دائماً — حتى لو حدث خطأ. رأيناه مع الملفات، لكن يمكن إنشاء مخصص.

Python — Context Manager مخصص
from contextlib import contextmanager
import time

# ١. باستخدام contextmanager (الأسهل)
@contextmanager
def timer_ctx(label="العملية"):
    start = time.perf_counter()
    print(f"⏳ بدأت: {label}")
    try:
        yield   # هنا يتنفّذ الكود داخل with
    finally:
        print(f"✅ {label}: {time.perf_counter()-start:.4f}s")

with timer_ctx("حساب المجموع"):
    total = sum(range(10_000_000))
    print(f"  النتيجة: {total:,}")

# ٢. باستخدام كلاس
class DatabaseConn:
    def __enter__(self):
        print("🔌 الاتصال بقاعدة البيانات...")
        return self   # ما يُعطى لـ 'as'

    def __exit__(self, exc_type, exc_val, tb):
        print("🔌 إغلاق الاتصال.")
        return False   # لا تكتم الأخطاء

    def query(self, sql):
        print(f"  🔍 تنفيذ: {sql}")

with DatabaseConn() as db:
    db.query("SELECT * FROM students")
# يُغلق تلقائياً حتى لو حدث خطأ

٨. Type Hints — توثيق الكود

Type hints لا تُغيّر سلوك بايثون لكنها تجعل الكود أوضح وتساعد محررات النص في اكتشاف الأخطاء مبكراً.

Python — Type Hints
from typing import List, Dict, Optional, Tuple, Union

# بدون type hints — مبهم
def process(data, factor):
    return [x * factor for x in data]

# مع type hints — واضح
def process(data: List[float], factor: float) -> List[float]:
    return [x * factor for x in data]

# أمثلة متنوعة
def greet(name: str, times: int = 1) -> str:
    return f"مرحباً {name}! " * times

# Optional — قد يكون None
def find_student(name: str) -> Optional[Dict]:
    db = {"خليل": {"grade": 18}}
    return db.get(name)   # قد يُرجع None

# Union — نوع أو آخر
def double(x: Union[int, float]) -> Union[int, float]:
    return x * 2

# بايثون 3.10+ — استخدم | بدل Union
def double_new(x: int | float) -> int | float:
    return x * 2

📝 تمارين تطبيقية

تمرين ١ — Decorator لحساب الوقت والتحقق

اكتب decorator يسمى @logged يطبع: اسم الدالة، المعاملات، النتيجة، والوقت المستغرق. ثم طبّقه على دالة تحسب المعدل.

Python — الحل
import time
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"\n📞 {func.__name__}({args}, {kwargs})")
        t      = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"   ↳ نتيجة: {result}")
        print(f"   ⏱️ وقت: {(time.perf_counter()-t)*1000:.2f}ms")
        return result
    return wrapper

@logged
def average(*grades):
    return sum(grades) / len(grades)

average(18, 16, 19, 15)
average(12, 14)

تمرين ٢ — Generator لقراءة CSV سطراً سطراً

اكتب generator يقرأ ملف CSV ويُعيد كل سطر كقاموس — بدون تحميل الملف كله في الذاكرة.

Python — الحل
def csv_reader(filepath):
    with open(filepath, encoding="utf-8") as f:
        headers = f.readline().strip().split(",")
        for line in f:
            values = line.strip().split(",")
            yield dict(zip(headers, values))

# استخدام — يعمل مع ملفات بأي حجم
for row in csv_reader("students.csv"):
    print(row)
    # {'name': 'خليل', 'grade': '18', 'city': 'مراكش'}

🧠 سؤال سريع

ما الفرق الرئيسي بين return و yield؟

لا فرق، كلاهما يرجع قيمة
yield أسرع من return
return ينهي الدالة، yield يوقفها مؤقتاً ويمكن استكمالها
yield يستخدم للأرقام فقط
✅ ممتاز! yield يحفظ حالة الدالة ويستكملها عند الطلب — هذا ما يجعل Generators فعّالة.
❌ خطأ. return ينهي الدالة نهائياً، بينما yield يوقفها مؤقتاً ويمكن استكمالها بـ next().

🚀 المشروع — نظام Pipeline لمعالجة البيانات

جمع كل المفاهيم: decorators + generators + type hints + comprehensions في نظام معالجة بيانات.

Python — data_pipeline.py
from typing import Generator, List, Dict, Callable
from functools import wraps, reduce
import time

# ===== Decorators =====
def timed(func):
    @wraps(func)
    def w(*a, **kw):
        t   = time.perf_counter()
        res = func(*a, **kw)
        print(f"  ⏱️ {func.__name__}: {(time.perf_counter()-t)*1000:.1f}ms")
        return res
    return w

# ===== Generator مصدر البيانات =====
def data_source(n: int) -> Generator[Dict, None, None]:
    import random
    cities = ["مراكش", "الرباط", "فاس", "الدار البيضاء"]
    for i in range(n):
        yield {
            "id"   : i + 1,
            "city" : random.choice(cities),
            "grade": random.randint(5, 20),
            "score": round(random.uniform(0, 100), 2)
        }

# ===== Pipeline بـ *args من Callables =====
def pipeline(data: List, *steps: Callable) -> List:
    return reduce(lambda d, fn: fn(d), steps, data)

# ===== خطوات التحويل =====
def only_passed(data):
    return [d for d in data if d["grade"] >= 10]

def add_percent(data):
    return [{**d, "percent": round(d["grade"]/20*100, 1)} for d in data]

def sort_desc(data):
    return sorted(data, key=lambda d: d["grade"], reverse=True)

# ===== التشغيل =====
print("🔄 تشغيل Pipeline...")
raw    = list(data_source(50))
result = pipeline(raw, only_passed, add_percent, sort_desc)

print(f"المدخلات: {len(raw)} | الناجحون: {len(result)}")
print("\n🏆 أعلى 5:")
for s in result[:5]:
    print(f"  #{s['id']:02d} [{s['city']:15}] {s['grade']}/20 ({s['percent']}%)")

🎉 أتقنت مفاهيم بايثون المتقدمة!

Lambda, *args/**kwargs, Decorators, Generators, Comprehensions, map/filter, Context Managers, Type Hints — كل هذا الآن في أدواتك.

← العودة للبداية