Tworzenie singletona w Pythonie

945

To pytanie nie dotyczy dyskusji, czy pożądany jest wzór singletonu , jest anty-wzorem, czy jakiejkolwiek wojny religijnej, ale do omówienia, w jaki sposób ten wzór najlepiej wdrożyć w Pythonie w najbardziej pythonowy sposób. W tym przypadku definiuję słowo „najbardziej python”, co oznacza, że ​​jest ono zgodne z „zasadą najmniejszego zdziwienia” .

Mam wiele klas, które stałyby się singletonami (mój przypadek użycia dotyczy rejestratora, ale to nie jest ważne). Nie chcę zaśmiecać kilku klas z dodanym gumph, kiedy mogę po prostu odziedziczyć lub ozdobić.

Najlepsze metody:


Metoda 1: Dekorator

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

Plusy

  • Dekoratorzy są addytywni w sposób, który jest często bardziej intuicyjny niż wielokrotne dziedziczenie.

Cons

  • Podczas gdy obiekty utworzone za pomocą MyClass () byłyby prawdziwymi obiektami singletonowymi, sam MyClass jest funkcją, a nie klasą, więc nie można wywoływać z niej metod klas. Również m = MyClass(); n = MyClass(); o = type(n)();wtedym == n && m != o && n != o

Metoda 2: Klasa podstawowa

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

Plusy

  • To prawdziwa klasa

Cons

  • Wielokrotne dziedziczenie - eugh! __new__można zastąpić podczas dziedziczenia z drugiej klasy podstawowej? Trzeba myśleć więcej niż to konieczne.

Metoda 3: Metaklasa

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

Plusy

  • To prawdziwa klasa
  • Auto-magicznie obejmuje dziedziczenie
  • Używa __metaclass__do właściwego celu (i uświadomił mi o tym)

Cons

  • Czy są jakieś?

Metoda 4: Dekorator zwraca klasę o tej samej nazwie

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

Plusy

  • To prawdziwa klasa
  • Auto-magicznie obejmuje dziedziczenie

Cons

  • Czy nie ma narzutów związanych z tworzeniem każdej nowej klasy? Tutaj tworzymy dwie klasy dla każdej klasy, którą chcemy stworzyć singleton. Chociaż w moim przypadku jest to w porządku, martwię się, że to może się nie skalować. Oczywiście jest kwestia debaty, czy skalowanie tego wzorca nie jest zbyt łatwe ...
  • Jaki jest sens tego _sealedatrybutu
  • Nie można wywoływać metod o tej samej nazwie w klasach podstawowych, super()ponieważ będą się powtarzać. Oznacza to, że nie możesz dostosowywać __new__i nie możesz podklasować klasy, do której musisz zadzwonić __init__.

Metoda 5: moduł

plik modułu singleton.py

Plusy

  • Prosty jest lepszy niż złożony

Cons

theheadofabroom
źródło
12
Kolejne trzy techniki: zamiast tego użyj modułu (często - ogólnie myślę - jest to bardziej odpowiedni wzorzec dla Pythona, ale zależy to nieco od tego, co z nim robisz); stwórz pojedynczą instancję i poradz sobie z nią zamiast tego ( foo.xlub jeśli nalegasz Foo.xzamiast Foo().x); użyj atrybutów klas i metod static / class ( Foo.x).
Chris Morgan
10
@ChrisMorgan: Jeśli zamierzasz używać tylko metod klasowych / statycznych, to nie zawracaj sobie głowy tworzeniem klasy.
Cat Plus Plus
2
@Cat: Efekt jest podobny, jednak powody tworzenia globalnej zmiennej mogą dotyczyć wszystkiego, w tym niewiedzy. Dlaczego tworzy się singletona? Jeśli musisz zapytać, nie powinno cię tu być. Ta jawność jest nie tylko bardziej pytoniczna, ale sprawia, że ​​konserwacja jest o wiele prostsza. Tak, singletony to cukier syntaktyczny dla globali, ale potem klasy to cukier syntaktyczny dla całej masy brzydkich rzeczy i nie sądzę, żeby ktokolwiek powiedział ci, że bez nich lepiej.
theheadofabroom
14
@BiggAl: Singletony nie są Pythonicami, bez względu na to, jak je implementujesz. W najlepszym razie są oznaką wadliwego projektu.
Cat Plus Plus
8
Nastrój anty-signletons to najgorsze programowanie kultu ładunku. To samo dotyczy osób słyszących (niewielu próbowało przeczytać) „Oświadczenie Goto uważane za szkodliwe” i uważają, że gotos są oznaką złego kodu niezależnie od kontekstu.
Hejazzman

Odpowiedzi:

658

Użyj metaklasy

Poleciłbym Metodę 2 , ale lepiej jest użyć metaklasy niż klasy podstawowej. Oto przykładowa implementacja:

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

Lub w Python3

class Logger(metaclass=Singleton):
    pass

Jeśli chcesz uruchomić za __init__każdym razem, gdy klasa jest wywoływana, dodaj

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

do ifoświadczenia wSingleton.__call__ .

