Python: المعاملات المتقدمة والنطاق
الآن بعد أن تعلمت أساسيات الدوال، دعنا نستكشف بعض التقنيات المتقدمة. كم طريقة يمكنك كتابة المعاملات بها؟ ماذا يعني "نطاق المتغير"؟ ما هي العودية؟ هذه المفاهيم سترفع فهمك للدوال إلى المستوى التالي.
1. المعاملات الافتراضية
المعاملات الافتراضية تتيح لك حذف الوسائط عند استدعاء دالة:
▶ مثال: المعاملات الافتراضية الأساسية
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", "Hey") # Hey, Charlie!
الناتج:
# Function defined successfully
(1) ⚠️ فخ المعامل الافتراضي
قيم المعاملات الافتراضية تُقيّم مرة واحدة فقط، عند تعريف الدالة، وليس في كل مرة تُستدعى فيها الدالة. إذا كان الافتراضي كائناً قابلاً للتعديل (مثل قائمة أو قاموس)، يمكن أن يؤدي هذا إلى سلوك غير متوقع:
▶ مثال: فخ المعامل الافتراضي القابل للتعديل
def add_item(item, items=[]): # ❌ خطير!
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] — وليس ['banana']!
print(add_item("orange")) # ['apple', 'banana', 'orange']
لماذا؟ القائمة الفارغة items=[] تُنشأ مرة واحدة عند تعريف الدالة. كل استدعاء لاحق يُضيف إلى نفس القائمة.
الطريقة الصحيحة: استخدم None كافتراضي وأنشئ قائمة جديدة داخل الدالة:
▶ مثال: معامل افتراضي آمن
def add_item(item, items=None): # ✅ آمن
if items is None:
items = []
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['banana']
print(add_item("orange")) # ['orange']
الناتج:
# Function defined successfully
None، أرقام، سلاسل نصية، صفوف). لا تستخدم أبداً كائنات قابلة للتعديل مثل القوائم أو القواميس كمعاملات افتراضية.
2. الوسائط المفتاحية
عند استدعاء دالة، يمكنك تحديد القيم باسم المعامل، مما يسمح بتمرير الوسائط خارج الترتيب:
▶ مثال: الوسائط المفتاحية
def create_user(name, age, city):
print(f"User: {name}, {age} years old, from {city}")
# الوسائط الموضعية — يجب أن تكون بالترتيب
create_user("Alice", 25, "Beijing")
# الوسائط المفتاحية — حدد أسماء المعاملات، الترتيب لا يهم
create_user(age=30, city="Shanghai", name="Bob")
create_user(name="Charlie", city="Guangzhou", age=22)
# مختلطة — موضعية أولاً، مفتاحية ثانياً
create_user("Diana", city="Shenzhen", age=28)
# create_user(name="Eve", 35, "Chengdu") ❌ الوسائط المفتاحية لا يمكن أن تسبق الموضعية
الناتج:
# Function defined successfully
3. *args: وسائط موضعية متغيرة
إذا كنت لا تعرف عدد الوسائط التي ستستقبلها الدالة، استخدم *args:
▶ مثال: *args لدالة الجمع
def sum_all(*numbers):
"""تقبل أي عدد من الأرقام وتُرجع مجموعها"""
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2)) # 3
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0 — بدون وسائط أيضاً تعمل
الناتج:
# Function defined successfully
*args يحزم جميع الوسائط الموضعية في صف:
▶ مثال: فحص args
def show_args(*args):
print(f"Received {len(args)} arguments: {args}")
for i, arg in enumerate(args, 1):
print(f" #{i}: {arg}")
show_args("apple", "banana", "orange")
الناتج:
# Function defined successfully
4. **kwargs: وسائط مفتاحية متغيرة
**kwargs تقبل أي عدد من الوسائط المفتاحية وتحزمها في قاموس:
▶ مثال: **kwargs لبناء ملفات تعريف المستخدمين
def create_profile(**info):
"""أنشئ ملف تعريف مستخدم، يقبل أي قدر من المعلومات"""
print("User Profile:")
for key, value in info.items():
print(f" {key}: {value}")
create_profile(name="Alice", age=25, city="Beijing", job="Engineer")
المخرجات:
User Profile:
name: Alice
age: 25
city: Beijing
job: Engineer
▶ مثال: دمج *args و **kwargs
def advanced_function(a, b, *args, **kwargs):
print(f"Positional: a={a}, b={b}")
print(f"Extra positional: {args}")
print(f"Keyword: {kwargs}")
advanced_function(1, 2, 3, 4, 5, name="Alice", age=25)
# Positional: a=1, b=2
# Extra positional: (3, 4, 5)
# Keyword: {'name': 'Alice', 'age': 25}
الناتج:
# Function defined successfully
المعاملات العادية ← *args ← المعاملات الافتراضية ← **kwargs
5. النطاق: قاعدة LEGB
أين يمكن الوصول إلى متغير؟ هذا يحدده النطاق. لدى بايثون أربعة مستويات من النطاق:
▶ مثال: تداخل نطاق LEGB
# نطاق عالمي
x = 10 # متغير عالمي
def outer():
# نطاق function المحيطة
x = 20 # هذا هو x المحلي لـ outer، مختلف عن x العالمي
def inner():
# نطاق محلي
x = 30 # هذا هو x المحلي لـ inner
print(f"inner: {x}") # 30
inner()
print(f"outer: {x}") # 20
outer()
print(f"global: {x}") # 10
الناتج:
# Function defined successfully
LEGB تعني:
- Local — داخل الدالة الحالية
- Enclosing — متغيرات الدالة المحيطة
- Global — على مستوى الوحدة
- Built-in — دوال بايثون المدمجة (مثل
print,len)
تبحث بايثون عن المتغيرات بترتيب L → E → G → B.
(1) global و nonlocal
لتعديل متغير عالمي داخل دالة، استخدم global:
▶ مثال: استخدام global
count = 0
def increment():
global count # أعلن عن نية تعديل variable العالمي
count += 1
increment()
increment()
print(count) # 2
الناتج:
# Function defined successfully
global. المتغيرات العالمية تجعل الكود أصعب في الفهم والتصحيح. يجب أن تستقبل الدوال المدخلات من خلال المعاملات وتُخرج النتائج من خلال return، وليس تعديل المتغيرات العالمية سراً.
6. أساسيات العودية
العودية هي عندما تستدعي دالة نفسها. الفكرة الأساسية: قسم مشكلة كبيرة إلى مشكلات فرعية أصغر ومتشابهة.
▶ مثال: العودية لحساب المضروب
def factorial(n):
"""احسب n! (مضروب n)"""
if n <= 1:
return 1 # شرط الإنهاء
return n * factorial(n - 1) # استدعاء عودي
print(factorial(5)) # 120 (5×4×3×2×1)
التنفيذ:
factorial(5) = 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
▶ مثال: متتالية فيبوناتشي (الصعوبة ⭐⭐⭐)
def fibonacci(n):
"""أرجع رقم فيبوناتشي رقم n (بدءاً من 0)"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# اطبع أول 10
for i in range(10):
print(fibonacci(i), end=" ")
# يُخرج: 0 1 1 2 3 5 8 13 21 34
الناتج:
# Function defined successfully
7. حالات الاستخدام الشائعة
- المعاملات الافتراضية: خيارات التهيئة، قيم المهلة، دوال الاستدعاء العكسي.
*args: دوال التجميع الرياضي (الجمع، المتوسط)، تنسيق السجلات.**kwargs: أغلفة الدوال، دمج التهيئة، معاملات استعلام قاعدة البيانات.- العودية: اجتياز شجرة الدليل، تحليل JSON المتداخل، الصيغ الرياضية (المضروب/فيبوناتشي).
- النطاق: فهم الإغلاقات (الدرس التالي)، فهم عمر المتغير.
❓ أسئلة شائعة
*args و kwargs مجرد اصطلاحات تسمية — النجميات هي المهمة. يمكنك كتابة *numbers، options. لكن اتباع الاصطلاح موصى به حتى يفهمه مبرمجو بايثون الآخرون فوراً.x = 10 داخل دالة يُعامل كـ "أنشئ متغيراً محلياً x." إذا كنت حقاً بحاجة لتعديل عالمي، استخدم global. لكن ذلك عادة يعني أن تصميمك يمكن تحسينه.📖 ملخص
- المعاملات الافتراضية تضيف مرونة؛ لا تستخدم أبداً كائنات قابلة للتعديل كمعاملات افتراضية
- الوسائط المفتاحية تتيح التمرير بالاسم، وليس بالموضع
*argsيحزم الوسائط الموضعية الإضافية في صف؛**kwargsيحزم الوسائط المفتاحية في قاموس- ترتيب المعاملات: عادي →
*args→ افتراضية →**kwargs - قاعدة LEGB تحدد ترتيب البحث عن المتغيرات: محلي → محيط → عالمي → مدمج
globalيعدل المتغيرات العالمية من داخل دالة؛ قلّل من استخدامه- العودية هي دالة تستدعي نفسها؛ تحتاج شرط إنهاء وشرط عودي
📝 تمارين
-
أساسي (الصعوبة ⭐): اكتب دالة
multiply(*nums)تقبل أي عدد من الأرقام وتُرجع حاصل ضربها. إذا لم تكن هناك وسائط، أرجع 0. -
متوسط (الصعوبة ⭐⭐): اكتب دالة
create_student(name, **scores)تستقبل اسم طالب وأي عدد من درجات المواد (وسائط مفتاحية)، وتحسب وتُرجع المتوسط. مثال استدعاء:create_student("Alice", Chinese=85, Math=92, English=78). -
تحدي (الصعوبة ⭐⭐⭐): اكتب دالة عودية
sum_digits(n)تحسب مجموع أرقام عدد صحيح موجب. مثال:sum_digits(123)تُرجع1+2+3=6. تلميح: باقي القسمة على 10 يعطي الرقم الأخير؛ القسمة الأرضية على 10 تزيله. شرط الإنهاء: n < 10.