هر چقدر یاد بگیری کمه!
چند روز پیش داشتم روی یه تیکه کد کار میکردم و به نظر میرسید که یک شرط در شرایطی درست کار نمیکنه.
از اونجایی که دیوانه اینطور باگ ها هستم رفتم دو فصل از کتاب های fluent python و the python apprentice رو خوندم تا کامل برام واضح بشه.
گفتم این موضوع رو با شما عزیزان هم به همون روشی که خودم یاد گرفتم به اشتراک بذارم
موضوع از اینجا شروع شد که شرط زیر توی پروژه رو دیدم:
if (django_model_instance.status is False):
...
اولین سوالی که به ذهنم رسید این بود که چرا condition به صورت زیر نوشته نشده:
(not django_model_instance.status)
رفتم مدل هایی که تعریف شده بود رو نگاه کردم و به صورت Boolean تعریف شده بودن، موضوعی که وجود داشت مقدار دیفالت None در نظر گرفته شده بود و null True بود اون فیلد خاص!
تا اینجا متوجه شدم که چرا از روش دوم استفاده نکردن قبلا(چون 3 حالت بود و فقط نیاز بود توی حالتی که False هستش اون تکه کد اجرا بشه)، ولی باز هم به نظر میرسید جاهایی که باید وارد شرط بشه، بعضی اوقات میشه و بعضی اوقات نمیشه!
اینطوری بود که سرچ کردن من شروع شد.
تا جایی که میدونستم توی پایتون وقتی مقادیری رو میخوایم به یک variable نسبت بدیم، مقدار به صورت instance ای از کلاس مد نظر ساخته میشه و reference اون instance توی متغیر ذخیره میشه.
پس با استفاده از Terminal پایتون رو اجرا کردم تا با استفاده از REPL این موضوع رو بررسی کنم.
False is False
# True
a = False
b = False
a is b
# True
اینجا بود که فهمیدم قضیه جذابی داره اتفاق می افته.
گفتم بذار ببینم برای int چه اتفاقی می افته؟
2 is 2
# True
a = 2
b = 2
a is b
# True
اینجا بود که دیگه مغزم نکشید، سرچ من از همین نقطه شروع شد.
با توجه به سرچ هایی که کردم، متوجه شدم مقادیر bool در پایتون به صورت singleton در نظر گرفته میشن و پایتون این رو تضمین میکنه که هرجایی که این داده وجود داره رفرنس به یک instance خاص هستش. پس id یا همون identifier اون رفرنس همیشه ثابته.
گفتم خب اوکی، برای int چطور؟ آیا برای int هم به صورت singleton عمل میکنه؟
توی کد های بالا 2 رو تست کرده بودم و به نظر میرسید که رفرنس ها یکی هستند، با تابع id رفرنس ها رو در آوردم و دیدم بله!
باز سوال شد که خب اوکی، چرا؟!
با خوندن کتاب ها و سرچ کردن به این جواب رسیدم که مقادیر -5 تا 256 در پایتون به صورت small int در نظر گرفته میشن و هرجا که قرار باشه از اینها استفاده بشه، به یک instance خاص اشاره میشه (گویا به علت پر استفاده بودن، برای بهبود پرفورمنس اینکار رو میکنه)
پس این کد رو تست کردم:
257 is 257
# True
a = 257
b = 257
a is b
# False
و اینجا بود که دوزاریم تا حدودی افتاد، توی این سرچ کردن ها با اصطلاحات جالبی مثل falsy آشنا شدم و همینطور بیشتر از قبل در مورد تفاوت های is و == اطلاعات به دست آوردم.
flasy: به مقادیری که False نیستن ولی بعد از cast شدن به bool مقدار False پیدا میکنن میگن، 0, "", [], ...
توی شرط ها اگر مقدار غیر bool رو به صورت مستقیم استفاده کنیم، اول به bool cast میشه بعد بررسی مقدار اتفاق می افته
if 1:
print(11)
# 11
تفاوت is و ==:
is در واقع داره identifier رفرنس ها رو با هم مقایسه می کنه
ولی == داره مقادیر رو با هم مقایسه میکنه
یه جورایی میشه گفت:
a = 265
b = 265
a is b
# id(a) == id(b)
# False
a == b
# 1 == 1
# True
در نهایت باگ روی مقادیر None در اون attribute خاص بود گویا و ربطی به False is False نداشت!😂
ولی کاوش جالبی بود.
به خودم میگم چرا بعد از این همه سال کار کردن، موارد ساده ای مثل این رو هنوز بلد نیستم؟
به نظرم مهم ترین دلیلش اینه که رفرنس نمیخوندم، سرچ می کردم فقط و اون کاری که میخواستم رو انجام میدادم.
مورد بعدی اینه که عمیق یاد نمی گرفتم و به پشت صحنه اتفاقاتی که می افتاد دقت نمی کردم.
الان نزدیک به 3 سال شده که دارم رفرنس میخونم، ولی هنوز هم خیلی چیزای ابتدایی متعجبم می کنن!
مطلبی دیگر از این انتشارات
مسیر موفقیت در برنامهنویسی اندروید
مطلبی دیگر از این انتشارات
مقایسه Dispatchers.Default و Dispatchers.IO در کاتلین کوروتین (kotlin Coroutine)
مطلبی دیگر از این انتشارات
چرت و پرت نامه | شش فصل تا پیروزی