Python bir singlet yaratmaq

Bu sual, tək nümunə şablonunun dizaynının arzuolunan olub olmadığını müzakirə etmək üçün deyil, bir anti-şablon və ya hər hansı bir dini şəxs üçün deyil, bu şablonun Python-da ən pythonik bir şəkildə necə tətbiq edildiyini müzakirə etməkdir. Bu vəziyyətdə "ən pythonic" ifadəsini "ən az sürpriz prinsipi" izah edirəm.

Bir nöqtə olacaq bir neçə sinif var (mənim istifadə vəziyyət qeydiyyatdan üçün deyil, lakin bu vacib deyil). Mən sadəcə miras alanda və ya bəzəyəcəyəm bir neçə sınıftan daha sıx güllələnmək istəmirəm.

Ən yaxşı təcrübələr:


Metod 1: dekorativ

 def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass 

Arqumentlər

  • Dekoratörler birdən çox mirasdan daha çox sezgir olan əlavə yollardır.

qarşı

  • MyClass () istifadə edərək yaradılmış obyektlər true singleton obyektləri olmasına baxmayaraq, MyClass özü bir funksiya deyil, bir sinifdir, belə ki, sinif üsullarından zəng edə bilməzsiniz. Həmçinin m = MyClass(); n = MyClass(); o = type(n)(); m = MyClass(); n = MyClass(); o = type(n)(); sonra m == n m != o n != o

Metod 2: Əsas sinif

 class Singleton(object): _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton, BaseClass): pass 

Arqumentlər

  • Bu əsl bir sinifdir.

qarşı

  • Bir çox miras - eugh! __new__ ikinci sinif sinifindən miras dövründə __new__ bilər? Lazım olduğundan daha çox düşünmək lazımdır.

Metod 3: Bir metaclass

 class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] #Python2 class MyClass(BaseClass): __metaclass__ = Singleton #Python3 class MyClass(BaseClass, metaclass=Singleton): pass 

Arqumentlər

  • Bu əsl bir sinifdir.
  • Avtomatik sehrli miras əhatə edir
  • Onun məqsədi üçün __metaclass__ istifadə edir (və bu barədə mənə bildirin)

qarşı

  • Hər hansı bir varmı?

Metod 4: Dekoratör eyni adlı bir sinifə qayıdır

 def singleton(class_): class class_w(class_): _instance = None def __new__(class_, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs) class_w._instance._sealed = False return class_w._instance def __init__(self, *args, **kwargs): if self._sealed: return super(class_w, self).__init__(*args, **kwargs) self._sealed = True class_w.__name__ = class_.__name__ return class_w @singleton class MyClass(BaseClass): pass 

Arqumentlər

  • Bu əsl bir sinifdir.
  • Avtomatik sehrli miras əhatə edir

qarşı

  • Hər bir yeni sinif yaratmaq üçün hər hansı bir yük varmı? Burada hər sinif üçün iki sinif yaradırıq ki, biz təklik etmək istəyirik. Bu mənim vəziyyətimdə yaxşı olsa da, bunun ölçüsü olmaya biləcəyindən narahatlıq edirəm. Əlbəttə ki, bu şablonu ölçmək çox çətin olub olmadığına dair bir sual yaranır ...
  • _sealed xüsusiyyətin məqsədi _sealed
  • super() istifadə edərək, əsas siniflərdə eyni adı olan metodlara zəng edə bilməzsiniz, çünki onlar recursive olacaq. Bu, __new__ konfiqurasiya edə bilməzsiniz və __init__ əvvəl zəng etmək üçün lazım olan sinfi subclass edə bilməzsiniz.
600
20 июля '11 в 13:47 2011-07-20 13:47 İyul ayının 20-də saat 13 : 00 -da başlanacaq
@ 1 cavab

Metaklasdan istifadə

# 2 üsulunu təklif edirəm, lakin əsas sinifdən daha yaxşı metaclass istifadə edin. Burada bir nümunə tətbiq olunur:

 class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Logger(object): __metaclass__ = Singleton 