Kilka słów o metaklasach. Metaklasa jest klasą klasy ; to znaczy klasa jest instancją jej metaklasy . Metaklasę obiektu można znaleźć w Pythonie za pomocą type(obj). Normalne klasy nowego stylu są typu type. Loggerw powyższym kodzie będzie typu class 'your_module.Singleton', podobnie jak (jedyna) instancja Loggerbędzie typu class 'your_module.Logger'. Podczas rozmowy telefonicznej z rejestratora Logger(), Python najpierw pyta o metaklasą Logger, Singletonco robić, umożliwiając tworzenie instancji należy uprzedzać. Ten proces jest taki sam, jak Python pytający klasę, co robić, wywołując, __getattr__gdy odwołujesz się do jednego z jego atrybutówmyclass.attribute .

Metaklasa zasadniczo decyduje, co oznacza definicja klasy i jak ją wdrożyć. Zobacz na przykład http://code.activestate.com/recipes/498149/ , który zasadniczo odtwarza style C structw Pythonie przy użyciu metaklas. Wątek Jakie są (konkretne) przypadki użycia metaklas? podaje także kilka przykładów, na ogół wydają się one być związane z programowaniem deklaratywnym, zwłaszcza w przypadku ORM.

W tej sytuacji, jeśli użyjesz metody nr 2 , a podklasa definiuje __new__metodę, będzie ona wykonywana przy każdym wywołaniu SubClassOfSingleton()- ponieważ odpowiada za wywołanie metody zwracającej przechowywaną instancję. Metaklasa zostanie wywołana tylko raz , gdy zostanie utworzona jedyna instancja. Chcesz dostosować, co to znaczy nazywać klasę , zależnie od jej typu.

Zasadniczo sensowne jest użycie metaklasy do wdrożenia singletonu. Singleton jest wyjątkowy, ponieważ jest tworzony tylko raz , a metaklasa to sposób, w jaki dostosowujesz tworzenie klasy . Korzystanie z metaklasy daje większą kontrolę na wypadek konieczności dostosowania definicji klas singletonów na inne sposoby.

Twoje singletony nie będą wymagały wielokrotnego dziedziczenia (ponieważ metaklasa nie jest klasą podstawową), ale w przypadku podklas utworzonej klasy, które używają wielokrotnego dziedziczenia, musisz upewnić się, że klasa singleton jest pierwszą / najbardziej lewą klasą z metaklasą, która redefiniuje __call__Jest to bardzo mało prawdopodobne, aby stanowił problem. Dict instancji nie znajduje się w przestrzeni nazw instancji, więc przypadkowo go nie zastąpi.

Usłyszysz również, że wzorzec singletonu narusza „zasadę pojedynczej odpowiedzialności” - każda klasa powinna zrobić tylko jedną rzecz . W ten sposób nie musisz się martwić, że zepsujesz jedną rzecz, którą robi kod, jeśli musisz zmienić inną, ponieważ są one osobne i hermetyzowane. Implementacja metaklasy przechodzi ten test . Metaklasa jest odpowiedzialna za wymuszanie wzorca, a utworzona klasa i podklasy nie muszą mieć świadomości, że są singletonami . Metoda nr 1 kończy się niepowodzeniem tego testu, jak zauważyłeś w „MyClass sama jest funkcją, a nie klasą, więc nie można wywoływać z niej metod klas”.

Wersja kompatybilna z Python 2 i 3

Pisanie czegoś, co działa zarówno w Python2, jak i 3, wymaga użycia nieco bardziej skomplikowanego schematu. Ponieważ metaklasy są zwykle podklasami typu type, możliwe jest użycie jednej z nich, aby dynamicznie utworzyć pośrednią klasę bazową w czasie wykonywania z nią jako metaklasą, a następnie użyć jej jako Singletonklasy podstawowej publicznej klasy bazowej. Trudniej to wytłumaczyć niż zrobić, jak pokazano poniżej:

# 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

Ironicznym aspektem tego podejścia jest to, że wykorzystuje podklasę do implementacji metaklasy. Jedną z możliwych zalet jest to, że w przeciwieństwie do czystej metaklasy isinstance(inst, Singleton)powróci True.

Korekty

W innym temacie zapewne już to zauważyłeś, ale implementacja klasy podstawowej w oryginalnym poście jest nieprawidłowa. _instancesnależy odwoływać się do klasy , należy jej użyć super()lub rekursuje się i __new__jest to metoda statyczna, do której należy przekazać klasę , a nie metoda klasy, ponieważ rzeczywista klasa nie została jeszcze utworzona nazywa się. Wszystkie te rzeczy będą prawdziwe także dla implementacji metaklasy.

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

Dekorator powracający klasę

Początkowo pisałem komentarz, ale był za długi, więc dodam go tutaj. Metoda nr 4 jest lepsza niż inna wersja dekoratora, ale zawiera więcej kodu niż jest to potrzebne do singletona i nie jest tak jasne, co robi.

