هذا المقال يتطلب مستوى: مبتدئ. لو انت لسه بادئ في بايثون، الكلام ده مكتوب لك بالظبط.
الفرق بين is و== في بايثون
لو استخدمت is عشان تقارن رقمين وطلعت النتيجة صح، ده مش دليل إن كودك سليم. المقال ده هيوريك ليه a is b ممكن يطلع False رغم إن a وb فيهم نفس الرقم، وإمتى تستخدم is وإمتى تستخدم ==. المكسب: تبطّل باج بيظهر فجأة على أرقام كبيرة.
المشكلة باختصار
في بايثون فيه طريقتين للمقارنة بيتلخبطوا مع بعض. == بتسأل: القيمتين متساويتين؟ وis بتسأل سؤال تاني خالص: هو ده نفس الكائن في الذاكرة؟ الفرق ده بيعدّي عليك من غير ما تحس، لحد ما كودك يشتغل صح على 100 ويفشل على 1000.
الفكرة بمثال بسيط
تخيّل توأمين متطابقين. لو سألت: "همّا متشابهين؟" الجواب أيوه. لكن لو سألت: "همّا نفس الشخص؟" الجواب لأ، دول اتنين. == بتقيس التشابه (القيمة)، وis بتقيس إنهم نفس الشخص (نفس المكان في الذاكرة).
مثال تاني أقرب للكود: عندك صورتين مطبوعتين من نفس الملف. الصورتين متطابقتين (== يقول True)، بس كل ورقة ليها مكان مختلف على المكتب (is يقول False). ركز على النقطة دي، هي مفتاح الموضوع كله.
التفسير العلمي بالتفصيل
أي كائن في بايثون ليه عنوان في الذاكرة بتشوفه بدالة id(). عامل == بينادي التابع __eq__ اللي بيقارن المحتوى. عامل is بيقارن ناتج id() للكائنين، يعني بيقارن العنوانين مباشرة. لما تكتب a = 1000 وb = 1000، بايثون بيعمل كائنين منفصلين في عنوانين مختلفين، فـis بترجع False رغم تساوي القيمة.
a = 1000
b = 1000
print(a == b) # True -> القيمتان متساويتان
print(a is b) # False -> كائنان مختلفان في الذاكرة
print(id(a), id(b)) # عنوانان مختلفان
x = 100
y = 100
print(x is y) # True غالبًا -> بسبب مخزن الأعداد الصغيرة
ليه النتيجة بتنقلب عند 256؟
هنا الجزء اللي بيلغبط المبتدئين. في تطبيق CPython (اللي بتستخدمه غالبًا)، بايثون بيجهّز مسبقًا كائنات ثابتة للأعداد الصحيحة من -5 لحد 256. أي متغيّرين قيمتهم في المدى ده بيشاوروا على نفس الكائن، فـis بترجع True. أول ما تعدّي 256، بايثون بيعمل كائن جديد كل مرة، فـis بترجع False. عشان كده 100 is 100 بيطلع True و1000 is 1000 بيطلع False.
الافتراض هنا: إن سلوك تخزين الأعداد الصغيرة ده تفصيلة خاصة بـCPython، ومش مضمون في تطبيقات تانية زي PyPy، ولا مضمون يفضل ثابت بين إصدارات. يعني متعتمدش عليه أبدًا في منطق كودك.
سيناريو واقعي بيوقعك
تخيّل دالة بتفحص كود حالة راجع من API:
def check(code):
if code is 200: # غلط: بتقارن الهوية مش القيمة
return "OK"
return "Error"
print(check(200)) # ممكن يطلع OK بالصدفة
print(check(1000)) # هيطلع Error حتى لو المفروض 1000 حالة صحيحة عندك
الكود ده هيشتغل صح على 200 بالصدفة (لإنه جوه المخزن)، ويفشل على أي رقم أكبر من 256. ولاحظ إن بايثون الحديث بيطلعلك SyntaxWarning لما تكتب is مع رقم مباشر، وده تنبيه واضح إنك بتعمل حاجة غلط. الصح إنك تستخدم ==:
def check(code):
if code == 200: # صح: بتقارن القيمة
return "OK"
return "Error"
القاعدة والـ trade-off
القاعدة بسيطة: استخدم == لما تقارن القيم (أرقام، نصوص، قوائم)، واحجز is للمقارنة مع الكائنات المفردة الثابتة زي None وTrue وFalse. يعني اكتب if x is None: مش if x == None:.
الـ trade-off: عامل is أسرع لإنه بيقارن عنوانين بس (عملية واحدة تقريبًا)، من غير ما ينادي أي تابع. بينما == ممكن ينادي __eq__ اللي ممكن يكون فيه منطق أبطأ. بتكسب سرعة بسيطة مع is، بس بتخسر الصحة لو استخدمته في مقارنة قيم. والسرعة دي مش هتفرق معاك كمبتدئ، فمتضحّيش بالصحة عشانها.
متى لا تستخدم is
ماتستخدمش is نهائيًا لمقارنة الأرقام أو النصوص أو أي قيمة عادية. كمان خلي بالك: مقارنة النصوص القصيرة بـis ممكن تطلع True بسبب تحسين داخلي اسمه string interning، بس ده كمان تفصيلة تطبيق متعتمدش عليها. لو محتاج تقارن قيمة، دايمًا == هي الإجابة.
الخطوة التالية
افتح أي كود بايثون عندك ودوّر بالبحث على is. أي حتة فيها is مش متبوعة بـNone أو True أو False، غيّرها لـ== واختبر. لو النتيجة اتغيّرت، يبقى كان عندك باج مستخبي.
مصادر
- Python Official Docs — Programming FAQ: "Is there an equivalent... difference between == and is":
docs.python.org/3/faq/programming.html - Python Docs —
id()built-in function:docs.python.org/3/library/functions.html#id - Python Language Reference — Value comparisons (
==) and identity comparisons (is):docs.python.org/3/reference/expressions.html#comparisons - PEP 8 — Programming Recommendations (استخدم
is/is notعند المقارنة معNone):peps.python.org/pep-0008/