Və ya Python3

 class Logger(metaclass=Singleton): pass 

Hər dəfə sinif çağırıldığında __init__ işlətmək istəyirsinizsə, əlavə edin

  else: cls._instances[cls].__init__(*args, **kwargs) 

Singleton.__call__ .

Metaclasses haqqında bir neçə söz. Metaclass - sinif sinfi ; yəni bir sinif metaklasın bir nümunəsidir . Python type(obj) ilə bir obyekt metaclass tapırsınız. Yeni üslubun adi sinifləri tip type . Yuxarıdakı kodun Logger növü class 'your_module.Singleton' tipli class 'your_module.Singleton' olacaq, yalnız bir nümunə kimi (yalnız) Logger tipli class 'your_module.Logger' tipli olacaq. Logger() ilə bir qeydçi çağırdığınızda Python əvvəlcə bir nümunə yaratmağa imkan verən metaclass Logger , Singleton soruşur. Bu proses, __getattr__ , öz __getattr__ çağırarak __getattr__ Python sınıfı myclass.attribute .

Metaclass əsasən hansı təsnifatın nə demək olduğunu və bu tərifi necə həyata keçirməyi qərara alır. Metaclasses istifadə Python C-stil struct əsasən yaradan http://code.activestate.com/recipes/498149/ baxın. Akışta Python'daki metaclasses üçün (xüsusi) kullanım durumlarınız nedir? bəzi nümunələr də verilir, xüsusilə də ORM-də istifadə edildikləri üçün dekretativ proqramlaşdırma ilə əlaqəli olurlar.

Bu vəziyyətdə, # 2 üsulunu istifadə edirsinizsə və alt sinif __new__ metodunu müəyyənləşdirirsə, bu SubClassOfSingleton() adlandırdığınız hər dəfə icra ediləcək - çünki SubClassOfSingleton() üsulunu çağırır. Metaclass ilə bir tək nümunə yaradıldığında yalnız bir dəfə çağrılacaq . Bir sinif çağırışının bu cür müəyyən edildiyi anlamına gəlir .

Ümumiyyətlə, bir klinika tətbiq etmək üçün metaklasdan istifadə etmək mantiqidir. Singleton yalnız bir dəfə yaradıldığı üçün xüsusi və metaclass sinif yaradılması yaratmaq üçün bir yoldur. Metatlas istifadə edərək, başqa üsullarla tək sinif anlayışlarını özelleştirmeniz lazımdırsa, daha çox nəzarət verir.

Singlonsunuzun birdən çox devralmaya ehtiyacı yoxdur (metaclass bir əsas sinif olmadığı üçün), lakin birdən çox miras istifadə edən yaradılan sinifin alt sinifləri üçün, singleton sinifinin __call__ ləğv edən metaclass ilə ilk / __call__ əmin olun. Bu problem ola bilməz. Dict bir nümunə nümunə ad boş deyil , belə ki, təsadüfən onu yazmaq olmaz.

Həmçinin tək nümunənin "tək məsuliyyət prinsipi" ni pozduğunu eşidəcəksiniz - hər bir sinif bir şey etməlidir. Beləliklə, birinin dəyişdirilməsinə ehtiyac olduqda kodun bir şeyi pozduğundan narahat olmursunuz, çünki onlar ayrıdır və əhatə olunur. Metaklasın tətbiqi bu testdən keçir . Meta sinif, nümunəni tətbiq etmək üçün məsuliyyət daşıyır və yaradılan sinif və alt siniflər tək olduğunun fərqində deyil . . "MyClass" istifadə edərək qeyd etdiyiniz kimi # 1 üsulu bu testi yerinə yetirmir - bu bir funksiya deyil, bir sinifdir, belə ki, sinif metodlarından zəng edə bilməzsiniz. "

Python 2 və 3-nin uyğun versiyası