Główne problemy wynikają z tego, że klasa jest własną klasą podstawową. Po pierwsze, czy to nie dziwne, że klasa jest podklasą niemal identycznej klasy o tej samej nazwie, która istnieje tylko w jej __class__atrybucie? Oznacza to również, że nie można określić żadnych metod, które wywołać metodę o tej samej nazwie na ich klasy podstawowej z super()ponieważ będą one recurse. Oznacza to, że twoja klasa nie może się dostosowywać __new__i nie może wywodzić się z żadnych klas, które trzeba __init__do nich wezwać.

Kiedy stosować wzór singletonu

Twój przypadek użycia jest jednym z lepszych przykładów chęci skorzystania z singletonu. Mówisz w jednym z komentarzy: „Dla mnie logowanie zawsze wydawało się naturalnym kandydatem do Singletonów”. Masz absolutną rację .

Kiedy ludzie mówią, że singletony są złe, najczęstszym powodem jest ukryty stan wspólny . Podczas gdy w przypadku zmiennych globalnych i importu modułów najwyższego poziomu jawny jest stan współdzielony, inne przekazywane obiekty są generalnie tworzone. To dobra uwaga, z dwoma wyjątkami .

Pierwszym i wspomnianym w różnych miejscach jest to, że singletony są stałe . Używanie stałych globalnych, zwłaszcza wyliczeń, jest powszechnie akceptowane i uważane za rozsądne, ponieważ bez względu na wszystko, żaden z użytkowników nie może ich zepsuć dla żadnego innego użytkownika . Dotyczy to również stałego singletonu.

Drugi wyjątek, o którym mniej się wspomina, jest odwrotny - gdy singleton jest tylko ujściem danych , a nie źródłem danych (bezpośrednio lub pośrednio). Właśnie dlatego rejestratorzy czują się jak „naturalne” zastosowanie singletonów. Ponieważ różni użytkownicy nie zmieniają rejestratorów w sposób, w jaki dbają o nich inni użytkownicy, nie ma tak naprawdę wspólnego stanu . To neguje główny argument przeciwko wzorowi singletonów i czyni je rozsądnym wyborem ze względu na łatwość użycia do zadania.

Oto cytat z http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :

Jest jeden rodzaj Singletona, który jest OK. Jest to singleton, w którym wszystkie osiągalne obiekty są niezmienne. Jeśli wszystkie obiekty są niezmienne, Singleton nie ma stanu globalnego, ponieważ wszystko jest stałe. Ale tak łatwo jest zmienić ten singleton w zmienny, że jest bardzo śliski. Dlatego też jestem przeciwny tym Singletonom, nie dlatego, że są źli, ale dlatego, że bardzo łatwo im się popsuć. (Na marginesie, wyliczenia Java są tylko tego rodzaju singletonami. Dopóki nie wprowadzisz stanu do swojego wyliczenia, wszystko jest w porządku, więc proszę nie.)

Innym rodzajem Singletonów, które są częściowo akceptowalne, są te, które nie wpływają na wykonanie kodu, nie mają „efektów ubocznych”. Logowanie jest doskonałym przykładem. Jest załadowany Singletonami i stanem globalnym. Jest to do przyjęcia (ponieważ nie zaszkodzi ci to), ponieważ twoja aplikacja nie zachowuje się inaczej, niezależnie od tego, czy dany rejestrator jest włączony, czy nie. Informacje płyną tutaj w jedną stronę: z aplikacji do rejestratora. Nawet jeśli rejestratory są stanem globalnym, ponieważ żadne informacje nie przepływają z rejestratorów do aplikacji, rejestratory są dopuszczalne. Powinieneś nadal wstrzykiwać program rejestrujący, jeśli chcesz, aby test potwierdził, że coś się loguje, ale ogólnie Loggery nie są szkodliwe, mimo że są pełne stanu.

agf
źródło
5
Nie, singletony nigdy nie są dobre. Rejestrowanie może być dobrym kandydatem do bycia globalnym (tak okropnym jak oni), ale na pewno nie singletonem.
Cat Plus Plus
11
Spójrz na googletesting.blogspot.com/2008/08/… . Jest ogólnie przeciw singletonowi (nie bez powodu), ale dobrze wyjaśnia, dlaczego niezmienne singletony i singletony bez efektów ubocznych nie mają takich samych problemów, jeśli jesteś ostrożny. Cytuję to trochę na końcu mojego postu.
agf
4
Mój problem z singletonami to głupia przesłanka „tylko jednego wystąpienia”. To i mnóstwo problemów z bezpieczeństwem nici. I ukrywanie zależności. Globale są złe, a singletony to tylko globale z większymi problemami.
Cat Plus Plus
4
@Cat Istnieją bardzo dobre zastosowania singletonów. Leniwa instancja modułów sprzętowych (szczególnie w aplikacjach jednowątkowych) jest jednym z nich (ale istnieją również bezpieczne dla wątków singletony).
Paul Manta,
3
@Alcott __new__w metaklasie ma miejsce, gdy klasa jest nowa - kiedy jest zdefiniowana, a nie kiedy instancja byłaby nowa. Wywołanie class ( MyClass()) to operacja, którą chcesz zastąpić, a nie definicja klasy. Jeśli naprawdę chcesz zrozumieć, jak działa Python, najlepszą rzeczą, jaką możesz zrobić (oprócz używania go), jest docs.python.org/reference/datamodel.html . Dobrym źródłem informacji o metaklasach jest eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example . Dobrym artykułem na temat singletonów jest seria z blogu google, który zamieściłem w tej odpowiedzi.
agf
91
class Foo(object):
     pass

