Python'da bir singleton oluşturma


945

Bu soru, tektonlu tasarım deseninin istenip istenmediği, bir anti-desen olup olmadığı veya herhangi bir dini savaş için tartışılmak değil, bu desenin Python'da en çok pitonik bir şekilde en iyi nasıl uygulandığını tartışmak içindir. Bu örnekte, 'en az şaşkınlık ilkesini' izlediği anlamına gelen 'en pitonik' tanımlıyorum .

Tek tek haline gelecek birden çok sınıfım var (benim kullanım durumum bir logger içindir, ama bu önemli değil). Ben sadece miras veya süslemek zaman ekledi gumph ile birkaç sınıflar dağınık istemiyorum.

En iyi yöntemler:


Yöntem 1: Bir dekoratör

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

Artıları

  • Dekoratörler, genellikle birden fazla mirastan daha sezgisel bir şekilde katkı maddesidir.

Eksileri

  • MyClass () kullanılarak oluşturulan nesneler gerçek tekli nesneler olsa da, MyClass'ın kendisi bir sınıf değil, bir işlevdir, bu nedenle sınıf yöntemlerini çağıramazsınız. Ayrıca m = MyClass(); n = MyClass(); o = type(n)();o zaman içinm == n && m != o && n != o

Yöntem 2: Bir temel sınıf

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

Artıları

  • Gerçek bir sınıf

Eksileri

  • Çoklu kalıtım - eugh! __new__ikinci bir temel sınıftan miras sırasında üzerine yazılabilir mi? Kişi gereğinden fazla düşünmek zorundadır.

Yöntem 3: Metasınıf

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

Artıları

  • Gerçek bir sınıf
  • Kalıtımla otomatik olarak kaplanır
  • __metaclass__Uygun amacı için kullanır (ve beni haberdar etti)

Eksileri

  • Orada hiç?

Yöntem 4: Aynı ada sahip bir sınıf döndüren dekoratö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

Artıları

  • Gerçek bir sınıf
  • Kalıtımla otomatik olarak kaplanır

Eksileri

  • Her yeni sınıfı oluşturmak için ek yük yok mu? Burada tek bir ders yapmak istediğimiz her sınıf için iki sınıf yaratıyoruz. Benim durumumda bu iyi olsa da, bunun ölçeklenmeyebileceğinden endişeleniyorum. Tabii ki bu kalıbı ölçeklendirmenin çok kolay olup olmadığı konusunda bir tartışma konusu var ...
  • _sealedÖzelliğin amacı nedir
  • Aynı sınıftaki yöntemleri temel sınıflarda çağıracakları için çağıramazlar super(). Bu __new__, çağrı yapmanız gereken bir sınıfı özelleştiremeyeceğiniz ve sınıflandıramayacağınız anlamına gelir __init__.

Yöntem 5: bir modül

bir modül dosyası singleton.py

Artıları

  • Basit karmaşık olmaktan iyidir

Eksileri


12
Başka üç teknik: bunun yerine bir modül kullanın (genellikle - genellikle, sanırım - bu Python için daha uygun bir modeldir, ancak biraz onunla ne yaptığınıza bağlıdır); Tek örneği yapmak yerine onunla başa ( foo.xveya eğer ısrar Foo.xyerine Foo().x); sınıf özniteliklerini ve statik / sınıf yöntemlerini ( Foo.x) kullanın.
Chris Morgan

10
@ChrisMorgan: Yalnızca class / static yöntemlerini kullanacaksanız, gerçekten sınıf oluşturmayı zahmet etmeyin.
Cat Plus Plus

2
@Cat: Etki benzer, ancak küresel bir değişken yaratmanın nedenleri daha iyi bilmemek de dahil olmak üzere hemen hemen her şey olabilir. Neden bir singleton yaratılır? Eğer sormak zorundaysan, burada olmamalısın. Bu açıklık sadece daha pitonik olmakla kalmaz, aynı zamanda bakımı çok daha basit hale getirir. Evet, singletonlar küreseller için sözdizimsel şekerdir, ancak daha sonra sınıflar bir sürü çirkin şey için sözdizimsel şekerdir ve kimsenin size onlar olmadan her zaman daha iyi olduğunuzu söyleyeceğini sanmıyorum.
theheadofabroom

14
@BiggAl: Singletons, bunları nasıl uygularsanız uygulasın Pythonic değildir . En iyi ihtimalle kusurlu bir tasarımın işaretidir.
Cat Plus Plus

8
Anti-signletons hissi en kötü ihtimalle kargo kült programlamadır. İnsanlarla aynı (çok az okumak gerçekten zahmetli) "Git ifadesi zararlı sayılır" ve düşünmek gotos bağlamdan bağımsız olarak kötü bir kod işareti olduğunu düşünüyorum.
Hejazzman

Yanıtlar:


658

Metasınıf Kullan

Yöntem # 2'yi öneririm , ancak bir temel sınıftan daha çok bir metasınıf kullanmaktan daha iyisiniz . İşte örnek bir uygulama:

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

Veya Python3'te

class Logger(metaclass=Singleton):
    pass

__init__Sınıf her çağrıldığında çalıştırmak istiyorsanız ,

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

için ifde açıklamada Singleton.__call__.

