Blog JSystems - uwalniamy wiedzę!

Szukaj

Singleton, w skrócie

Jedna instancja klasy:

class Config:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
Singleton - zawsze jeden egzemplarz
Wiele wywołań zwraca ten sam obiekt (np. jedno wspólne połączenie do bazy)

Singleton to wzorzec kreacyjny. Zapewnia istnienie co najwyżej jednej instancji danej klasy. Instancja ta musi być dostępna z dowolnego miejsca. Przykład zacznę od zupełnie zwyczajnej klasy, stworzenia 2 jej obiektów i sprawdzenia czy to ten sam obiekt:

class Singleton:
    pass

x = Singleton()
y = Singleton()
print(id(x))
print(id(y))
print(x is y)


W efekcie dostałem na konsoli wynik:

2639965680864
2639965681032
False

Id obiektów się różnią, co świadczy o tym że są to różne obiekty. Potwierdza to też sprawdzenie wyrażenia "x is y" w ostatniej linii. Przerobiłem nieco naszą klasę przesłaniając metodę "__new__" i ponowiłem test:

class Singleton:
    __instancja=None
    def __new__(cls, *args, **kwargs):
        if cls.__instancja is None:
            cls.__instancja=super().__new__(cls,*args,**kwargs)
        return cls.__instancja
a=Singleton()
b=Singleton()
print(id(a))
print(id(b))
print(a is b)

Wynik na konsoli:

2597567231368
2597567231368
True

Tym razem otrzymaliśmy te same id i potwierdzenie że jest to ten sam obiekt. Na czym jednak polegał ten fikołek? Co tu się stało? Metoda "__new__" jest wywoływana w chwili tworzenia obiektu. W naszej klasie mamy prywatne pole "__instancja" które będzie zawierało obiekt klasy Singleton. Obiekt naszej klasy "Singleton" będzie zawierał obiekt klasy "Singleton". Teraz zasada działania kodu wewnątrz metody "__new__" jest taka: jeśli nie mam jeszcze zainicjalizowanego obiektu to go tworzymy, a niezależnie czy właśnie go stworzyliśmy czy był już wcześniej zwracamy go. To jest mechanizm który pilnuje istnienia tylko jednego obiektu klasy Singleton. Znając już mechanizm powinniśmy dotrzeć do prostego wniosku. Jeśli istnieje tylko jeden obiekt klasy Singleton, to w ramach tej klasy mogę stworzyć pole które również będzie przechowywane w jednej instancji. Przeanalizujmy poniższy przykład:

class Singleton:
    pole=None
    __instancja=None

    def __new__(cls, *args, **kwargs):
        if cls.__instancja is None:
            cls.__instancja=super().__new__(cls,*args,**kwargs)
        return cls.__instancja

a=Singleton()
b=Singleton()
a.pole='Zawartość pola'
print(b.pole)

W związku z zastosowaniem wzorca "Singleton" obiekty "a" i "b" to w rzeczywistości ten sam obiekt. Z tego powodu mimo przypisania "zawartość pola" do pola "pole" w obiecie "a", wartość ta jest widoczna również w obiekcie "b". Jest to spowodowane wskazywaniem na ten sam adres w pamięci dla obu obiektów. Zasymulujmy wykorzystanie singletona do utrzymywania tylko jednego połączenia z bazą danych:

class DatabaseConnector:
    polaczenie=None
    instancja=None

    def __new__(cls, *args, **kwargs):
        if cls.instancja is None:
            print('nawiązywanie połączenia')
            cls.polaczenie='połączenie do bazy Oracle'
            cls.instancja=super().__new__(cls,*args,**kwargs)
        return cls.instancja

a=DatabaseConnector()
b=DatabaseConnector()
print(b.polaczenie)

wynik działania na konsoli:

nawiązywanie połączenia
połączenie do bazy Oracle