some_global_variable = Foo()

Moduły są importowane tylko raz, wszystko inne jest przesadne. Nie używaj singletonów i staraj się nie używać globałów.

Cat Plus Plus
źródło
14
dlaczego powiedziałeś „Nie używaj singletonów”? Jakiegokolwiek powodu?
Alcott,
3
To nie zadziała, jeśli singleton będzie musiał być marynowany. Na podstawie s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
podanego
4
@dividebyzero: isoperator sprawdza równość wskaźnika. Byłbym raczej zaskoczony - do tego stopnia, że ​​nazwałbym go błędem - gdyby pickle.loadszwrócił odwołanie do wcześniej istniejącego obiektu, a nie odwołanie do nowo utworzonego obiektu. Zatem testowanie, czy s is s1nie mówi nic o przydatności wykorzystania modułów jako singletonów.
Jonas Kölker
1
@ JonasKölker już pickle.loads()to robi, np. Dla przypadków booli NoneType. pickle.loads(pickle.dumps(False)) is FalseplonyTrue
Dan Passaro
2
@ leo-the-manic: fair point; jednak, że to tylko efekt uboczny Pythonie interning obiektów True, Falsei None, i nie ma nic wspólnego z pupę kodu pickle.loads. Można to również bezpiecznie robić tylko w przypadku obiektów tylko do odczytu. Gdyby pickle.loadszwrócić odwołanie do już istniejącego modyfikowalnego obiektu - takiego jak moduł - byłby to błąd. (I tak twierdzę, że przykład kodu dividebyzero niczego nie dowodzi.)
Jonas Kölker
69

Użyj modułu. Jest importowany tylko raz. Zdefiniuj w nim niektóre zmienne globalne - będą to „atrybuty” singletona. Dodaj kilka funkcji - „metody” singletona.

warvariuc
źródło
11
Więc kończysz na ... Nie na zajęciach. Nie możesz używać go jako klasy, nie możesz opierać na nim innych klas, używasz składni importu i nagle tracisz wszystkie zalety OOP ...
theheadofabroom
16
jeśli możesz oprzeć na nim inne klasy, to może nie być singletonem. możesz stworzyć jedną z klas pochodnych, ale także klasę podstawową, ale klasa pochodna jest również członkiem bazy i masz dwie z bazy, której z nich powinieneś użyć?
SingleNegationElimination
To nie działa między modułami. W moim „głównym” module ustawiłem wartość. Następnie odwołuję się do niego w innym module i jego wartość zerowa. Westchnienie.
Paul Kenjora
1
@PaulKenjora Musisz mieć błąd w kodzie. Jeśli zdefiniujesz zmienną globalną w module, dostęp do niej z innego modułu powinien mieć wartość.
warvariuc
Ma wartość, ale kiedy ją zmieniam, nie jest zachowywana. Skończyło się na użyciu klasy z właściwościami w module, który działa. Proste typy, ponieważ globale nie działały dla mnie (straciły wartości, gdy tylko zmieniono zakres).
Paul Kenjora,
29

Prawdopodobnie nigdy nie potrzebujesz singletona w Pythonie. Wystarczy zdefiniować wszystkie dane i funkcje w module, aby uzyskać de facto singleton.

Jeśli naprawdę musisz mieć lekcję singletona, wybrałbym:

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

my_singleton = My_Singleton()

Używać:

from mysingleton import my_singleton
my_singleton.foo()

gdzie mysingleton.py jest nazwą pliku, w którym zdefiniowano My_Singleton. Działa to, ponieważ po pierwszym zaimportowaniu pliku Python nie uruchamia ponownie kodu.

Alan Dyke
źródło
4
W większości prawda, ale czasem to nie wystarczy. Np. Mam projekt z koniecznością rejestrowania instancji wielu klas na poziomie DEBUG. Potrzebuję opcji wiersza poleceń przeanalizowanych podczas uruchamiania, aby ustawić określony przez użytkownika poziom rejestrowania przed utworzeniem instancji tych klas. Instancje na poziomie modułu sprawiają, że to problem. Możliwe, że mógłbym dokładnie skonstruować aplikację, aby wszystkie te klasy nie zostały zaimportowane, dopóki przetwarzanie CLI nie zostanie zakończone, ale naturalna struktura mojej aplikacji jest ważniejsza niż dogmatyczne przestrzeganie „singletonów są złe”, ponieważ mogą być zrobione całkiem czysto.
CryingCyclops
jeśli miałbyś przetestować swój kod podczas łatania my_singleton, czy byłoby to możliwe? ponieważ ten my_singleton może zostać utworzony w innym module.
Naveen,
@Naveen - my_singleton to pojedynczy obiekt. Jeśli go „załatasz”, zmiana ta wpłynie na wszystkie przyszłe odniesienia, nawet w innych modułach.
Alan Dyke
16

Oto jedna linijka dla Ciebie:

singleton = lambda c: c()

Oto jak go używasz:

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

assert wat.get_x() == 1

Twój obiekt jest chętnie tworzony. To może być lub nie być to, czego chcesz.

Jonas Kölker
źródło
5
-1. To jest bardzo złe i głupie. Nie definiujesz klasy, która ma być używana jako obiekt. Nie możesz już uzyskać dostępu do klasy, nie będąc bardzo brzydkim jak type(wat)lub wat.__class__. Jeśli naprawdę chcesz to osiągnąć, lepiej zdefiniuj klasę i od razu ją utwórz, nie musisz się martwić dekoratorem.
0xc0de
2
Dlaczego musisz wiedzieć, jak korzystać z klasy singletonu? Wystarczy użyć obiektu singleton ..
Tolli
1
To nie jest wzorzec singletonu , więc funkcja IMO powinna mieć inną nazwę.
GingerPlusPlus
8
Wikipedia: „wzorzec singletonu to wzorzec projektowy, który ogranicza tworzenie instancji klasy do jednego obiektu”. Powiedziałbym, że moje rozwiązanie właśnie to robi. Ok, chyba można to zrobić wat2 = type(wat)(), ale to jest python, wszyscy zgadzamy się na dorosłych i tak dalej. Nie możesz zagwarantować , że będzie tylko jedna instancja, ale możesz zagwarantować, że jeśli ludzie zrobią drugą, będzie wyglądać brzydko i - jeśli są przyzwoitymi, uczciwymi ludźmi - jak znak ostrzegawczy. czego mi brakuje?
Jonas Kölker,
7

Sprawdź pytanie Przepełnienie stosu Czy istnieje prosty, elegancki sposób definiowania singletonów w Pythonie? z kilkoma rozwiązaniami.

Zdecydowanie polecam oglądać rozmowy Alexa Martellego na temat wzorców projektowych w pythonie: część 1 i część 2 . W szczególności w części 1 mówi o singletonach / obiektach stanu wspólnego.

Anton
źródło
2
Chociaż tak naprawdę nie jest to odpowiedź na moje pytanie, zasoby, na które wskazujesz, są bardzo przydatne. Niechętnie daję ci +1
theheadofabroom
3

Oto moja własna implementacja singletonów. Wszystko, co musisz zrobić, to udekorować klasę; aby uzyskać singleton, musisz użyć Instancemetody. Oto przykład:

   @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

A oto 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)
Paul Manta
źródło
2
To nie jest prawda singleton. SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())powinien drukować Truew prawdziwym singletonie; z nadrukami koduFalse
GingerPlusPlus
@GingerPlusPlus Miałem świadomość kilku ograniczeń, ale nie tego, o którym wspomniałeś. Dzięki, że o tym wspomniałeś. Niestety nie mam w tej chwili czasu, aby zastanowić się nad rozwiązaniem tego problemu.
Paul Manta
2

Metoda 3 wydaje się być bardzo fajna, ale jeśli chcesz, aby twój program działał zarówno w Pythonie 2, jak i Pythonie 3 , nie działa. Nawet ochrona osobnych wariantów testami wersji Python kończy się niepowodzeniem, ponieważ wersja Python 3 daje błąd składniowy w Python 2.

Podziękowania dla Mike'a Watkinsa: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Jeśli chcesz, aby program działał zarówno w Pythonie 2, jak i Pythonie 3, musisz zrobić coś takiego:

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

Zakładam, że „obiekt” w przydziale musi zostać zastąpiony przez „BaseClass”, ale nie próbowałem tego (próbowałem kodu, jak pokazano).

Tim
źródło
z pewnością nie jest to metaklasa - w python3, aby użyć metaklasy do zbudowania MyClass, zrobiłbyśclass MyClass(metaclass=Singleton)
theheadofabroom
Mikewatkins.ca link jest (w praktyce) złamany.
Peter Mortensen
1

Cóż, poza tym, że zgadzasz się z ogólną sugestią Pythona dotyczącą posiadania globalnego poziomu na poziomie modułu, co powiesz na to:

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)

Dane wyjściowe to:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance
Strzec
źródło
Jaki jest sens tego _sealedatrybutu? O ile widzę, to nic nie robi? Coś mnie dręczy w tym, co mówi, że nie powinno działać dobrze ... W tym tygodniu przeprowadzę kilka testów porównawczych.
theheadofabroom
_sealed zapewnia, że ​​twój init jest uruchamiany tylko raz; Nie widzę sensu, dlaczego miałaby działać gorzej niż zwykły dekorator podobny do funkcji - funkcja jest wykonywana tylko raz na klasę i zwraca nową odziedziczoną klasę
Straż
BTW, twoja edycja zawiera zakładki, które łamią wcięcia Mówisz również „tworzymy 2 klasy” - czy masz na myśli, że tworzymy „1 dodatkową klasę”?
Strzeż
Tak, miałem na myśli jedną dodatkową klasę. Zamierzam dołączyć elementy, __init__które będą wywoływane za każdym razem, gdy zostaną zainicjowane. Po prostu „Inicjalizacja w klasie. Metoda”. co do wcięcia - używałeś tabulatorów i spacji - naprawiłem większość z nich, ale wydaje się, że przegapiłem jedną, jeśli chcesz ją uzyskać (po prostu sprawdź dziennik edycji)
theheadofabroom
re init : oczywiście to zależy od ciebie, właśnie próbowałem naśladować zachowanie singletonu w innych językach, w których kod konstruktora (który nie jest dokładnie init , ale bardzo zbliżony w swoim znaczeniu) jest wywoływany tylko raz, jeśli chcesz, aby init był wywoływana za każdym razem, po prostu zabij wszystkie odwołania do _sealed re spaces / tabs - cóż, wtedy mój emacs wymaga naprawy. w każdym razie powyżej znajduje się poprawiona wersja
Straż
1