Metasınıflar hakkında birkaç söz. Metasınıf bir sınıfın sınıfıdır ; yani, sınıf meta sınıfının bir örneğidir . Python'da bir nesnenin metasınıfını ile bulabilirsiniz type(obj). Normal yeni stil sınıfları türdedir type. Loggeryukarıdaki kodda class 'your_module.Singleton', tıpkı (yalnızca) örneğinin Loggertipte olacağı gibi class 'your_module.Logger'. Birlikte logger aradığınızda Logger(), Python ilk metaclass sorar Logger, Singleton, örnek oluşturma önceden empted sağlayan ne yapacağını. Bu işlem, bir sınıfa __getattr__özniteliklerinden birine başvurarak ne yapılacağını soran Python ile aynıdır myclass.attribute.

Bir metasınıf esasen bir sınıf tanımının ne anlama geldiğine ve bu tanımın nasıl uygulanacağına karar verir . Örneğin , bkz. Http://code.activestate.com/recipes/498149/ , temelde structmetasınıflar kullanarak Python'daki C stilini yeniden oluşturur . İplik Metasınıflar için bazı (somut) kullanım durumları nelerdir? ayrıca bazı örnekler de sağlarlar, özellikle ORM'lerde kullanıldığı gibi, genellikle bildirimsel programlama ile ilişkili görünmektedirler.

Bu durumda, Yöntem # 2'nizi kullanırsanız ve bir alt sınıf bir __new__yöntem tanımlarsa , her aradığınızda yürütülürSubClassOfSingleton() - depolanan örneği döndüren yöntemi çağırmaktan sorumludur. Bir metasınıfla, tek örnek oluşturulduğunda yalnızca bir kez çağrılacaktır . Sınıfa göre karar vermenin ne anlama geldiğini özelleştirmek istersiniz .

Genel olarak, bir singleton uygulamak için bir metasınıf kullanmak mantıklıdır . Singleton özeldir, çünkü sadece bir kez oluşturulur ve metasınıf bir sınıfın oluşturulmasını özelleştirmenizdir . Bir metasınıf kullanmak , singleton sınıf tanımlarını başka şekillerde özelleştirmeniz gerektiğinde size daha fazla kontrol sağlar .

Teklitonlarınızın birden fazla kalıtıma ihtiyacı yoktur (metasınıf temel bir sınıf olmadığından), ancak birden fazla kalıtım kullanan oluşturulan sınıfın alt sınıfları için , singleton sınıfının, yeniden tanımlayan bir metasınıflı ilk / en soldaki sınıf olduğundan emin olmanız gerekir. __call__Bunun bir sorun olması pek olası değildir. Örnek diktesi, örneğin ad alanında değildir, dolayısıyla yanlışlıkla üzerine yazmaz.

Ayrıca, tekil paternin "Tek Sorumluluk İlkesi" ni ihlal ettiğini de duyacaksınız - her sınıf sadece bir şey yapmalıdır . Bu şekilde, kodun bir başkasını değiştirmeniz gerekiyorsa bir şeyi karıştırmaktan endişelenmenize gerek kalmaz, çünkü bunlar ayrı ve kapsüllenmiştir. Metaclass uygulaması bu testi geçer . Metaclass, kalıbın uygulanmasından sorumludur ve yaratılan sınıf ve alt sınıfların tekton olduklarının farkında olmaları gerekmez . Yöntem 1. Birlikte belirtildiği gibi, bu testi başarısız "ondan sınıf yöntemleri çağırmak olamaz yani kendisi aa fonksiyon, bir sınıftır Sınıfım."

Python 2 ve 3 Uyumlu Sürüm

Hem Python2 hem de 3'te çalışan bir şey yazmak biraz daha karmaşık bir şema kullanmayı gerektirir. Metasınıflar genellikle tip alt sınıflar olduğundan , metasınıfı typeolarak çalışma zamanında dinamik olarak bir ara taban sınıfı oluşturmak için birini kullanmak ve daha sonra bunu kamu Singletontaban sınıfının temel sınıfı olarak kullanmak mümkündür. Daha sonra açıklandığı gibi açıklamak daha zordur:

# 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 yaklaşımın ironik bir yönü, bir metasınıf uygulamak için alt sınıflandırma kullanmasıdır. Olası bir avantaj, saf bir metasınıfın aksine isinstance(inst, Singleton)geri dönmesidir True.

Düzeltmeler

Başka bir konuda, muhtemelen bunu zaten fark ettiniz, ancak orijinal yayınınızdaki temel sınıf uygulaması yanlış. _instancesihtiyaçlar edilecek sınıfına başvurulan , kullanmak gerekmez super()veya sen recursing ve __new__sen zorunda aslında statik bir yöntemdir için sınıf geçmek fiili sınıf olarak değil, bir sınıf yöntemi oluşturulmadı henüz ne zaman denir. Bütün bunlar bir metasınıf uygulaması için de geçerli olacak.

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()

Sınıf Dönen Dekoratör

Başlangıçta bir yorum yazıyordum ama çok uzundu, bu yüzden buraya ekleyeceğim. Yöntem # 4 , diğer dekoratör sürümünden daha iyidir, ancak tek birton için gerekenden daha fazla koddur ve ne yaptığı açık değildir.

