Soru Singletonları tanımlamanın basit ve zarif bir yolu var mı? [çift]


Bu sorunun zaten bir cevabı var:

Tanımlamanın birçok yolu var gibi görünüyor singletonların Python'da. Yığın Taşması konusunda bir fikir birliği var mı?


372
2017-08-28 09:03


Menşei


Hata: 52. satır - Mutabakat bulunamadı. - Homer6
Singletonlar Patolojik Yalanlardeğil mi? - Jonas Byström
“Bu soru, Soru-Cevap formatımıza uygun değil” - Bence bu, öznel bir soru değil, SO Q & A formatına uyması için böyle soruları sormanın bir yolu var mı? - binithb
Soru çok popüler olduğu için kapatıldı ... - orokusaki
Bunun yapıcı olmadığını kabul etmiyorum. Taşınırsa yeniden açılabilir programmers.stackexchange.com ? - stackoverflowwww


Cevaplar:


İhtiyacı görmüyorum, fonksiyonlara sahip bir modül (ve bir sınıf değil) tekil olarak iyi bir hizmet olacaktır. Tüm değişkenleri yine de tekrar tekrar başlatılamadı modülüne bağlı olurdu.

Bir sınıf kullanmak isterseniz, Python'da özel sınıflar veya özel kurucular oluşturmanın bir yolu yoktur, bu yüzden API'nizi kullanımınızdaki sözleşmelerden farklı olarak birden fazla örneğe karşı korunamazsınız. Hala bir modülde yöntemler koyarım ve modülü singleton olarak düşünürdüm.


307
2017-08-28 09:10



Oluşturucu, bir örneğin zaten oluşturulmuş olup olmadığını kontrol edip, bir istisna atar mıydı? - Casebash
Bu, tasarımınızın bir parçası olarak kalıtım kullanmanız gerekmediği sürece iyidir, bu durumda aşağıdaki yanıtların çoğu daha uygundur - Jim Jeffries
Döngüsel içe aktarımınız olduğunda bozuldu - dieend
Bu modülün miras alınmasını istiyorsam ne yapacağım? - yossi
Bu benim görüşüme göre yanlış. Modül seviyesi arayüzleri hakkında bir sıkıntı ithalatı yönetmektir. Örneğin, Python logging bir modül seviyesi arabirimidir. Sonra tamamen temizlemenizi sağlamak için logging aramalısın logging.shutdown(). Bu, içe aktarmanız gerektiği anlamına gelir logging hangi modüllere çağırır shutdown. Tekil bir desen olsaydı, örnek üzerinde geçirildiği herhangi bir modülde kapatma yapmak mümkündür. - Matt