W powyższym przykładzie "połączenie" jest zwykłym polem tekstowym, ale nic nie stoi na przeszkodzie byśmy tam umieścili cokolwiek innego. Stworzyłem dwa obiekty. W chwili tworzenia obiektu "a" pole "instancja" było w obiekcie klasy "DatabaseConnector" w związku z czym inicjalizujemy to pole uzupełniając także pole "polaczenie". W chwili tworzenia obiektu "b" pole instancja jest już zapełnione w związku z czym "__new__" oddaje już wcześniej zainicjalizowany obiekt bez jego powtórnego inicjalizowania (czyli również zapełniania pola "polaczenie". Zarówno obiekt "a" jak i "b" referują do tego samego miejsca w przestrzeni pamięci - tak więc "a" i "b" to w rzeczywistości ten sam obiekt.

Szkolenie Python od podstaw, przez analizę danych, do machine learning w JSystems

Ten kurs daje Ci solidne podstawy Pythona. Jeśli chcesz pójść dalej i pod okiem trenera dojść od pierwszej linijki kodu do własnego modelu uczenia maszynowego, sprawdź nasze pięciodniowe szkolenie Python od podstaw, przez analizę danych, do machine learning. Pierwsze trzy dni to Python w praktyce: typy i kolekcje, instrukcje warunkowe i pętle, funkcje, moduły i pakiety, pliki tekstowe, usługi sieciowe oraz praca z bazą PostgreSQL. Czwarty dzień to analiza danych w pandas (wczytywanie CSV, TSV i XLS, filtrowanie, agregacje, statystyki) i wizualizacja w Matplotlib, Seaborn oraz Plotly. Piąty dzień to uczenie maszynowe w scikit-learn: regresja liniowa i logistyczna, drzewa decyzyjne, las losowy, XGBoost, walidacja predykcji, dobór hiperparametrów i przeciwdziałanie przeuczeniu. Szkolenie ma terminy gwarantowane, czyli odbywa się niezależnie od wielkości grupy.

Szkolenie Python od podstaw, przez analizę danych, do machine learning

To szkolenie może być dofinansowane dla Ciebie z KFS lub BUR.

★★★★★Średnia ocena naszych szkoleń w Google: 5/5

Najczęściej zadawane pytania

Czym jest wzorzec Singleton w Pythonie?
Singleton to wzorzec kreacyjny, który zapewnia istnienie co najwyżej jednej instancji danej klasy, dostępnej z dowolnego miejsca w programie. Każde kolejne wywołanie konstruktora zwraca ten sam, wcześniej utworzony obiekt zamiast tworzyć nowy.
Jak zaimplementować Singleton w praktyce?
Przesłaniamy metodę __new__, która jest wywoływana przy tworzeniu obiektu. W polu klasy trzymamy referencję do jedynej instancji: jeśli jest ona pusta, tworzymy obiekt przez super().__new__, a następnie zawsze zwracamy zapamiętaną instancję. Dzięki temu wszystkie obiekty wskazują na to samo miejsce w pamięci.
Dlaczego dwa zwykłe obiekty tej samej klasy to różne obiekty?
Bo domyślnie każde wywołanie konstruktora tworzy nowy obiekt w innym miejscu pamięci. Widać to po funkcji id, która zwraca różne wartości, oraz po sprawdzeniu x is y, które daje False. Dopiero przesłonięcie metody __new__ sprawia, że zwracana jest jedna wspólna instancja.
Do czego przydaje się Singleton w aplikacji?
Typowym zastosowaniem jest utrzymywanie jednego wspólnego zasobu, na przykład pojedynczego połączenia do bazy danych lub obiektu konfiguracji. Skoro istnieje tylko jedna instancja, pole ustawione na jednym obiekcie jest widoczne przez wszystkie odwołania, bo to w rzeczywistości ten sam obiekt.
Czy pole ustawione na jednym obiekcie Singletona widać w innym?
Tak. Ponieważ oba odwołania wskazują na ten sam adres w pamięci, przypisanie wartości do pola przez jeden obiekt jest natychmiast widoczne przez drugi. W praktyce to nie są dwa obiekty, tylko dwie nazwy dla tej samej instancji.

Komentarze (0)

Musisz być zalogowany by móc dodać komentarz. Zaloguj się przez Google

Brak komentarzy...