Temel problemler sınıfın kendi temel sınıfı olmasından kaynaklanmaktadır. Birincisi, bir sınıfın, yalnızca __class__özniteliğinde var olan aynı ada sahip neredeyse aynı sınıfın bir alt sınıfı olması garip değil mi? Bu aynı zamanda tanımlamak anlamına gelir onların temel sınıf aynı adı taşıyan yöntemini çağırın herhangi yöntemleri ile super()onlar Recurse çünkü. Bu, sınıfınızın özelleştirilemeyeceği __new__ve __init__çağrılması gereken sınıflardan türeyemeyeceği anlamına gelir .

Singleton paterni ne zaman kullanılır?

Kullanım durumunuz, singleton kullanmak isteyebileceğiniz en iyi örneklerden biridir. Bir yorumda "Bana göre giriş her zaman Singletons için doğal bir aday gibi göründü" diyorsunuz. Sen kesinlikle doğru .

İnsanlar singletonların kötü olduğunu söylediklerinde en yaygın neden örtülü paylaşılan devlet olmalarıdır . Genel değişkenler ve üst düzey modül içe aktarmaları açık paylaşılan durum olsa da, iletilen diğer nesneler genellikle somutlaştırılır. Bu iki istisna dışında iyi bir nokta .

Birincisi ve çeşitli yerlerde bahsedilen, tektonların sabit olduğu zamandır . Küresel sabitler, özellikle çeteleler kullanımı yaygın olarak kabul ve aklı başında sayılır, çünkü ne olursa olsun, başka bir kullanıcı için kullanıcı hiçbiri mahvedebilir onları . Bu, sabit bir singleton için de aynı şekilde geçerlidir.

Daha az bahsedilen ikinci istisna tam tersidir - singleton bir veri kaynağı değil, doğrudan veya dolaylı olarak sadece bir veri havuzu olduğunda. Bu nedenle logger'lar singletonlar için "doğal" bir kullanım gibi hissederler. Çeşitli kullanıcılar , günlükçüleri diğer kullanıcıların önem verdiği şekilde değiştirmediği için , gerçekten paylaşılan bir durum yoktur . Bu, singleton desenine karşı birincil argümanı reddeder ve görev için kullanım kolaylığı nedeniyle onları makul bir seçim yapar .

Http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html adresinden bir alıntı :

Şimdi, bir tür Singleton var. Bu, ulaşılabilir tüm nesnelerin değişmez olduğu bir singleton. Tüm nesneler Singleton'dan değişmezse, her şey sabit olduğu için küresel bir durumu yoktur. Ancak bu tür singletonu mutable olana dönüştürmek çok kolaydır, çok kaygan bir eğimdir. Bu nedenle, bu Singletonlara da karşıyım, kötü oldukları için değil, kötü gitmeleri çok kolay olduğu için. (Bir yan not olarak Java numaralandırması sadece bu tür tekil tonlardır. Numaralandırmanıza durum koymadığınız sürece sorun değil, lütfen yapmayın.)

Yarı-kabul edilebilir olan diğer Singletons, kodunuzun yürütülmesini etkilemeyenlerdir, "yan etkisi" yoktur. Günlüğe kaydetme mükemmel bir örnektir. Singletons ve global durum ile yüklenir. Uygulamanız belirli bir günlükçünün etkinleştirilip etkinleştirilmediği konusunda farklı davranmadığı için (içinde olduğu gibi size zarar vermeyecektir) kabul edilebilir. Buradaki bilgiler tek bir şekilde akar: Uygulamanızdan kaydediciye. Günlükçülerden uygulamanıza hiçbir bilgi akışı olmadığından, günlükçüler küresel durumdur, günlükçüler kabul edilebilir. Testinizin bir şeyin günlüğe kaydedildiğini iddia etmesini istiyorsanız, kaydedicinizi enjekte etmelisiniz, ancak genel olarak Logger'lar durum dolu olmasına rağmen zararlı değildir.


5
Hayır, singletonlar asla iyi değildir. Günlüğe kaydetme küresel olmak için iyi bir aday olabilir (oldukları kadar korkunç), ama kesinlikle singleton değil.
Cat Plus Plus

11
Googletesting.blogspot.com/2008/08/… adresine bakın . Genellikle anti-singleton'dur (iyi bir nedenden dolayı), ancak dikkatli değilseniz, değişmez singletonların ve yan etkileri olmayan singletonların neden aynı sorunlara sahip olmadığını iyi bir şekilde açıklar. Yazımın sonunda biraz alıntı yapacağım.
agf

4
Singletons ile ilgili sorunum, "sadece bir örnek" in aptalca öncülüydü. Bu ve tonlarca iplik güvenlik problemleri. Ve bağımlılık gizleniyor. Globaller kötüdür ve singletonlar sadece daha fazla sorunu olan globallerdir.
Cat Plus Plus

4
@Cat Singletonlar için çok iyi kullanımlar vardır. Donanım modüllerinin tembel olarak başlatılması (özellikle tek dişli uygulamalarda) bunlardan biridir (ancak iplik güvenli tektonlar da vardır).
Paul Manta