Co powiesz na to:

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

Użyj go jako dekoratora w klasie, która powinna być singletonem. Lubię to:

@singleton
class MySingleton:
    #....

Jest to podobne do singleton = lambda c: c()dekoratora w innej odpowiedzi. Podobnie jak inne rozwiązanie, jedyna instancja ma nazwę class ( MySingleton). Jednak dzięki temu rozwiązaniu możesz nadal „tworzyć” instancje (faktycznie uzyskać jedyną instancję) z klasy, wykonując to MySingleton(). Zapobiega to również tworzeniu dodatkowych instancji przez wykonanie type(MySingleton)()(co również zwraca tę samą instancję).

Tolli
źródło
Nie definiujesz klasy, aby używać jej jako obiektu.
0xc0de
1
Za każdym razem, gdy dzwonisz type(MySingleton)(), MySingleton.__init__()zostaje wywołany, a obiekt zostaje zainicjowany wiele razy; możesz to naprawić pisząc cls.__init__ = lambda self: passw swoim singleton. Również nadpisywanie cls.__call__wydaje się bezcelowe, a nawet szkodliwe - __call__zdefiniowane w tym kontekście jest używane podczas połączenia MySingleton(any, list, of, arguments), a nie podczas połączenia type(MySingleton)(any, list, of, arguments).
GingerPlusPlus
@ GingerPlusPlus, Dziękujemy za zwrócenie uwagi, że __init__()zostanie to ponownie wywołane podczas działania type(MySingleton)(). Rozwiązanie, które proponujesz (dodajesz cls.__init__ = lambda self: pass), powoduje błąd składniowy, ponieważ ostatnia część wyrażenia lambda musi być wyrażeniem, a nie wyrażeniem. Jednak dodawanie cls.__init__ = lambda self: Nonedziała, więc dodałem to do mojej odpowiedzi.
Tolli,
1
@GingerPlusPlus, w odniesieniu do użytkowania __call__. moim zamiarem było zrobienie obu type(MySingleton)()i MySingleton()zwrócenie instancji. Więc robi to, co chciałem. Możesz myśleć o MySingleton jako o typie singletonu lub instancji singletonu (lub obu).
Tolli,
1

Wrzucę moją do ringu. To prosty dekorator.

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.

Myślę, że korzyści w porównaniu z innymi rozwiązaniami:

  • Jest jasne i zwięzłe (dla mojego oka; D).
  • Jego działanie jest całkowicie zamknięte. Nie musisz nic zmieniać w implementacjiYourClass . Oznacza to, że nie musisz używać metaklasy w swojej klasie (pamiętaj, że powyższa metaklasa jest w fabryce, a nie w „prawdziwej” klasie).
  • Nie polega na łataniu małp.
  • Jest to przezroczyste dla dzwoniących:
    • Dzwoniący nadal po prostu importują YourClass, wygląda jak klasa (ponieważ tak jest) i używają jej normalnie. Nie trzeba dostosowywać dzwoniących do funkcji fabrycznych.
    • To, co YourClass()tworzy instancję, jest nadal prawdziwą instancją, YourClassktórą wdrożyłeś, a nie jakimkolwiek proxy, więc nie ma z tego powodu żadnych skutków ubocznych.
    • isinstance(instance, YourClass) i podobne operacje nadal działają zgodnie z oczekiwaniami (choć ten bit wymaga abc, więc wyklucza Pythona <2.6).

Przydarza mi się jedna wada: metody klasowe i metody statyczne prawdziwej klasy nie są przezroczyście wywoływalne przez klasę fabryczną, która je ukrywa. Używałem tego na tyle rzadko, że nigdy nie spotkałem się z tą potrzebą, ale można to łatwo naprawić, używając niestandardowej metaklasy w fabryce, która implementuje__getattr__() aby delegować dostęp do wszystkich atrybutów do prawdziwej klasy.

Powiązany wzorzec, który faktycznie uważam za bardziej przydatny (nie to, że mówię, że tego rodzaju rzeczy są w ogóle bardzo często wymagane) to „unikalny” wzorzec, w którym utworzenie instancji klasy z tymi samymi argumentami powoduje odzyskanie tego samego wystąpienia. To znaczy „singleton na argumenty”. Powyższe dostosowuje się do tego dobrze i staje się jeszcze bardziej zwięzłe:

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

