أشهر أخطاء أكواد بايثون مع الأمثلة العملية
في عالم البرمجة، يعتبر التعامل مع الأخطاء جزءًا طبيعيًا من عملية تطوير البرامج. ولغة **بايثون**، رغم سهولتها ومرونتها، ليست استثناءً. أثناء كتابة الأكواد، قد تواجه مجموعة متنوعة من الأخطاء التي يمكن أن تعرقل سير العمل وتؤدي إلى نتائج غير متوقعة. سواء كنت مبتدئًا في البرمجة أو متمرسًا، فإن فهم الأخطاء الشائعة في بايثون وكيفية التعامل معها يساعدك على تحسين كفاءتك البرمجية وحل المشكلات بسرعة.
في هذا المقال، سنتناول مجموعة من الأخطاء الشائعة التي قد تصادفك عند استخدام لغة بايثون، مع شرح كل خطأ وكيفية تصحيحه، مما يمكّنك من بناء أكواد أكثر استقرارًا وسلاسة.
أشهر أخطاء بايثون
- استخدام التعابير كقيم افتراضية لوسطاء الدوال عند تحديد وسيط كقيمة افتراضية (مثل قائمة)، تُستخدم القيمة نفسها لكل استدعاء. الحل: استخدام `None` والتحقق منه لتجنب تكرار نفس القيمة.
- استخدام متغيرات الصنف بشكل خاطئ: بتغيير متغيرات الصنف يؤثر على جميع الأصناف الموروثة الحل فهم "ترتيب تحليل التوابع" (MRO) يساعد في التعامل الصحيح
- تحديد المعاملات في كتلة الاستثناءات بشكل خاطئ: يجب وضع الاستثناءات في مجموعة (Tuple) واستخدام `as` لربطها.
- سوء فهم قواعد نطاق بايثون (Scope): قواعد بايثون تعتمد على LEGB. قد يؤدي إسناد متغير إلى تظليل متغيرات من نطاقات أعلى.
- تعديل القائمة أثناء المرور عليها كحذف عناصر أثناء المرور على القائمة قد يسبب أخطاء الحل: استخدام استيعاب القوائم (List Comprehensions) لتجنب الحذف أثناء التمرير.
- ربط المتغيرات في المنغلقات (Closures): الربط المتأخر للمتغيرات يسبب نتائج غير متوقعة والحل: تمرير المتغيرات كوسائط افتراضية
- الاعتماديات الدائرية بين الوحدات فاستيراد الوحدات التي تستدعي بعضها بعضًا يمكن أن يسبب أخطاء والحل: تأجيل استيراد الوحدة إلى داخل الدالة عند الحاجة.
الخطأ 1: استخدام التعابير كقيم افتراضية لوسطاء الدوال
مثال:
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2]
شرح المشكلة:
القيمة الافتراضية `my_list` تُحفظ عبر الاستدعاءات، وبالتالي تظل القائمة تحتوي على العناصر المضافة في كل استدعاء.
الحل:
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [2]
الخطأ 2: استخدام متغيرات الصنف بشكل خاطئ
مثال:
class A:
x = 1
class B(A):
pass
class C(A):
pass
print(A.x, B.x, C.x) # 1 1 1
B.x = 2
print(A.x, B.x, C.x) # 1 2 1
A.x = 3
print(A.x, B.x, C.x) # 3 2 3
شرح المشكلة:
تغيير متغير الصنف في الفئة الأم يؤثر على الفئة الابنة ما لم يتم تعديل المتغير في الفئة الابنة بشكل صريح.
الحل:
فهم كيفية ارتباط متغيرات الأصناف واستخدام متغيرات خاصة في الأصناف إذا كان الهدف هو استقلالية المتغير.
الخطأ 3: تحديد المعاملات لكتلة الاستثناء بشكل خاطئ
مثال
try:
lst = ["a", "b"]
int(lst[2])
except ValueError, IndexError:
print("Error occurred")
المشكلة:
هذه الصيغة غير صحيحة في Python 3 وتسبب ربط `IndexError` كمتغير وليس كاستثناء.
الحل:
try:
lst = ["a", "b"]
int(lst[2])
except (ValueError, IndexError) as e:
print(f"Error occurred: {e}")
الخطأ 4: سوء فهم نطاق المتغيرات
مثال:
x = 10
def increment():
x += 1
print(x)
increment() # خطأ: UnboundLocalError
المشكلة:
بايثون تعتبر أن `x` متغير محلي لأننا نقوم بإسناد قيمة له داخل الدالة.
الحل:
x = 10
def increment():
global x
x += 1
print(x)
increment() # 11
الخطأ 5: تعديل القائمة أثناء المرور عليها
مثال:
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
print(numbers) # [1, 3, 5]
المشكلة:
تعديل القائمة أثناء المرور عليها يؤدي إلى تخطي بعض العناصر.
الحل:
numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers) # [1, 3, 5]
الخطأ 6: عدم فهم الربط في المنغلقات (Closures)
مثال:
def create_multipliers():
return [lambda x: i * x for i in range(5)]
multipliers = create_multipliers()
for multiplier in multipliers:
print(multiplier(2)) # 8 8 8 8 8
المشكلة
يتم حفظ المتغير `i` عند آخر قيمة له بسبب الربط المتأخر.
الحل:
def create_multipliers():
return [lambda x, i=i: i * x for i in range(5)]
multipliers = create_multipliers()
for multiplier in multipliers:
print(multiplier(2)) # 0 2 4 6 8
الخطأ 7: الاعتماديات الدائرية
مثال
# a.py
import b
def f():
return b.x
print(f())
# b.py
import a
x = 1
def g():
print(a.f())
المشكلة:
الاستيراد الدائري قد يعمل في بعض الحالات لكنه يسبب أخطاء في حالات أخرى إذا لم يتم تعريف المتغيرات بشكل صحيح.
الحل:
نقل الاستيراد داخل الدوال لتجنب التباس الاستيراد الدائري:
# b.py
x = 1
def g():
import a
print(a.f())
أرجو أن تكون فهمت من خلال هذه الأمثلة كل خطأ مع الحل المقترح لتحسين الشيفرة البرمجية التي تكتبها وتجنب المشاكل وكما رأيت فإن فهم آليات عمل اللغة مثل الوراثة في بايثون والتعامل مع الاستثناءات، والتعامل مع النطاقات، يمكن أن يحل العديد من هذه الأخطاء.