3
@ Metasınıftaki alcott sınıfın yeni __new__olduğu zaman - tanımlandığında, örneğin yeni olacağı zaman değil . Class ( ) öğesini çağırmak, sınıfın tanımı değil, geçersiz kılmak istediğiniz işlemdir. Python'un nasıl çalıştığını gerçekten anlamak istiyorsanız, yapabileceğiniz en iyi şey (kullanmaya devam etmek dışında) docs.python.org/reference/datamodel.html . Metasınıflar için iyi bir referans eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example'dir . Singletons hakkında iyi bir makale, bu cevapta bağladığım google blogundaki dizidir. MyClass()
agf

91
class Foo(object):
     pass

some_global_variable = Foo()

Modüller sadece bir kez içe aktarılır, diğer her şey aşırı düşünülür. Singleton kullanmayın ve globalleri kullanmamaya çalışın.


14
neden "singleton kullanma" dedin? Herhangi bir nedenle?
Alcott

3
Singleton turşusu gerekirse bu işe yaramaz. s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
Verdiğiniz

4
@dividebyzero: isoperatör işaretçi eşitliğini test eder. Ben pickle.loadsyeni bir oluşturulan bir referans yerine önceden var olan bir nesneye bir referans döndü eğer --- bir hata deme noktasına oldukça şaşırırdım. Böylece, testlerin s is s1modüllerin tekil olarak kullanılmasının uygunluğu hakkında bir şey söylemediğini test etmek .
Jonas Kölker

1
@ JonasKölker pickle.loads()örnekleri için örneğin zaten böyle yapıyor boolve NoneType. pickle.loads(pickle.dumps(False)) is FalseverimTrue
Dan Passaro

2
@ leo-the-manic: adil nokta; Ancak, nesneleri stajyerlik Python sadece bir yan etkisi True, Falseve None, ve kod arkasında ile ilgisi yoktur pickle.loads. Ayrıca, yalnızca salt okunur nesneler için güvenlidir. Eğer pickle.loadsbir başvuru dönmek için zaten mevcut olan değiştirilebilir nesne, örneğin bir modül-hatanın gibi. (Ve böylece, dividebyzero'nun kod örneğinin hiçbir şey kanıtlamadığı
sonucuna göre duruyorum

69

Bir modül kullanın. Yalnızca bir kez içe aktarılır. İçindeki bazı küresel değişkenleri tanımlayın - bunlar singleton'un 'nitelikleri' olacaktır. Bazı fonksiyonlar ekleyin - singleton'un 'yöntemleri'.


11
Sonuç olarak ... Sınıf değil. Bir sınıf olarak kullanamazsınız, üzerine başka sınıflar
dayatamazsınız

16
Eğer diğer sınıfları dayandırabilirseniz, bu bir singleton olmayabilir. türetilmiş sınıftan birini değil, aynı zamanda temel sınıftan birini de oluşturabilirsiniz, ancak türetilmiş sınıf da tabanın bir üyesidir ve tabandan iki tanesine sahipsiniz, hangisini kullanmanız gerekiyor?
SingleNegationElimination

Bu modüller arasında çalışmaz. "Ana" modülümde bir değer ayarladım. Sonra başka bir modül ve null referans. İç çekmek.
Paul Kenjora

1
@PaulKenjora Kodunuzda bir hata olması gerekir. Bir modülde global bir değişken tanımlarsanız, başka bir modülden eriştiğinizde bu değere sahip olmalıdır.
warvariuc

Değeri var ama değiştirdiğim zaman korunmuyor. Ben çalışan bir modül özellikleri ile bir sınıf kullanarak sona erdi. Küresel olarak basit türler benim için işe yaramadı (kapsam değiştikçe değerleri kaybettiler).
Paul Kenjora

29

Muhtemelen Python'da asla bir singletona ihtiyacınız yoktur. Tüm verilerinizi ve işlevlerinizi bir modülde tanımlayın ve fiili bir tektonuz var.

Gerçekten kesinlikle bir singleton sınıf olması gerekiyorsa ben ile gitmek istiyorum:

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

my_singleton = My_Singleton()

Kullanmak:

from mysingleton import my_singleton
my_singleton.foo()

Burada mysingleton.py, My_Singleton'un tanımlandığı dosya adınızdır. Bu, bir dosyanın ilk kez içe aktarılmasından sonra Python'un kodu yeniden yürütmediği için çalışır.


4
Çoğunlukla doğru, ama bazen bu yeterli değil. Örneğin, DEBUG düzeyinde birçok sınıfın örneklemelerinin kaydedilmesi gereken bir projem var. Bu sınıflar başlatılmadan önce kullanıcı tarafından belirtilen günlük düzeyini ayarlamak için başlangıçta ayrıştırılan komut satırı seçenekleri gerekir . Modül düzeyindeki örnekler bunu sorunlu hale getirir. Uygulamayı dikkatlice yapılandırabilirim, böylece CLI işleme tamamlanana kadar bu sınıfların tümü içe aktarılmaz, ancak uygulamanızın doğal yapısı "singletonlar kötü" ye dogmatik bağlılıktan daha önemlidir, çünkü oldukça temiz yapılır.
16:29

Eğer my_singleton yamasını yaparken kodunuzu test etmek olsaydı, bu mümkün olurdu? çünkü bu my_singleton başka bir modülde somutlaştırılabilir.
Naveen

@Naveen - my_singleton tek bir nesnedir. Eğer "yama" yaparsanız, bu değişiklik diğer modüllerde bile gelecekteki tüm referansları etkiler.
Alan Dyke

16

İşte size bir astar:

singleton = lambda c: c()

İşte nasıl kullanacağınız:

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

assert wat.get_x() == 1

Nesneniz hevesle başlatılıyor. İstediğiniz şey bu olabilir veya olmayabilir.


5
-1. Bu çok kötü ve aptalca. Nesne olarak kullanmak için bir sınıf tanımlamazsınız. Artık böyle çok çirkin olmadan sınıfa erişebilirsiniz type(wat)ya wat.__class__. Bunu gerçekten başarmak istiyorsanız, sınıfı daha iyi tanımlayın ve hemen başlatın, dekoratörle uğraşmanıza gerek yok.
0xc0de

2
Neden bir singleton sınıfını kullanmayı bilmeniz gerekiyor? Sadece singleton nesnesini kullanın ..
Tolli

1
Tekil desen değil , bu nedenle IMO işlevi farklı adlandırılmalıdır.
GingerPlusPlus

8
Wikipedia: "tekil desen, bir sınıfın bir nesneye örneklenmesini kısıtlayan bir tasarım modelidir". Çözümümün bunu yaptığını söyleyebilirim. Tamam, sanırım biri yapabilirdi wat2 = type(wat)(), ama bu python, hepimiz yetişkinlere ve tüm bunlara izin veriyoruz. Yalnızca tek bir örnek olacağını garanti edemezsiniz , ancak insanlar ikinci bir örnek oluşturursanız, çirkin görüneceklerini ve eğer iyi, dikinsan insanlarsa onlara bir uyarı işareti gibi olacağını garanti edebilirsiniz. Neyi kaçırıyorum?
Jonas Kölker


3

İşte benim singleton uygulaması. Tek yapmanız gereken sınıfı süslemektir; singleton'u elde etmek için, Instanceyöntemi kullanmanız gerekir . İşte bir örnek:

   @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

Ve işte kod:

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)