Python2 və 3-də işləyən bir şey yazma bir az daha mürəkkəb bir sxem tələb edir. Metaclasses type subclasses olduğundan, dinamik olaraq, metaclass kimi işlədilən vaxtda aralıq bazası sinfi yaratmaq üçün istifadə edə bilərsiniz və sonra onu Singleton ictimai bazasının əsas sinfi kimi istifadə edə bilərsiniz. Aşağıda göstərildiyi kimi, izah etmək daha çətindir:

 # works in Python 2  3 class _Singleton(type): """ A metaclass that creates a Singleton base class when called. """ _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Singleton(_Singleton('SingletonMeta', (object,), {})): pass class Logger(Singleton): pass 

Bu yanaşmanın istehzalı cəhəti metaklasın tətbiq edilməsi üçün alt siniflərdən istifadə etməkdir. Olası faydalardan biri, saf metaclassdan fərqli olaraq, isinstance(inst, Singleton) True qaytarır.

Düzeltmeler

Başqa bir mövzuda, yəqin ki bunu artıq fərq etmişsiniz, ancaq orijinal mesajınızdakı əsas sinifin tətbiqi səhvdir. _instances sinifdə göstərilməlidir , super() və ya recursively istifadə __new____new__ həqiqətən __new__ olduğunuz statik metoddur, çünki sinif metodu deyil, çünki faktiki sinif yaradılmır , lakin çağırıldıqda. Bütün bunlar metaklasın tətbiq edilməsi üçün doğru olacaqdır.

 class Singleton(object): _instances = {} def __new__(class_, *args, **kwargs): if class_ not in class_._instances: class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs) return class_._instances[class_] class MyClass(Singleton): pass c = MyClass() 

Dekoratör Dönən Sınıf

Mən əvvəlcə bir şərh yazdım, amma çox uzun idi, buna əlavə edəcəyəm. 4-cü üsul dekoratörün digər versiyasından daha yaxşıdır, amma bu, tək-tək üçün lazım olandan daha çox koddur və bu nə qədər aydın deyil.

Əsas problemlər sinifin əsas sinif olması ilə bağlıdır. Birincisi, bir sinif __class__ eyni adı olan demək olar ki, eyni sinifin bir alt sinifi olması qəribə deyilmi? Bununla yanaşı, super() ilə əsas sinifdə eyni adı olan metodu adlandırdığınız üsulları müəyyən edə bilməyəcəksiniz, çünki onlar təkrarlanacaqdır. Bu, sizin sinif __new__ konfiqurasiya edə bilməyəcəyini və __init__ tələb edən hər hansı bir sinifdən onlara müraciət edə __init__ bildirir.

Bir şablonu istifadə edərkən

Sizin istifadə halınız bir tək istifadə etmək istəyir ən yaxşı nümunələrindən biridir . Şərhlərdən birində deyirsiniz: "Mənim üçün jurnal həmişə təklinar təbii namizəd kimi görünürdü." Siz tamamilə haqlısınız .

İnsanlar təklərin pis olduğunu söyləyərkən, ən ümumi səbəb onların gizli bir dövlətdir . Qlobal dəyişənlər və üst səviyyəli modulların idxalı açıq bir ümumi dövlət olmasına baxmayaraq, qəbul edilən digər obyektlər adətən yaradılır. Bu iki istisna olmaqla yaxşı bir məqamdır.

Birincisi, fərqli yerlərdə qeyd olunan bir şey təkamül zamanlarıdır. Qlobal sabitlərin istifadəsi, xüsusən sayımlar, geniş qəbul edilir və məqbul sayılır, çünki heç bir halda heç bir istifadəçi onları başqa bir istifadəçi üçün qıra bilər . Bu da təklin sabitinə bərabər doğrudur.

Daha az qeyd olunduğu ikinci istisna əksinədir - təklink yalnız məlumat axını deyil, doğrudan və ya dolayı bir məlumat mənbəyidir. Qeydiyyatçılar tək oyunçular üçün "təbii" hiss edirlər. Fərqli istifadəçilər, qeydiyyatdan keçməyən digər istifadəçilərin qayğısına qalacaq şəkildə dəyişməmələri səbəbindən, həqiqətən paylaşılmış bir dövlət yoxdur . Bu tək nümunə modelinə qarşı əsas mübahisəni ləğv edir və vəzifəsi üçün asanlıqla istifadə etmək üçün onları ağlabatan seçim edir.