To powiedziawszy, zgadzam się z ogólną radą, że jeśli uważasz, że potrzebujesz jednej z tych rzeczy, naprawdę powinieneś się na chwilę zatrzymać i zadać sobie pytanie, czy naprawdę tego potrzebujesz. 99% czasu, YAGNI.

CryingCyclops
źródło
1
  • Jeśli ktoś chce mieć wiele wystąpień tej samej klasy, ale tylko wtedy, gdy argumenty lub kwargs są różne, można tego użyć
  • Dawny.
    1. Jeśli masz serialkomunikację obsługującą klasę i aby utworzyć instancję, którą chcesz wysłać port szeregowy jako argument, wówczas tradycyjne podejście nie będzie działać
    2. Korzystając z wyżej wymienionych dekoratorów, można utworzyć wiele instancji klasy, jeśli argumenty są różne.
    3. Dla tych samych argumentów dekorator zwróci to samo wystąpienie, które zostało już utworzone.
>>> 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
>>>
Siddhesh Suhas Sathe
źródło
0

Kod oparty na odpowiedzi Tolli .

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

Wyjaśnienie:

  1. Utwórz nową klasę, dziedzicząc po danym cls
    (nie zmienia się, clsjeśli ktoś chce na przykład singleton(list))

  2. Utwórz instancję. Przed zastąpieniem __new__jest to takie proste.

  3. Teraz, gdy łatwo utworzyliśmy instancję, zastępuje __new__przy użyciu metody zdefiniowanej przed chwilą.
  4. Funkcja zwraca instancetylko wtedy, gdy oczekuje tego rozmówca, w przeciwnym razie podnosi TypeError.
    Warunek ten nie jest spełniony, gdy ktoś próbuje odziedziczyć klasę odznaczoną.

  5. Jeśli __new__()zwraca instancję cls, __init__()metoda nowej instancji zostanie wywołana tak __init__(self[, ...]), jak self, gdzie jest nowa instancja, a pozostałe argumenty są takie same, jak przekazane __new__().

    instancejest już zainicjowany, więc funkcja zastępuje __init__funkcję, która nic nie robi.

Zobacz, jak działa online

GingerPlusPlus
źródło
0

Jest nieco podobny do odpowiedzi fab, ale nie dokładnie taki sam.

Umowa singleton nie wymaga, abyśmy byli w stanie wywołać konstruktora wiele razy. Jako że singleton powinien zostać utworzony raz i tylko raz, czy nie należy go postrzegać jako utworzonego tylko raz? „Fałszowanie” konstruktora prawdopodobnie pogarsza czytelność.

Więc moja propozycja jest taka:

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

Nie wyklucza to użycia konstruktora lub pola instancewedług kodu użytkownika:

if Elvis() is King.instance:

... jeśli wiesz na pewno, że Elvisjeszcze nie zostało utworzone i tak Kingjest.

Ale zachęca użytkowników do theuniwersalnego korzystania z tej metody:

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

Aby to zakończyć, możesz również przesłonić __delattr__()zgłoszenie wyjątku, jeśli podjęta zostanie próba usunięcia instance, i przesłonić __del__()zgłoszenie wyjątku (chyba że wiemy, że program się kończy ...)

Dalsze doskonalenia


Dziękuję tym, którzy pomogli w komentarzach i zmianach, z których więcej jest mile widziane. Podczas korzystania z Jython powinno to działać bardziej ogólnie i być bezpieczne dla wątków.

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

Uwaga:

  1. Jeśli nie podklasujesz z obiektu w python2.x, otrzymasz klasę starego stylu, która nie używa __new__
  2. Podczas dekorowania __new__musisz udekorować za pomocą @classmethod lub __new__będzie to metoda instancji niezwiązanej
  3. Można by to poprawić za pomocą metaklasy, ponieważ pozwoliłoby to stworzyć thewłaściwość na poziomie klasy, ewentualnie zmieniając jej nazwę nainstance
gryzoń mike
źródło
Chociaż jest to nieco inna interpretacja wzorca singletonu, jestem całkiem pewien, że jest on nadal aktualny, chociaż może kusi mnie użycie __new__ zamiast __init__, ponieważ działa on wyłącznie na atrybuty klasy, co zapobiega krótkotrwałemu wystąpieniu. Różnica między tą a metodą 2 polega na tym, czy próba zainicjowania więcej niż raz zwraca pojedynczą instancję, czy wywołuje wyjątek. Myślę, że cieszę się, że albo spełniają wzór singletonu, jeden jest łatwiejszy w użyciu, a drugi jest bardziej wyraźny, że jest singletonem.
theheadofabroom
Oczywiście użycie nazwy klasy __init__zapobiega podklasom, ale chociaż to ułatwia, nie jest wymagane
theheadofabroom
Dzięki ... ach tak, chwilowa sekunda przed rzuceniem wyjątku. Zmodyfikowałem __init__tak, aby mam nadzieję, że będzie to podklasa ...
Mike Gryzonie
Fajnie, theprawdopodobnie skorzystałby na byciu metodą klasową z podobnych powodów
theheadofabroom
tak, masz rację. Następnie możesz mieć singleton podklasy SuperElvis i (na przykład) singleton podklasy ImaginaryElvis ... i mogą one współistnieć. Zobacz dodatkowe przemyślenia. Popraw swój kod.
Mike gryzoni
0