2
Gerçek singleton değil. gerçek singletonda SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())yazdırmalıdır True; kodunuz yazdırılıyorFalse
GingerPlusPlus

@GingerPlusPlus Birkaç sınırlamanın farkındaydım, ancak işaret ettiğinden değil. Bahsettiğiniz için teşekkürler. Ne yazık ki, şu anda buna bir çözüm bulmak için zamanım yok.
Paul Manta

2

Yöntem 3 çok düzgün gibi görünüyor, ancak her ikisi de pistine Programınızı istiyorsanız Python 2 ve Python 3 , bu işi yapmaz. Python 3 sürümü Python 2'de sözdizimi hatası verdiğinden, ayrı varyantları Python sürümü testleriyle korumak bile başarısız olur.

Mike Watkins'e teşekkürler: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Programın hem Python 2 hem de Python 3'te çalışmasını istiyorsanız, aşağıdaki gibi bir şey yapmanız gerekir:

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

Ödevde 'nesne' yerine 'BaseClass' değiştirilmesi gerektiğini varsayıyorum, ama bunu denemedim (Ben gösterildiği gibi kodu denedim).


elbette bu bir metasınıf değil - python3'te MyClass'ı inşa etmek için bir metaclass kullanmak içinclass MyClass(metaclass=Singleton)
theheadofabroom

Mikewatkins.ca bağlantı (etkili) kırık olduğunu.
Peter Mortensen

1

Peki, modül düzeyinde küresel olma konusunda genel Pythonic önerisini kabul etmek dışında, buna ne dersiniz:

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)

Çıktı:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

_sealedÖzelliğin amacı nedir ? Gördüğüm kadarıyla bu hiçbir şey yapmıyor mu? İyi performans göstermemesi gerektiğini söyleyen bir şey beni rahatsız ediyor ... Bu haftanın ilerleyen saatlerinde karşılaştırmalı testler yapacağım.
theheadofabroom

_sealed sizin sağlayan init sadece bir kez koştu olduğu; Neden normal fonksiyona benzer dekoratörden daha kötü performans göstermesi gerektiğini anlamıyorum - fonksiyon sınıf başına sadece bir kez yürütülür ve yeni kalıtsal bir sınıf döndürür
Guard

BTW, düzenlemeniz girintileri kıran sekmeler içeriyor Ayrıca '2 sınıf oluşturuyoruz' diyorsunuz - '1 ekstra sınıf' oluşturduğumuzu mu söylüyorsunuz?
Guard

Evet demek istediğim ekstra bir sınıf. Ben __init__her başlatıldığında çağrılacak şeyler dahil etmek niyetinde . Sadece basit bir 'class.method'da başlatılmak'. girintiye gelince - sekmeleri ve boşlukları kullandınız - çoğunu düzelttim, ancak almak istiyorsanız birini kaçırmış gibi görünüyor (sadece düzenleme günlüğünü kontrol edin)
theheadofabroom