Http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :

İndi tək bir növ var, yaxşıdır. Bütün mövcud obyektlərin dəyişdirilə bilmədiyi bir təkdir. Bütün obyektlər dəyişdirilə bilməzsə, Singleton qlobal bir dövlətə malik deyildir, çünki hər şey qalıcıdır. Lakin bu təklüyü dəyişdirilə bilmək asan olur, çox sürüşür. Buna görə də, mən bu singletons qarşı deyiləm, çünki onlar pis, lakin onlar məhv etmək üçün çox asandır. (Əlavə bir qeyd olaraq, Java hesabı belə bir təkdir. Vəsiyyətinizə dövlət qoymadığınız müddətcə yaxşısınız, buna görə də bunu etməyin.)

Yarım məqbul olan digər növlər, kodunuzun icrasına təsir etməyənlərdir, "yan təsirləri" yoxdur. Bir jurnalın saxlanması böyük bir nümunədir. Birlik və qlobal bir dövlətlə yüklənir. Bu qəbul edilə bilər (çünki bu sizə zərər verməyəcək), çünki tətbiqiniz bu qeydiyyatdan keçməmişdən asılı olmayaraq, fərqli davranmır. Buradakı məlumat bir istiqamətdə axır: ərizənizdən qeydiyyatdan. Hətta mantıksal loglar qlobal bir dövlətdir, çünki qeydiyyatdan gələn məlumatlar ərizənizdə qəbul edilmir, qeydlər qəbul edilə bilər. Testinizin bir şeyin qeydiyyata alındığını bildirməsini istəyirsinizsə, qeydiyyatcıya hələ daxil olmalısan, amma ümumiyyətlə, qeydiyyatcılar dolu olmasına baxmayaraq, zərərli deyil.

408
23 июля '11 в 6:28 2011-07-23 06:28 cavab iyul ayının 23-də saat 11: 00-da saat 06 : 28- də veriləcək
 class Foo(object): pass some_global_variable = Foo() 
border=0

Modullar yalnız bir dəfə idxal olunur, hər şey çox üstündür. Singletons istifadə etməyin və ya qlobal dəyişənləri istifadə etməyə çalışın.

66
20 июля '11 в 13:52 2011-07-20 13:52 Cavab Cat Plus Plus tərəfindən 20 İyul, '11 'də 13:52 2011-07-20 13:52' də verilir

Modul istifadə edin. Yalnız bir dəfə idxal olunur. Burada hər hansı bir qlobal dəyişənləri müəyyənləşdirin - onlar təklüyün xüsusiyyətləri olacaqdır. Bəzi funksiyalar əlavə edin - tək üsullar.

52
20 июля '11 в 13:58 2011-07-20 13:58 cavabı 20 iyul 'da 13:58' da verildi

Python'ta bir singleton ehtiyacınız yoxdur. Sadəcə bütün məlumatlarınızı və funksiyalarınızı modulda təsvir edin və bir de facto singleton var.

Həqiqətən bir sinifə ehtiyacınız varsa, mən gedərdim:

 class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() 

İstifadəsi:

 from mysingleton import my_singleton my_singleton.foo() 

mysingleton.py, My_Singleton'un belirtildiği dosya adınızdır. Bu, bir faylın ilk idxalından sonra Python kodu yenidən işləməyəcəkdir.

14
11 дек. Cavab Alan Dyke tərəfindən 11 dekabrda verilir. 2014-12-11 01:24 '14 da 1:24 2014-12-11 01:24

Burada bir liner lazımdır:

 singleton = lambda c: c() 

Bunu necə istifadə edirsiniz:

 @singleton class wat(object): def __init__(self): self.x = 1 def get_x(self): return self.x assert wat.get_x() == 1 

