💻 آخرین فرصت یادگیری برنامه‌نویسی با آفر ویژه قبل از افزایش قیمت در ۵ آذر ماه (🎁 به همراه یک هدیه ارزشمند )
۰ ثانیه
۰ دقیقه
۰ ساعت
۱ Hossein Ranjbari
نحوه پیاده سازی سینگلتون برای کلاسی که دارای متد ــinit__ باشد
جامعه پایتون (وب) ایجاد شده در ۲۲ دی ۱۳۹۹

سلام؛

 من یه سوالی برام پیش اومده؛

چطوری میشه با الگوی سینگلتون کلاسی پیاده سازی کرد که متد __init__ داشته باشه و هدف این باشه که فقط یک نمونه بشه ازش ایجاد کرد چون این کدی که زدم کار نمیکنه در واقع اصلا نمیتونم نمونه‌ای ازش ایجاد کنم و به ویژگی‌های اون نمونه دسترسی داشته باشم.

class Supervisor:
    instance = None
    def __init__(self, username, password, phone_number):
        self.username = username
        self.password = password
        self.phone_number = phone_number
    @classmethod
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, 'instance'):
            cls.instance = super(*args, **kwargs)
        return cls.instance

سلام

 

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

 

هرکلاس توی پایتون یک type است.

هر کلاس در پایتون به‌طور پیش‌فرض از object پایتون ارث‌بری می‌کند.

اگر بخواهیم از داخل متد فرزند، متد کلاس والد را صدا بزنیم به این صورت عمل می‌کنیم:
 

super(ClassType, obj).<method-name>(*args, **kwargs)

 

که در اینجا ClassType میشه همون کلاس فرزند، obj میشه همون self که تازه ساختیم، <method-name> متدی هست که توی کلاس والد باید اجرا بشه و داخل پرانتز هم تمام آرگومان‌های مورد نیاز برای اجرا متد رو می‌فرستیم.

این نکته رو هم می‌دونیم که متد‌ها می‌تونن زنجیر وار از کلاس‌های فرزند و طبق MRO هم دیگه رو صدا بزنن. پس در نهایت اگر همه متد‌ها super والد خودشون رو صدا بزنن در انتها می‌رسیم به object پایتون (چون همونطور که گفتم تمام کلاس‌ها در نهایت از object ارث‌بری می‌کنن)

حالا پاسخ این مورد خاص:
وقتی ما تصمیم داریم متد new والد رو از داخل کلاس فرزند صدا بزنیم یک استثنا داریم و باید به این صورت بنویسیم:
 

super(ClassType, obj).__new__(cls)
# یا به اختصار
super().__new__(cls)

خب با اینطور نوشتن سوالات زیادی توی ذهن ما شکل می‌گیره چون گویا متد call اینجا با در نظر گرفتن محلی که ما new رو صدا می‌زنیم رفتار دوگانه‌ای داره. یعنی در واقع وقتی ما بطور عادی می‌خوایم یه instance از کلاس مدنظر خودمون بسازیم متد new داخل پرانتز آرگومان‌های args و kwargs رو هم میگیره و instance رو می‌سازه اما اگر از داخل اون کلاس متد new کلاس والد رو صدا بزنیم و توی پرانتز بهش آرگومان args و kwargs بدیم خطا میده و میگه متد new توی object هیچ آرگومانی نمی‌گیره :)
رفتار متد call اینجا واقعا عجیبه و منم درست درک نمی‌کنم که اینجا چرا باید استثنا داشته باشیم و خطا بگیریم. چیزی که مشخصه اینه که در این حالت هر دو متد call توی کلاس‌های والد و فرزند اجرا میشن (در صورتیکه به صورت تئوری ما می‌دونیم فقط یکی از اونها باید اجرا بشه)
بهرحال فکر می‌کنم من داکیومنت زیادی در مورد چرایی این مورد پیدا نکردم و احتمال میدم که در آپدیت‌های آینده این مورد اصلاح بشه.


نکته پایانی و مهم اینکه سطح مشکلی که پیش اومده و سوالی که وجود داره خیلی بالاست و اگر بخشی از توضیحات بالا براتون مفهوم نیست نگران نباشید چون واقعا دلیلی نداره همین ابتدای مسیر انقدر توی چنین موارد نادری عمیق بشیم. نحوه درست صدا زدن متد در کلاس والد رو بلد باشید و بدونید که برای صدا زدن متد new در کلاس فرزند استثنا داریم، همین :)
 

حسین رمضان پور ۲۸ دی ۱۳۹۹، ۱۷:۳۴