re init : elbette size kalmış, sadece diğer dillerde singleton davranışını taklit etmeye çalıştım, burada yapıcı kodu (tam olarak init değil , anlamında çok yakın) sadece initin olmasını istiyorsanız bir kez çağrılır her seferinde çağrıldığında, _sealed re spaces / tabs için tüm referansları öldürün - o zaman emacs'ımın düzeltilmesi gerekiyor. her neyse, yukarıda düzeltilmiş versiyonu
Guard

1

Buna ne dersin:

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

Tekli olması gereken bir sınıfta dekoratör olarak kullanın. Bunun gibi:

@singleton
class MySingleton:
    #....

Bu singleton = lambda c: c()başka bir cevapta dekoratöre benzer . Diğer çözüm gibi, tek örnek de class ( MySingleton) adına sahiptir . Ancak, bu çözüm ile yine de sınıftan örnekler yaratabilirsiniz (aslında tek örneği alırsınız) MySingleton(). Ayrıca yaparak type(MySingleton)()(aynı örneği döndüren) ek örnekler oluşturmanızı da engeller .


Bir sınıfı nesne olarak kullanmak için tanımlamazsınız.
0xc0de

1
Her aradığınızda type(MySingleton)(), MySingleton.__init__()çağrılır ve nesne birden çok kez başlatılır; Yazmakta tamir edebilir cls.__init__ = lambda self: passGözlerinde farklı singleton. Ayrıca, geçersiz kılma cls.__call__anlamsız ve hatta zararlı görünüyor - __call__bu bağlamda tanımlanan, aradığınızda MySingleton(any, list, of, arguments)değil, aradığınızda kullanılır type(MySingleton)(any, list, of, arguments).
GingerPlusPlus

@GingerPlusPlus, Yaparken __init__()tekrar çağrılan işaret ettiğin için teşekkürler type(MySingleton)(). Önerilen (ekleme cls.__init__ = lambda self: pass) çözüm bir sözdizimi hatası verir, çünkü lambda ifadesinin son kısmının bir ifade değil, bir ifade olması gerekir. Ancak, iş ekleyerek cls.__init__ = lambda self: None, cevabımı ekledim.
Tolli

1
@GingerPlusPlus, kullanımı ile ilgili __call__. niyetim her ikisini de yapmak type(MySingleton)()ve MySingleton()örneği geri vermekti. Yani istediğimi yapıyor. MySingleton'u singletonun türü veya singleton örneği (veya her ikisi) olarak düşünebilirsiniz.
Tolli

1

Benimkini ringe atacağım. Basit bir dekoratör.

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.