Jeden liniowiec (nie jestem dumny, ale spełnia swoją rolę):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify
polvoazul
źródło
Może to zrobić zadanie, o ile twoja klasa nie zostanie zaimportowana do innych modułów ...
Aran-Fey
Prawdziwe. Jeśli możesz zapłacić za zainicjowanie klasy, możesz uruchomić Myclass () natychmiast po definicji klasy, aby tego uniknąć.
polvoazul
0

Jeśli nie potrzebujesz leniwej inicjalizacji instancji Singleton, poniższe czynności powinny być łatwe i bezpieczne dla wątków:

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

W ten sposób Ajest singleton zainicjowany podczas importu modułu.

Serge Rogatch
źródło
0

Może nie rozumiem wzoru singletonu, ale moje rozwiązanie jest proste i pragmatyczne (pytoniczne?). Ten kod spełnia dwa cele

  1. Spraw, aby instancja była Foodostępna wszędzie (globalnie).
  2. FooMoże istnieć tylko jedna instancja .

To jest kod.

#!/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()

Wynik

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!
buhtz
źródło
0

Po dłuższym zmaganiu się z tym w końcu wymyśliłem następujące rzeczy, aby obiekt konfiguracyjny został załadowany tylko raz, gdy zostanie wywołany z oddzielnych modułów. Metaklasa pozwala na przechowywanie instancji klasy globalnej we wbudowanym dykcie, który obecnie wydaje się najładniejszym sposobem przechowywania odpowiedniego programu globalnego.

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>...
Den-Jason
źródło
-1

Nie pamiętam, gdzie znalazłem to rozwiązanie, ale uważam, że jest to najbardziej „eleganckie” z mojego punktu widzenia, nie znającego języka 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

Dlaczego to lubię Bez dekoratorów, bez meta klas, bez wielokrotnego dziedziczenia ... a jeśli zdecydujesz, że nie chcesz, aby był to Singleton, po prostu usuń __new__metodę. Ponieważ jestem nowy w Pythonie (i ogólnie OOP), spodziewam się, że ktoś wyjaśni mi, dlaczego jest to okropne podejście?

2cynykyl
źródło
2
dlaczego to jest okropne podejście? jeśli chcesz utworzyć kolejną klasę singleton, musisz skopiować i wkleić __new__. Nie powtarzaj się .
GingerPlusPlus
Ponadto, dlaczego nowa trwa *argsi **kwargs, a potem nic nie robi z nimi? Przekazać je w dict.__new__ten sposób: dict.__new__(cls, *args, **kwargs).
GingerPlusPlus
Spowoduje to wywołanie __init__metody przy każdym wywołaniu klasy. Jeśli twoja __init__metoda rzeczywiście coś zrobiła, zauważysz problem. Ilekroć to zrobisz SomeSingleton(), stan twojego singletonu jest resetowany __init__metodą.
Aran-Fey
-2

To jest mój preferowany sposób implementacji singletonów:

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
fab
źródło
1
Nie jest to ściśle singleton, ponieważ pozwala na istnienie więcej niż jednej instancji klasy. Ulepszeniem byłoby uniemożliwienie zainicjowania klasy
sprawienie
-2

Ta odpowiedź prawdopodobnie nie jest tym, czego szukasz. Chciałem singletonu w tym sensie, że tylko ten obiekt miał swoją tożsamość, dla porównania. W moim przypadku był on używany jako wartość wartownika . Na które odpowiedź jest bardzo prosta, utwórz dowolny obiekt, mything = object()a ze względu na naturę pytona tylko ta rzecz będzie miała swoją tożsamość.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration
ThorSummoner
źródło
Dowiedziałem się, że moduły można faktycznie importować wiele razy, w takim przypadku jest to tylko lokalny singleton, który tak naprawdę nie jest singletonem.
ThorSummoner
Czy możesz opracować sposób wielokrotnego importowania modułu? Jedyny raz, kiedy widziałem, że występuje wyjątek podczas ładowania modułu, użytkownik może nadal ładować moduł później, ale efekty uboczne już się pojawią, więc niektóre akcje mogą być wykonane po raz drugi.
sleblanc
Gdy moduł zostanie w pełni załadowany, nie widzę sposobu na jego ponowne uruchomienie, chyba że zmuszę tłumacza do zrobienia tego za pomocą evallub importlib.reload.
sleblanc
-3

To rozwiązanie powoduje zanieczyszczenie przestrzeni nazw na poziomie modułu (trzy definicje, a nie tylko jedna), ale uważam, że jest łatwe do naśladowania.

Chciałbym móc napisać coś takiego (leniwa inicjalizacja), ale niestety klasy nie są dostępne w treści ich własnych definicji.

# 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

Ponieważ nie jest to możliwe, możemy przełamać inicjalizację i instancję statyczną w

Chętna inicjalizacja:

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

Leniwa inicjalizacja:

Chętna inicjalizacja:

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
Gregory Nisbet
źródło