Sizin obyektiniz səbirsizliklə bir surət alır. İstədiyiniz və ya olmaya bilər.

11
19 окт. Jonas Kölker'e cavab Oct 19 2013-10-19 17:43 '13 at 17:43 2013-10-19 17:43

Yığın daşması barədə sual ver Python-un təklinklərini aşkarlamaq üçün sadə və zərif bir yol varmı? bir çox həlli ilə.

Partiya 1-ci2- ci bölmədə Aleks Martellinin python dizayn desenlərini müzakirə etməyə gəlir. Xüsusilə, 1-ci hissədə, tək nöqtə / ümumi dövlət obyektləri haqqında danışır.

6
20 июля '11 в 14:00 2011-07-20 14:00 Cavab 20 iyul 'da saat 14: 00-da Anton tərəfindən verilir. 2011-07-20 14:00

İşdə özümün tək həyata keçirilməsi. Bütün etmək lazımdır, sinfi bəzəyir; Singleton almaq üçün Instance metodunu istifadə etməlisiniz. Burada bir nümunə:

  @Singleton class Foo: def __init__(self): print 'Foo created' f = Foo() # Error, this isn't how you get the instance of a singleton f = Foo.Instance() # Good. Being explicit is in line with the Python Zen g = Foo.Instance() # Returns already created instance print f is g # True 

Və burada kodu var:

 class Singleton: """ A non-thread-safe helper class to ease implementing singletons. This should be used as a decorator -- not a metaclass -- to the class that should be a singleton. The decorated class can define one `__init__` function that takes only the `self` argument. Other than that, there are no restrictions that apply to the decorated class. To get the singleton instance, use the `Instance` method. Trying to use `__call__` will result in a `TypeError` being raised. Limitations: The decorated class cannot be inherited from. """ def __init__(self, decorated): self._decorated = decorated def Instance(self): """ Returns the singleton instance. Upon its first call, it creates a new instance of the decorated class and calls its `__init__` method. On all subsequent calls, the already created instance is returned. """ try: return self._instance except AttributeError: self._instance = self._decorated() return self._instance def __call__(self): raise TypeError('Singletons must be accessed through `Instance()`.') def __instancecheck__(self, inst): return isinstance(inst, self._decorated) 
3
20 сент. Cavab Paul Manta tərəfindən 20 sentyabrda verilir. 2011-09-20 21:04 '11 at 21:04 2011-09-20 21:04
2
01 авг. Cavab verilir warvariuc 01 aug. 2011-08-01 17:20 '11 at 17:20 'da 2011-08-01 17:20

3-cü üsul çox təmiz görünür, amma Python 2Python 3 -də proqramınızın istifadəsini istəyirsinizsə, bu işləmir. Python versiyası 3 Python 2-də bir sözdizimi səhvini verir, çünki Python versiyası üçün testlərdən istifadə fərdi variantların qorunması da uğursuz olur.

Mike Watkins sayəsində: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Proqramın Python 2 və Python 3-də işləməsini istəyirsinizsə, aşağıdakı kimi bir şey etməlisiniz:

 class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] MC = Singleton('MC', (object), {}) class MyClass(MC): pass # Code for the class implementation 

Hesab edirəm ki, tapşırıqdakı "obyekt" "BaseClass" ilə əvəz olunmalıdır, amma bunu sınamamışam (kodu göstərildiyi kimi sınamışam).

2
24 июля '13 в 20:06 2013-07-24 20:06 Cavab Tim tərəfindən 24 iyul '13 'də saat 20:06' da verildi 2013-07-24 20:06

Qlobal bir modul səviyyəsinin ümumi pitonik təklifi ilə razılaşmaqdan daha yaxşıdır:

 def singleton(class_): class class_w(class_): _instance = None def __new__(class2, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs) class_w._instance._sealed = False return class_w._instance def __init__(self, *args, **kwargs): if self._sealed: return super(class_w, self).__init__(*args, **kwargs) self._sealed = True class_w.__name__ = class_.__name__ return class_w @singleton class MyClass(object): def __init__(self, text): print text @classmethod def name(class_): print class_.__name__ x = MyClass(111) x.name() y = MyClass(222) print id(x) == id(y) 