Avantajları Bence diğer bazı çözümler üzerinde:

  • Açık ve özlüdür (gözüme; D).
  • Eylemi tamamen kapsüllenmiştir. Uygulamasının tek bir şeyi değiştirmenize gerek yoktur YourClass. Bu, sınıfınız için bir metasınıf kullanmaya gerek duymamayı içerir (yukarıdaki metasınıfın "gerçek" sınıfta değil, fabrikada olduğunu unutmayın).
  • Maymun yamalı hiçbir şeye dayanmaz.
  • Arayanlar için şeffaftır:
    • Arayanlar hala içe aktarıyor YourClass, bir sınıf gibi görünüyor (çünkü) ve normal olarak kullanıyorlar. Arayanları fabrika işlevine uyarlamaya gerek yoktur.
    • Ne YourClass()başlatır hala gerçek bir örneğidir YourClassyan etkileri şansı o kaynaklanan nedenle, her türlü değil, bir vekil uyguladı.
    • isinstance(instance, YourClass) ve benzer işlemler hala beklendiği gibi çalışır (bu bit abc gerektirmesine rağmen Python <2.6'yı engeller).

Benim için bir dezavantaj oluşuyor: gerçek sınıfın sınıf yöntemleri ve statik yöntemleri, onu gizleyen fabrika sınıfı aracılığıyla şeffaf bir şekilde çağrılamaz. Bunu nadiren bu ihtiyaçla karşılaşmayacak kadar kullandım, ancak __getattr__()gerçek sınıfa tüm-ish öznitelik erişimini delege etmek için uygulayan fabrikada özel bir metasınıf kullanılarak kolayca düzeltilebilir .

Aslında daha yararlı bulduğum ilgili bir desen (bu tür şeylerin çok sık gerekli olduğunu söylemiyorum), aynı argümanlarla sınıfı başlatmanın aynı örneği geri getirmesiyle sonuçlanan "Benzersiz" bir modeldir . Yani bir "argüman başına singleton". Yukarıdakiler bu kuyuya uyum sağlar ve daha da özlü hale gelir:

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

Tüm bunlar, bunlardan birine ihtiyacınız olduğunu düşünüyorsanız, muhtemelen bir anlığına durmanız ve kendinize gerçekten yapıp yapmadığınızı sormanız gerektiğine dair genel tavsiyeye katılıyorum. Zamanın% 99'u YAGNI.


1
  • Biri ama, aynı sınıfın örnekleri çoklu sayıda istiyorsa Args veya kwargs farklıdır yalnızca, birini kullanabilirsiniz bu
  • Ör.
    1. Sınıf işleme serialiletişiminiz varsa ve seri bağlantı noktasını argüman olarak göndermek istediğiniz bir örnek oluşturmak istiyorsanız, geleneksel yaklaşımla çalışmaz
    2. Yukarıda bahsedilen dekoratörler kullanılarak, bağımsız değişkenler farklıysa sınıfın birden çok örneği oluşturulabilir.
    3. Aynı argümanlar için, dekoratör zaten oluşturulmuş olan aynı örneği döndürür.
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>

0

Tolli'nin cevabına dayanan kod .

#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)

Açıklama:

  1. Verilen sınıftan devralınan yeni sınıf oluşturun cls
    ( clsörneğin, birisinin istemesi durumunda değişmez singleton(list))

  2. Örnek oluştur. Geçersiz kılmadan önce __new__çok kolay.

  3. Şimdi, kolayca örnek oluşturduğumuzda, __new__an önce tanımlanmış yöntemi kullanarak geçersiz kılar .
  4. İşlev instanceyalnızca arayanın beklediği zaman döner , aksi takdirde yükselir TypeError.
    Birisi dekore edilmiş sınıftan miras almaya çalıştığında koşul karşılanmaz.

  5. Eğer __new__()döner bir örneği cls, daha sonra yeni örneğinin __init__()yöntemi çağrılır gibi __init__(self[, ...])kendini yeni örneği olduğu ve kalan argümanlar geçirildi aynıdır __new__().

    instancezaten başlatılmış olduğundan işlev __init__hiçbir şey yapmadan işlevle değiştirilir .

Çevrimiçi çalıştığını görün


0

Fab'ın cevabına biraz benziyor ama tam olarak aynı değil.

Singleton sözleşme biz kurucu birden fazla kez çağırmak mümkün olmasını gerektirmez. Bir singleton sadece bir kez yaratılacağı için, sadece bir kez yaratıldığı görülmemeli midir? "Kimlik sahtekarlığı" kurucu okunabilirliği tartışmalı bir şekilde bozar.

Benim önerim sadece şu:

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, kurucunun veya alanın instancekullanıcı koduna göre kullanılmasını engellemez :

if Elvis() is King.instance:

... Elvisbunun henüz yaratılmadığından ve bu yaratıldığından eminseniz King.

Ancak kullanıcıları yöntemi evrensel olarak kullanmaya teşvik ederthe :

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

Bu da geçersiz olabilir eksiksiz yapmak için __delattr__()bir girişimde silmek yapılırsa bir özel durum yükseltmek instanceve geçersiz kılmayı __del__()bir özel durum yükseltir, böylece (biz programı sona eriyor bilmedikleri sürece ...)

Diğer iyileştirmeler


Yorum ve düzenlemelere yardımcı olanlara teşekkür ediyorum. Jython'u kullanırken, bu daha genel çalışmalı ve iş parçacığı açısından güvenli olmalıdır.

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()

Dikkat edilecek noktalar:

  1. Python2.x dosyasındaki nesneden alt sınıf almazsanız, kullanmayan eski bir sınıf alırsınız __new__
  2. Dekorasyon yaparken __new__@classmethod ile süslemeniz gerekir veya __new__ilişkisiz bir örnek yöntemi olacaktır.
  3. Bu muhtemelen bir metasınıfın kullanımı yoluyla geliştirilebilir, çünkü bu, thesınıf düzeyinde bir özellik oluşturmanıza izin verir , muhtemeleninstance

Bu singleton deseninin biraz farklı bir yorumu olsa da, hala geçerli olduğuna eminim, ancak sadece sınıf nitelikleri üzerinde hareket ettiği için __new__ kullanmak yerine cazip gelebilirim __init__ve bu da kısaca ikinci bir örnek olmasını önler. Bu ve yöntem 2 arasındaki fark, birden çok kez başlatmaya çalışmanın tek örneği döndürüp döndürmemesi veya bir istisna oluşturup oluşturmadığıdır. Sanırım ya tekil kalıbı tatmin etmekten memnunum, birinin kullanımı daha kolay, diğeri ise tekli bir açıktır.
theheadofabroom

Açıkçası sınıf adının __init__alt sınıfta kullanılmasını önler, ancak bu işleri kolaylaştırır, ancak gerekli değildir
theheadofabroom

Teşekkürler ... ah evet, İstisna atılmadan önceki anlık ikinci örnek. Ben değiştirdiniz __init__umarım bu subclassable olmalı ki ...
mike kemirgen

Harika, themuhtemelen benzer nedenlerden dolayı bir sınıf yöntemi olmaktan yararlanabilir
theheadofabroom

Evet haklısın. Sonra bir SuperElvis alt sınıf singletonuna ve (örneğin) bir ImaginaryElvis alt sınıf singletonuna sahip olabilirsiniz ... ve bunlar birlikte var olabilir. Ek düşünceleri görün. Lütfen kodumu geliştirmek için çekinmeyin.
mike kemirgen

0

Bir astar (Gurur duymuyorum, ama işi yapıyor):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

Sınıfınız başka bir modüle alınmadığı sürece bu işi yapabilir ...
Aran-Fey

Doğru. Sınıfı başlatma maliyetini ödeyebiliyorsanız, sınıf tanımından hemen sonra bunu önlemek için bir Sınıfım () çalıştırabilirsiniz.
polvoazul

0

Singleton örneğinin tembel başlatılmasına ihtiyacınız yoksa, aşağıdakiler kolay ve iş parçacığı açısından güvenli olmalıdır:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

Bu şekilde Amodül içe aktarmada başlatılan tek bir ton.


0

Belki tekil kalıbı yanlış anlıyorum ama benim çözümüm bu basit ve pragmatik (pitonik?). Bu kod iki hedefi tamamlar

  1. Örneğini yapın FooErişilebilirlik her yerde erişilebilir hale getirin (global).
  2. Yalnızca bir örneği Foobulunabilir.

Kod budur.

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

Çıktı

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!

0

Bir süre bununla uğraştıktan sonra nihayetinde aşağıdakileri buldum, böylece yapılandırma nesnesi ayrı modüller çağrıldığında yalnızca bir kez yüklenecekti. Metasınıf, şu anda uygun bir programı küresel olarak saklamanın en güzel yolu gibi görünen yerleşik bir dikte içinde küresel bir sınıf örneğinin depolanmasına izin verir.

import builtins

# -----------------------------------------------------------------------------
# So..... you would expect that a class would be "global" in scope, however
#   when different modules use this,
#   EACH ONE effectively has its own class namespace.  
#   In order to get around this, we use a metaclass to intercept
#   "new" and provide the "truly global metaclass instance" if it already exists

class MetaConfig(type):
    def __new__(cls, name, bases, dct):
        try:
            class_inst = builtins.CONFIG_singleton

        except AttributeError:
            class_inst = super().__new__(cls, name, bases, dct)
            builtins.CONFIG_singleton = class_inst
            class_inst.do_load()

        return class_inst

# -----------------------------------------------------------------------------

class Config(metaclass=MetaConfig):

    config_attr = None

    @classmethod
    def do_load(cls):
        ...<load-cfg-from-file>...

-1

Bu çözümü nerede bulduğumu hatırlamıyorum, ancak Python uzmanı olmayan bakış açımdan en 'zarif' olduğunu düşünüyorum:

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

Bunu neden sevdim? Dekoratörler yok, meta sınıfları yok, çoklu kalıtım yok ... ve artık Singleton olmasını istemediğinize karar verirseniz, __new__yöntemi silin . Python'da (ve genel olarak OOP'da) yeni olduğumdan, birinin neden bu korkunç bir yaklaşım olduğu konusunda beni düzleştireceğini umuyorum?