İşte bende tekil bir uygulamam var. Tek yapman gereken dersi süslemek; singleton almak için, o zaman kullanmak zorunda Instance yöntem. İş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. Also, the decorated class cannot be
    inherited from. 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.

    """

    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)

270
2017-09-08 09:46



Python pil dahil olmak üzere bu bir parçası olmalıdır desing_pattern standart kütüphane, teşekkürler - dashesy
Bu dekoratör, yapıcıları argümanlarla desteklemez. Daha genel amaçlı bir dekoratör için şunları kullanın: def Instance(self, *args, **kwargs): self._instance = self._decorated(*args, **kwargs) - akhan
@akhan Yapımcıları amaç doğrultusunda argümanlarla desteklememeye karar verdim çünkü argümanlar sadece ilk kez kullanıldı ve diğer tüm zamanları görmezden gelecekti. Bu, kodunuzu takip etmeyi çok zorlaştırabilir, çünkü farklı yerlerde farklı argümanlar kullanabilirsiniz, ancak bu çağrılardan hangisinin aslında singleton'u başladığını bilmiyor olabilirsiniz. - Paul Manta
@akhan Tektonunuzu argümanlarla gerçekten başlatmak istiyorsanız, ayrı bir initialize() Herhangi bir argüman alabilir ve birden fazla çağrılırsa atar. - Paul Manta
Bu bir Gerçekten kötü tekil uygulama. Her şeyden önce, uygun bir dekoratör değil çünkü kullanmıyor functools.wraps veya functools.update_wrapper. İkincisi, örneği arayarak almak zorundayım Foo.Instance() korkunç derecede ürkütücü ve tam olarak neden uygulanamadığı 0 nedeni var. Foo() yerine. Üçüncüsü, bu sınıfın yerine geçmesi gibi beklenmedik sonuçlar doğurur type(Foo.instance()) is Foo -> False - Aran-Fey


Bunu geçersiz kılabilirsiniz. __new__ böyle bir yöntem:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                                cls, *args, **kwargs)
        return cls._instance


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    if (id(s1) == id(s2)):
        print "Same"
    else:
        print "Different"

175
2017-11-27 19:37



UYARı: __new __ (), cls örneğini döndürürse, yeni örneğin __init __ () yöntemi, __init __ (self [, ...]) gibi çağrılır, burada kendiliğinden yeni örnek ve kalan argümanlar, aynıdır. __new __ () öğesine geçti. Singleton'un herhangi bir alt sınıfı, __init __ () öğesini uygularsa, aynı benimle birden çok kez çağrılır. Bunun yerine bir fabrika kullanarak bitirdim. - alsuren
buradaki cevap olarak bir metaclass kullanarak daha iyi olurdu: stackoverflow.com/a/33201/804147 - underrun
Bu aşağıdaki uyarıyı verir - singleton.py:9: DeprecationWarning: object.__new__() takes no parameters  cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) - Siddhant
@Siddhant: Daha da kötüsü, Python 3'te, bu uyarı bir hata olur. Görmek bugs.python.org/issue1683368 ve blog.jaraco.com/2014/05/... daha fazla ayrıntı için. - Jason R. Coombs


Python'daki singletonu uygulamak için biraz farklı bir yaklaşım borg kalıbı Alex Martelli (Google çalışanı ve Python dehası).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

Yani tüm örnekleri aynı kimliğe sahip olmaya zorlamak yerine, devleti paylaşırlar.


101
2017-08-28 14:53



Monostat olarak da bilinir. Bekarlığa göre daha kötüler. - Tom Hawtin - tackline
Yeni stil sınıflarıyla çalışmaz - James Emerton
Bunun neden yeni tarz sınıflarla çalışmadığını açıklayabilecek biri var mı? - Stephen Emslie
@JamesEmerton: Sadece Python 2.7.2 üzerinde çalıştım, yeni stil sınıflarıyla iyi çalışıyor. - voithos
@pylover: Haklısın, bu bir Singleton değil - bu muhtemelen sebebin bir parçası Alex Martelli farklı bir isim verdi - ama etkileri çok benzer. - martineau


Modül yaklaşımı iyi çalışıyor. Eğer bir singletona ihtiyacım olursa Metaclass yaklaşımını tercih ederim.

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None 

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton

74
2017-08-28 19:39



Bu model "Tek Sorumluluk İlkesi" ne karşıdır (c2.com/cgi/wiki?SingleResponsibilityPrinciple). İçindeki noktayı (2) gör blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx. - haridsv
@haridsv Katılıyorum. Sınıfın tek kişilik olduğu gerçeği olduğu metaclass uygulamasında soyutlanmış olan - sınıfın kendisi, bu ihtiyacı yerine getirmekle yükümlü olduğu için bir tek kişilik olduğunu bilmez ya da önemsemez. Ancak, aşağıdaki yöntem açıkça belirtildiği gibi bir ihlaldir. Temel sınıf yöntemi, aralarında bir yerdedir. - agf
bununla ilgili problem, birden çok örnek oluşturabilmenizdir. MyClass vasitasiyla deepcopy. Eğer koşarsan from copy import deepcopy as dcp ; m1 = MyClass() ; m2 = dcp(m1) ; print m1 is m2, alacaksınız False. Etrafında herhangi bir yol biliyor musun?
@ dare2be: Bahsettiğiniz kopyalama sorunu, sadece meta sınıfın da eklenmesiyle giderilemedi. __deepcopy__() oluşturulan sınıfa yöntem mi? - martineau
@martineau: Bu type.__init__ o geçersiz, değil MyClass.__init__ - Eric


Şu uygulamaya bakın: PEP318tekli deseni bir dekoratörle uygulamak:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

40
2018-05-02 02:47



Bu dekoratörle ilgili sorun 'Sınıfım' artık bir sınıf değil, ör. super () işe yaramaz, classmethods işe yaramaz vs: @singleton sınıfı MyClass (BaseClass): def __init __ (self): süper (MyClass, self) .__ init __ () - ithkuil
Dekoratörün yeni Miras sorunu ile başa çıkmak için sınıftan ziyade yöntem. Hangi noktada, dekoratörün zarif okunabilirliği azalır. Ya da dekoratör, dekorasyon yapmak için kullandığı sınıf ile fritz ihtiyacı yeni işlev mantıklı bir şey yapmak. - F1Rumors


Olarak kabul edilen cevap diyor ki, en aptalca yol sadece bir modül kullan.

Bunu düşünerek, işte bir konsept kanıtı:

def singleton(cls):
    obj = cls()
    # Always return the same object
    cls.__new__ = staticmethod(lambda cls: obj)
    # Disable __init__
    try:
        del cls.__init__
    except AttributeError:
        pass
    return cls

Bakın Python veri modeli hakkında daha fazla bilgi için __new__.

Örnek:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

Notlar:

  1. Yeni stil sınıflarını kullanmanız gerekir ( object) bunun için.

  2. Tekil, kullanıldığı ilk kez değil, tanımlandığında başlatılır.

  3. Bu sadece bir oyuncak örneğidir. Bunu aslında üretim kodunda hiç kullanmadım ve planlamıyorum.


25
2017-10-12 00:13



Bunu denedim ama hatayı aldım: TypeError: unbound method <lambda> () ilk argüman olarak Integer örneğiyle çağrılmalıdır (bunun yerine type örneği var) My Integer sınıfı sizin Duck sınıfınızdır: @singleton sınıfı Tamsayı (object): "" "Tamsayı türü" "" nesnelerinin sınıfı - Tom Prats
Bunu işaret ettiğin için teşekkürler. Bunun neden olduğu hakkında hiçbir fikrim yok, fakat düzenlenen sürüm Python 2.7 ve 3.3 üzerinde çalışmalı. - Lambda Fairy
Bu iyi değil, __init__() Sınıf tanımlandığında (ilk kez kullanılıncaya kadar beklemek isteyebilirsiniz) ve daha sonra her çağrıda yöntem çağrılıyor Duck(). - tiho
İlk sayımı belgeledim ve ikincisi düzeltdim. İşaret ettiğin için teşekkürler. - Lambda Fairy


Python'da bir singleton yazdığım bir keresinde, tüm üye fonksiyonların classmethod dekoratörüne sahip olduğu bir sınıf kullandım.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y

14
2017-08-29 19:15



Bu yaklaşımı seviyorum, ama küçük bir yaka var. En azından Python 2.6 ile, __len__ veya __getitem__ classmethods olarak çalışın, böylece bir nesnede olduğu gibi özelleştirmek için esnekliğe sahip değilsiniz. Sık sık bir Singleton'u veri koleksiyonu olarak kullanmak istediğimden, bu biraz hayal kırıklığı yaratıyor. - Dan Homerick
Bana öyle geliyor ki, bu bir demet şeylerin bir isim alanına sarılmasından başka bir şey değil ... bununla ilgili yanlış bir şey yok, bazılarının bile harika bir fikir olduğunu düşündüklerini söylediler (import this) - bu yaklaşımın, basitçe kötü bir mühendislik uygulaması olarak kabul edilen global değişkenleri kullanmaktan çok uzak ve çok daha yakın görünmesidir. - martineau
@martineau Tek birton kullanmanın, nasıl uygulandığı önemli değil, global değişkenleri kullanmaya çok yakın olduğunu öneriyorum. - David Locke
Tektonlar, küresel değişkenlerden iki şekilde daha iyidir: Küresel isim alanını hiç kirletmezler (ya da cevabınız gibi) ve aynı zamanda tembel değerlendirme sağlarlar, oysa küresel değişkenler genellikle yoktur (ve sizin cevabınız da değildir). ). - martineau
@DanHomerick for __len__, __getitem__ ve hatta @property kullanabilirsiniz __metaclass__ Yukarıdakileri tanımlayan bir sınıfa ayarlanır. Harika çalış. Tek sınıf olarak bir sınıfa oy veriyorum, bu dilin tasarımına göre, onun bir metaclass örneğidir. Aslında, tüm yöntemler meta sınıfta tanımlanabilir ve sonra sınıf, tekil bir referans olarak kullanılacaktır. - Leonid Usov


Bu konuda çok emin değilim, ama projemde 'kongre bekarları' (zorunlu tekil değil) kullanılıyor, yani eğer bir dersim varsa DataControllerBunu aynı modülde tanımlarım:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller

Tam altı satır olduğu için zarif değil. Ama bütün tekliklerim bu modeli kullanıyor ve en azından çok açık (pythonic olan).


12
2017-08-22 00:44



+1 Python'da her şey kongre ile ilgili olmalıdır (çünkü genellikle zorla sınırları aşabilir). Şahsen, örneğe erişmek ve saklamak için bir classmethod ve class değişkenini tercih ederim, dolayısıyla kullanmak zorunda değilsiniz. global. (Genel olarak kullanımdan vazgeçiriyorum. global Ancak bu, kabul edilebilir birkaç kullanım örneğinden biridir.) - schlamar


Python belgeleri bunu kapsamaktadır:

class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass

Muhtemelen bunun gibi görünmesi için yeniden yazabilirim:

class Singleton(object):
    """Use to create a singleton"""
    def __new__(cls, *args, **kwds):
        """
        >>> s = Singleton()
        >>> p = Singleton()
        >>> id(s) == id(p)
        True
        """
        self = "__self__"
        if not hasattr(cls, self):
            instance = object.__new__(cls)
            instance.init(*args, **kwds)
            setattr(cls, self, instance)
        return getattr(cls, self)

    def init(self, *args, **kwds):
        pass

Bunu uzatmak için nispeten temiz olmalıdır:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...

11
2017-07-17 06:38



Guido van Rossum’un uygulanmasından söz eden tek kişi olmak için +1. Ancak kendi versiyonunuz yanlış: kullanmamalısınız hasattr ve getattr içeride __new__ ikisi de aradıkları gibi object.__getattribute__ hangi sırayla görünüyor "__self__" Sadece geçerli sınıf yerine tüm sınıf hiyerarşisi ile öznitelik. Guido kullanırsa __dict__ bir sebepten dolayı özellik erişimi için. Deneyin: class A(GuidoSingleton): pass, class B(A): pass, class C(YourSingleton): pass, class D(C): pass, print(A(), B(), C(), D()). Tüm alt sınıflar aynı YourSingleton! - Maggyero