١. Lambda — دوال مجهولة الهوية
lambda هي دالة مختصرة في سطر واحد — بدون اسم، بدون def، مثالية للعمليات البسيطة.
🐢 الطريقة العادية
def double(x):
return x * 2
print(double(5)) # 10
⚡ Lambda
double = lambda x: x * 2
print(double(5)) # 10
# 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))
2 ** 3 = 2³ = 8** هي عملية الأس (power) — 2³ = 8٢. *args و **kwargs — دوال مرنة
*args تقبل عدداً غير محدود من المعاملات. **kwargs تقبل أزواج مفتاح-قيمة.
# *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
# **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 هو دالة تلف دالة أخرى وتضيف لها سلوكاً دون تعديل كودها. يُعبّر عنه بـ @.
# 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 المفيدة في العمل
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 فوق دالة يعني:
@timer يعادل تماماً: my_func = timer(my_func)@timer يعادل func = timer(func).٤. Generators — توليد البيانات بكفاءة
الـ generator يولّد العناصر واحدة بواحدة عند الطلب دون تحميل كل شيء في الذاكرة — مفيد مع ملايين السجلات.
# دالة عادية 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 — كود مختصر وسريع
# ١. 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
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 يضمن تنفيذ كود التنظيف دائماً — حتى لو حدث خطأ. رأيناه مع الملفات، لكن يمكن إنشاء مخصص.
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 لا تُغيّر سلوك بايثون لكنها تجعل الكود أوضح وتساعد محررات النص في اكتشاف الأخطاء مبكراً.
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 يطبع: اسم الدالة، المعاملات، النتيجة، والوقت المستغرق. ثم طبّقه على دالة تحسب المعدل.
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 ويُعيد كل سطر كقاموس — بدون تحميل الملف كله في الذاكرة.
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 يحفظ حالة الدالة ويستكملها عند الطلب — هذا ما يجعل Generators فعّالة.return ينهي الدالة نهائياً، بينما yield يوقفها مؤقتاً ويمكن استكمالها بـ next().🚀 المشروع — نظام Pipeline لمعالجة البيانات
جمع كل المفاهيم: decorators + generators + type hints + comprehensions في نظام معالجة بيانات.
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 — كل هذا الآن في أدواتك.
← العودة للبداية