💻 آخرین فرصت یادگیری برنامه‌نویسی با آفر ویژه قبل از افزایش قیمت در ۱۵ آذر ماه (🎁 به همراه یک هدیه ارزشمند )
۰ ثانیه
۰ دقیقه
۰ ساعت
۵ محمدعلی رضا
تو کلاس متد __new__ در کلاس Singleton چه اتفاقی داره می‌افته؟
جامعه پایتون (وب) ایجاد شده در ۲۶ آبان ۱۳۹۹

سلااااااااااااااااااام

میخواستم بدونم تو کلاس متد __new__ در کلاس Singleton چه اتفاقی داره می‌افته؟ من این تیکه کد رو نمیفهمم.

class Singleton:
    @classmethod
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, 'instance'):
            cls.instance = super(*args, **kwargs)
        return cls.instance

 

سلااااااااااااااااااام :)

تو این متد ما حواسمون هست که بیشتر از یه ابجکت از کلاس مون ساخته نشه. در واقع دفعه اول که کلاس Singleton صدا زده بشه ما هیچ class attribute ای بنام instance نداریم پس یه ابجکت میسازیم از کلاس Singleton و ابجکتی که ساختیم رو داخل class attribute ای بنام instance ذخیره میکنیم و برمیگردونیشم. دفعه‌های بعد که کلاس Singleton صدا زده بشه دیگه ابجکت جدید ازش ساخته نمیشه و مقدار class attribute instance رو برمیگردونیم.

 

حالا خط به خط هم کد رو توضیح بدم.

خط ۴: ما داریم کلاس متد __new__ رو باز نویسی میکنیم. همونطور که میدونیم همه کلاس‌های پایتون از کلاس object ارث بری میکنن. و وقتی ما یه کلاس رو صدا میزنیم مثلا کلاس Singleton رو صدا میزنیم اول متد __new__ اجرا میشه که توش یه ابجکت از نوع Singleton ساخته میشه و برش میگردونه و بعد متد __init__ اجرا میشه که توش self مقدار دهی میشه و در واقع __init__ یه constructor هست.

خط ۵: ما چک میکنیم که cls که همون کلاس Singleton هست تو این خط، آیا method یا attribute ای بنام instance داره یا نه؟ اگر method یا attribute ای بنام instance نداشت. دستورات داخل شرط اجرا میشه.

خط ۶: یه attribute بنام instance اضافه میکنیم و توش نتیجه صدا زدن super با آرگومان هایی که بهش میدیم رو میریزیم. در واقع وقتی super رو تو این خط صدا میزنیم ۲ تا متد اجرا میشه. اول متد __new__ کلاس super که اینجا super همون object هست صدا زده میشه و یه ابجکت ساخته میشه و بعد متد __init__ کلاس Singleton اجرا میشه.

خط ۷: مقدار cls.instance رو برمیگردونیم که حتما توش یه ابجکت از Singleton هست.

 

امیدوارم تو این پاسخ به جواب تون رسیده باشید و دیگه کامل فهمیده باشید تو کد بالا چه اتفاقی داره می‌افته و فهیمده باشیدش. باز اگر سوالی داشتید تو همین تاپیک بپرسید.

محمدعلی رضا ۲۶ آبان ۱۳۹۹، ۰۴:۵۶

پوریا یه پیاده سازی دیگه هم از Singleton نوشته بود که بنظرم خوب بود (من یه خورده اصلاحش کردم)، البته کامل مطمئن نیستم که پیاده سازی درستی باشه. اما داره درست کار میکنه و به قول گفتنی It works

class Singleton2:
    instance = None
    @classmethod
    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance
    def __init__(self):
        print("init touched!")

البته من تو گوگل سرچ کردم python singleton design pattern و پیاده سازی‌های دیگه ای دیدم. اما بنظرم پیاده سازی استاد واقعا خیلی بهتره.

محمدعلی رضا ۲۶ آبان ۱۳۹۹، ۰۵:۰۳

پاسخ اولی که دادم خط ۶ رو یه مقدار اشتباه توضیح داده بودم. اصلاح شده اش اینطور میشه:

خط ۶: یه attribute بنام instance اضافه میکنیم و توش نتیجه صدا زدن super با آرگومان هایی که بهش میدیم رو میریزیم. در واقع وقتی super رو تو این خط صدا میزنیم ۲ تا متد اجرا میشه. اول متد __new__ کلاس super که اینجا super همون object هست صدا زده میشه و یه ابجکت ساخته میشه و بعد متد __init__ کلاس super که اینجا super همون object هست صدا زده. یعنی 

 