Çıxış:

 111 # the __init__ is called only on the 1st time MyClass # the __name__ is preserved True # this is actually the same instance 
2
25 июля '11 в 3:09 2011-07-25 03:09 Cavab 25.07.11 tarixində 3:09 2011-07-25 03:09

Bir liner (mən qürur deyiləm, amma işi yerinə yetirirəm):

 class Myclass: def __init__(self): # do your stuff globals()[type(self).__name__] = lambda: self # singletonify 
1
07 марта '18 в 7:50 2018-03-07 07:50 Polvazazul tərəfindən 07 mart 18: 07-də cavab verilib , 2018-03-07 07:50

Necə olar?

 def singleton(cls): instance=cls() cls.__new__ = cls.__call__= lambda cls: instance cls.__init__ = lambda self: None return instance 

Dərsxanada dekorativ olaraq istifadə edin, bir təkli olmalıdır. Burada:

 @singleton class MySingleton: #.... 

Bu, bir cavabda singleton = lambda c: c() dekoratörünə bənzəyir. Digər həlli olduğu kimi, tək nümunə sinif adı ( MySingleton ) var. Lakin, bu həlli ilə, MySingleton() bir sinifdən "hala" nümunələri (həqiqətən bir nümunə MySingleton() . Bu həmçinin type(MySingleton)() (həmçinin eyni type(MySingleton)() qaytarır type(MySingleton)() ilə əlavə hallarda yaratmaqdan mane olur.

1
18 мая '14 в 2:48 2014-05-18 02:48 Tolli tərəfindən 18 May 'da 14:48' də cevap verildi 2:48 2014-05-18 02:48

Kodu əsaslı tolli cavab .

 #decorator, modyfies new_cls def _singleton(new_cls): instance = new_cls() #2 def new(cls): if isinstance(instance, cls): #4 return instance else: raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls)) new_cls.__new__ = new #3 new_cls.__init__ = lambda self: None #5 return new_cls #decorator, creates new class def singleton(cls): new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1 return _singleton(new_cls) #metaclass def meta_singleton(name, bases, attrs): new_cls = type(name, bases, attrs) #1 return _singleton(new_cls) 

Şərhlər:

  • Verilən cls yeni bir sinif yaradın
    (əgər kiminsə istəsə, cls dəyişməz, məsələn, singleton(list) )

  • Bir nümunə yaradın. __new__ keçməzdən əvvəl, bu sadədir.

  • İndi asan bir nümunə yaratdığımız zaman, bir an əvvəl müəyyən edilmiş üsulla __new__ .
  • Funksiya yalnız bir gözləmə zəngini gözləyərkən bir instance qaytarır, əks halda TypeError meydana gəlir.
    Vəziyyəti bəzədilmiş sinifdən miras almağa çalışırsa razı deyil.

  • Əgər __new__() cls bir nümunəsini qaytarırsa, __new__() yeni bir nümunəsi __init__(self[, ...]) kimi adlanır __init__(self[, ...]) , özünü yeni bir nümunədir və qalan argumentlər __new__() üçün __new__() .

    instance artıq __init__ , funksiya __init__ heç bir şey etməyən funksiya ilə əvəz __init__ .

İnternette necə işlədiyini görün.

0
31 дек. Cavab GingerPlusPlus tərəfindən verilir 31 dekabr. 2014-12-31 00:47 '15 'də 0:47 2014-12-31 00:47

Cavab fab kimi bir az, lakin eyni deyil.

Singleton müqaviləsi bir neçə dəfə konstruktor çağırmamızı tələb etmir. Bir element bir dəfə və bir dəfə yaradılmalı olduğundan, bir dəfə yaradılmırmı? "Spoofing" qurucusu oxunaqlılığı azalda bilər.

Beləliklə, mənim təklifim:

 class Elvis(): def __init__(self): if hasattr(self.__class__, 'instance'): raise Exception() self.__class__.instance = self # initialisation code... @staticmethod def the(): if hasattr(Elvis, 'instance'): return Elvis.instance return Elvis() 

Bu, istifadəçi kodu ilə konstruktorun və ya instance sahəsinin istifadəsini istisna etmir:

 if Elvis() is King.instance: 

... Elvis hələ yaranmadığını və King nə olduğunu dəqiq bilirsən.

Но он поощряет пользователей использовать метод the универсально:

 Elvis.the().leave(Building.the()) 

Чтобы сделать это, вы можете также переопределить __delattr__() , чтобы создать исключение, если была сделана попытка удалить instance , и переопределить __del__() , чтобы он вызывал исключение (если мы не знаем, что программа заканчивается)..)

Дальнейшие улучшения


Я благодарю тех, кто помог с комментариями и изменениями, из которых больше приветствуются. Хотя я использую Jython, это должно работать в более общем плане и быть потокобезопасным.

 try: # This is jython-specific from synchronize import make_synchronized except ImportError: # This should work across different python implementations def make_synchronized(func): import threading func.__lock__ = threading.Lock() def synced_func(*args, **kws): with func.__lock__: return func(*args, **kws) return synced_func class Elvis(object): # NB must be subclass of object to use __new__ instance = None @classmethod @make_synchronized def __new__(cls, *args, **kwargs): if cls.instance is not None: raise Exception() cls.instance = object.__new__(cls, *args, **kwargs) return cls.instance def __init__(self): pass # initialisation code... @classmethod @make_synchronized def the(cls): if cls.instance is not None: return cls.instance return cls() 

Точки обзора:

  • Если вы не подклассом объекта в python2.x, вы получите класс старого стиля, который не использует __new__
  • При декорировании __new__ вы должны украсить с помощью @classmethod, или __new__ будет методом несвязанного экземпляра
  • Возможно, это возможно улучшить с помощью метакласса, поскольку это позволит вам сделать the свойство класса, возможно, переименовать его в instance
0
ответ дан mike rodent 21 февр. '16 в 11:48 2016-02-21 11:48

Я подброшу на ринг. Это простой декоратор.

 from abc import ABC def singleton(real_cls): class SingletonFactory(ABC): instance = None def __new__(cls, *args, **kwargs): if not cls.instance: cls.instance = real_cls(*args, **kwargs) return cls.instance SingletonFactory.register(real_cls) return SingletonFactory # Usage @singleton class YourClass: ... # Your normal implementation, no special requirements. 

Преимущества Я думаю, что это связано с некоторыми другими решениями:

  • Это ясно и кратким (на мой взгляд, D).
  • Его действие полностью инкапсулировано. Вам не нужно ничего менять в реализации YourClass . Это означает, что вам не нужно использовать метакласс для вашего класса (обратите внимание, что метакласс выше находится в factory, а не в "реальном" классе).
  • Он не полагается на обезвреживание обезьян.
  • Он прозрачен для вызывающих:
    • Абоненты по-прежнему просто импортируют YourClass , это похоже на класс (потому что это так), и они обычно используют его. Нет необходимости адаптировать вызывающих абонентов к функции factory.
    • То, что YourClass() создает экземпляр, по-прежнему является истинным экземпляром YourClass , который вы внедрили, а не прокси-сервером любого типа, поэтому нет никаких побочных эффектов, возникающих в результате этого.
    • isinstance(instance, YourClass) и подобные операции все еще работают как ожидалось (хотя этот бит требует abc, поэтому исключает Python < 2.6).

Один недостаток имеет место для меня: classmethods и staticmethods реального класса не могут быть прозрачно вызываемыми через класс factory, скрывающий его. Я использовал это достаточно редко, так что я никогда не сталкивался с этой необходимостью, но его можно было бы легко исправить, используя специальный метакласс в factory, который реализует __getattr__() , чтобы делегировать доступ к атрибуту all-ish к реальному класс.

Связанный шаблон, который я на самом деле нашел более полезным (не то, что я говорю, что такие вещи требуются очень часто вообще) - это "уникальный" шаблон, при котором экземпляр класса с теми же аргументами приводит к возврату тот же экземпляр. То есть "одиночный аргумент". Вышеприведенное приспособлено к этой скважине и становится еще более кратким:

 def unique(real_cls): class UniqueFactory(ABC): @functools.lru_cache(None) # Handy for 3.2+, but use any memoization decorator you like def __new__(cls, *args, **kwargs): return real_cls(*args, **kwargs) UniqueFactory.register(real_cls) return UniqueFactory 

Все, что сказал, я согласен с общим советом, что, если вы считаете, что вам нужна одна из этих вещей, вам действительно стоит остановиться на мгновение и спросить себя, действительно ли вы это делаете. 99% времени, YAGNI.

0
ответ дан CryingCyclops 28 авг. '16 в 1:22 2016-08-28 01:22

Я не могу вспомнить, где я нашел это решение, но я считаю его самым "элегантным" с моей точки зрения не с точки зрения Python:

 class SomeSingleton(dict): __instance__ = None def __new__(cls, *args,**kwargs): if SomeSingleton.__instance__ is None: SomeSingleton.__instance__ = dict.__new__(cls) return SomeSingleton.__instance__ def __init__(self): pass def some_func(self,arg): pass 

Почему мне это нравится? Нет декораторов, нет мета-классов, нет множественного наследования... и если вы решите, что не хотите, чтобы он был Singleton, просто удалите метод __new__ . Поскольку я новичок в Python (и ООП в целом), я ожидаю, что кто-то задаст мне, почему это ужасный подход?

0
ответ дан 2cynykyl 11 нояб. '14 в 8:38 2014-11-11 08:38

Этот ответ скорее всего не то, что вы ищете. Мне нужен синглтон в том смысле, что только этот объект имел свою идентичность для сравнения. В моем случае он использовался как Sentinel Value . Для чего ответ очень прост, сделайте любой объект mything = object() и природой питона, только эта вещь будет иметь свою идентичность.

 #!python MyNone = object() # The singleton for item in my_list: if item is MyNone: # An Example identity comparison raise StopIteration 
-1
ответ дан ThorSummoner 29 сент. '16 в 21:40 2016-09-29 21:40

Это мой предпочтительный способ реализации синглетонов:

 class Test(object): obj = None def __init__(self): if Test.obj is not None: raise Exception('A Test Singleton instance already exists') # Initialization code here @classmethod def get_instance(cls): if cls.obj is None: cls.obj = Test() return cls.obj @classmethod def custom_method(cls): obj = cls.get_instance() # Custom Code here 
-1
ответ дан fab 30 окт. '14 в 2:12 2014-10-30 02:12

Это решение вызывает некоторое загрязнение пространства имен на уровне модуля (три определения, а не только одно), но мне легко следовать.

Я хотел бы написать что-то вроде этого (ленивая инициализация), но, к сожалению, классы не доступны в теле их собственных определений.

 # wouldn't it be nice if we could do this? class Foo(object): instance = None def __new__(cls): if cls.instance is None: cls.instance = object() cls.instance.__class__ = Foo return cls.instance 

Так как это невозможно, мы можем разбить инициализацию и статический экземпляр в

Ожидаемая инициализация:

 import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): return foo_instance foo_instance = FooMaker() foo_instance.__class__ = Foo 

Ленивая инициализация:

Ожидаемая инициализация:

 import random class FooMaker(object): def __init__(self, *args): self._count = random.random() self._args = args class Foo(object): def __new__(self): global foo_instance if foo_instance is None: foo_instance = FooMaker() return foo_instance foo_instance = None 
-1
ответ дан Gregory Nisbet 01 нояб. '16 в 1:07 2016-11-01 01:07

Другие вопросы по меткам или Задайте вопрос