2
neden bu korkunç bir yaklaşım? başka bir singleton sınıfı oluşturmak istediğinizde __new__,. Kendinizi tekrarlamayın .
GingerPlusPlus

Ayrıca, neden yeni sürer *argsve **kwargs, sonra onlarla hiçbir şey yapmaz? Bunları iletin dict.__new__bu şekilde: dict.__new__(cls, *args, **kwargs).
GingerPlusPlus

Bu __init__, sınıf her çağrıldığında yöntemi çağırır . Senin Eğer __init__yöntem aslında bir şey yaptı, sorunu fark ediyorum. Ne zaman yaparsanız yapın SomeSingleton(), singleton'unuzun durumu __init__yöntemle sıfırlanır .
Aran-Fey

-2

Tektonları uygulamak için tercih ettiğim yol:

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
Sınıfın birden fazla örneğinin var olmasına izin verdiği için bu kesinlikle tekil bir öğe değildir. Bir gelişme, sınıfın başlatılamamasını sağlamak ve tüm sınıf yöntemlerinin sınıf öznitelikleri üzerinde hareket etmesini sağlamaktır
theheadofabroom

-2

Bu cevap muhtemelen aradığınız şey değil. Karşılaştırma için sadece o nesnenin kimliğine sahip olması anlamında bir singleton istedim. Benim durumumda bir Sentinel Değeri . Cevabın çok basit olduğu, herhangi bir nesneyi yapın mything = object()ve python'un doğası gereği, sadece o şeyin kimliği olacaktır.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

Modüllerin aslında birçok kez içe aktarılabileceğini öğrendim, bu durumda bu sadece yerel bir singleton, ki bu gerçekten herhangi bir kapasitede singleton değil.
ThorSummoner

Bir modülün birden çok kez nasıl içe aktarılabileceğini ayrıntılı olarak açıklayabilir misiniz? Gördüğüm tek zaman, modül yüklenirken bir istisna oluştuğunda, kullanıcı modülü daha sonra yükleyebilir, ancak yan etkiler zaten gerçekleşmiş olabilir, bu nedenle bazı eylemler ikinci kez yürütülebilir.
sleblanc

Bir modül tamamen yüklendikten sonra, bu modülü tekrar kullanmanın bir yolunu göremiyorum, başka bir yorumlayıcıyı kullanarak evalveya kullanmaya zorlamaktan başka importlib.reload.
sleblanc

-3

Bu çözüm, modül düzeyinde bazı ad alanı kirliliğine neden olur (sadece bir değil üç tanım), ancak takip etmeyi kolay buluyorum.

Böyle bir şey yazabilmek istiyorum (tembel başlatma), ancak ne yazık ki sınıflar kendi tanımlarının gövdesinde mevcut değil.

# 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

Bu mümkün olmadığından, başlatma işlemini ve statik örneği

İstekli Başlatma:

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

Tembel başlatma:

İstekli Başlatma:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.