جواب سوالهایی که به عنوان دیدگاه تو پاسخ اولم نوشتی:

جواب سوال ۱) ببین ارث بری چطور بود؟ وقتی کلاس Singleton از کلاس object ارث بری میکنه، تمام method‌ها و attribute هاش رو به ارث میبره. حالا چون ما __new__ رو بازنویسی کردیم وقتی داخل متد __new__ ای که برای Singleton هست super رو صدا میزنیم. اول متد __new__ کلاس super اجرا میشه و بعد متد __init__ کلاس super، که منظور از super باز همون object هست.

پس ابجکت جدید طبق کدی که داخل __new__ کلاس object هست ساخته میشه ولی نوعش از کلاس Singleton هست. و اگر از تابع type استفاده کنی برا تشخص نوع ابجکت نهایی میبینی که میگه نوعش Singleton هست.

 

جواب سوال ۲) این قسمت رو قبلا اشتباه گفته بودم که اول همین پاسخ اصلاح کردم.

 

جواب سوال ۳) ۲ تا دلیل داره، یک بخاطر اینکه ما داریم __new__ رو بازنویسی میکنیم و __new__ داخل کلاس object یک classmethod هست و دلیل دوم که مهم تره اینکه که وقتی متد __new__ صدا زده میشه هنوز ابجکتی ساخته نشده و چیزی بنام self نداریم. و اگر بخواهیم به عنوان متد عادی تعریفش کنیم باید اولین آرکومان مون self باشه.

 

بنظرم خونده این صفحه میتونه بهتون کمک کنه:

https://jfine-python-classes.readthedocs.io/en/latest/call-a-class.html

 

این پاسخ هم توضیحاتش خیلی خوبه:

https://stackoverflow.com/a/61698095

محمدعلی رضا ۲۶ آبان ۱۳۹۹، ۰۹:۵۲

سلام مجدد
در اصل یعنی اگر بخواهیم بدون classmethod استفاده کنیم به این شکل میشود کدمون ؟

class Singleton:
    instance = None
    def __new__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = super().__new__(self)
        return self.instance
    def __init__(self):
        print("init touched!")

این کد هم خروجی یکسانی میده
فقط فرقش اینه من از classmethod استفاده نکردم .

راستیتش هنوز متوجه نشدم چرا classmethod استفاده کردیم

 

خروجی کد بالا :

<__main__.Singleton object at 0x000001B4148BDC08>
<__main__.Singleton object at 0x000001B4148BDC08>
<__main__.Singleton object at 0x000001B4148BDC08>

Ahmad As ۲۶ آبان ۱۳۹۹، ۱۱:۲۳

اول یه حرفی که بالا زدم رو اصلاح کنم. (واقعا ببخشید :) من تو پاسخ سومی که تو این تاپیک دادم یه جا گفتم:

جواب سوال ۳) ۲ تا دلیل داره، یک بخاطر اینکه ما داریم __new__ رو بازنویسی میکنیم و __new__ داخل کلاس object یک classmethod هست و دلیل دوم که مهم تره اینکه که وقتی متد __new__ صدا زده میشه هنوز ابجکتی ساخته نشده و چیزی بنام self نداریم. و اگر بخواهیم به عنوان متد عادی تعریفش کنیم باید اولین آرکومان مون self باشه.

که اصلاح شدش میشه این:

جواب سوال ۳) ما داریم از classmethod استفاده میکنیم بخاطر اینکه که وقتی متد __new__ صدا زده میشه هنوز ابجکتی ساخته نشده و چیزی بنام self نداریم. و اگر بخواهیم به عنوان متد عادی تعریفش کنیم باید اولین آرکومان مون self باشه. (البته میتونیم از staticmethod هم استفاده کنیم. ولی چون داخل متد میخواهیم از cls استفاده کنیم بهتره classmethod بنویسیم.)

 

جواب سوال "در اصل یعنی اگر بخواهیم بدون classmethod استفاده کنیم به این شکل میشود کدمون ؟"

اینکه نوشتی کار میکنه اما مساله اینه که کد پوریا تو پاسخ دوم رو برداشتی و توش فقط بجای کلمه cls از self استفاده کردی تو این تیکه کدت منظور از self همون cls هست. اگر از PyCharm استفاده کنی موس رو ببری روی آرگومان اول متد یعنی self، میبینی بهت این پیام رو میده:

Usually first parameter of such methods is named 'cls' 

 

با خوندن اون حرفی که اول این پاسخ نوشتم و اصلاح کردم شاید بتونی بفهمی چرا اینجا از classmethod استفاده کردیم.

محمدعلی رضا ۲۶ آبان ۱۳۹۹، ۱۳:۳۸