Membuat singleton dengan Python

946

Pertanyaan ini bukan untuk diskusi tentang apakah pola desain tunggal diinginkan atau tidak , merupakan anti-pola, atau untuk perang agama apa pun, tetapi untuk membahas bagaimana pola ini paling baik diimplementasikan dengan Python sedemikian rupa sehingga paling pythonic. Dalam contoh ini saya mendefinisikan 'paling pythonic' berarti bahwa ia mengikuti 'prinsip paling tidak heran' .

Saya memiliki beberapa kelas yang akan menjadi lajang (use-case saya adalah untuk seorang logger, tetapi ini tidak penting). Saya tidak ingin mengacaukan beberapa kelas dengan gumph tambahan ketika saya bisa mewarisi atau menghias.

Metode terbaik:


Metode 1: Seorang 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

Pro

  • Dekorator bersifat aditif dengan cara yang seringkali lebih intuitif daripada pewarisan berganda.

Cons

  • Sementara objek yang dibuat menggunakan MyClass () akan menjadi objek singleton sejati, MyClass sendiri adalah fungsi, bukan kelas, jadi Anda tidak dapat memanggil metode kelas dari itu. Juga untuk m = MyClass(); n = MyClass(); o = type(n)();saat itum == n && m != o && n != o

Metode 2: Kelas dasar

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

Pro

  • Kelas yang benar

Cons

  • Warisan berganda - eugh! __new__dapat ditimpa selama pewarisan dari kelas dasar kedua? Kita harus berpikir lebih dari yang diperlukan.

Metode 3: Metaclass

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

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

Pro

  • Kelas yang benar
  • Secara otomatis mencakup warisan
  • Menggunakan __metaclass__untuk tujuan yang tepat (dan membuat saya menyadarinya)

Cons

  • Apakah ada?

Metode 4: dekorator mengembalikan kelas dengan nama yang sama

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

Pro

  • Kelas yang benar
  • Secara otomatis mencakup warisan

Cons

  • Apakah tidak ada overhead untuk membuat setiap kelas baru? Di sini kita membuat dua kelas untuk setiap kelas yang ingin kita buat sendiri. Meskipun ini baik-baik saja dalam kasus saya, saya khawatir ini mungkin tidak skala. Tentu saja ada masalah perdebatan, apakah terlalu mudah untuk mengubah skala pola ini ...
  • Apa gunanya _sealedatribut
  • Tidak dapat memanggil metode dengan nama yang sama pada kelas dasar menggunakan super()karena mereka akan berulang. Ini berarti Anda tidak dapat mengkustomisasi __new__dan tidak dapat mensubkelas kelas yang perlu Anda panggil __init__.

Metode 5: modul

file modul singleton.py

Pro

  • Sederhana lebih baik daripada kompleks

Cons

ruang kepala
sumber
12
Tiga teknik lain: gunakan modul sebagai gantinya (sering - umumnya, saya pikir - ini adalah pola yang lebih tepat untuk Python tetapi sedikit tergantung pada apa yang Anda lakukan dengannya); buat satu contoh dan atasi saja ( foo.xatau jika Anda bersikeras Foo.xbukan Foo().x); gunakan atribut kelas dan metode statis / kelas ( Foo.x).
Chris Morgan
10
@ ChrisMorgan: Jika Anda akan menggunakan metode kelas / statis saja, maka jangan repot-repot membuat kelas, sungguh.
Cat Plus Plus
2
@ Kucing: Efeknya serupa, namun alasan di balik pembuatan variabel global bisa apa saja, termasuk tidak mengetahui yang lebih baik. Mengapa seseorang membuat singleton? Jika Anda harus bertanya, Anda seharusnya tidak berada di sini. Penjelasan ini tidak hanya lebih pythonic, tetapi membuat perawatan menjadi lebih sederhana. Ya lajang adalah gula sintaksis untuk global, tetapi kemudian kelas adalah gula sintaksis untuk sejumlah besar hal yang tidak sedap dipandang dan saya tidak berpikir ada orang yang akan memberi tahu Anda bahwa Anda selalu lebih baik tanpanya.
theheadofabroom
14
@BiggAl: Lajang bukan Pythonic, tidak peduli bagaimana Anda menerapkannya. Mereka adalah tanda desain cacat yang terbaik.
Cat Plus Plus
8
Sentimen anti-signletons adalah pemrograman pemujaan kargo yang paling buruk. Sama dengan orang-orang yang mendengar (hanya sedikit yang bersusah payah untuk benar-benar membaca) "Pernyataan goto dianggap berbahaya" dan menganggap goto adalah tanda kode buruk terlepas dari konteksnya.
Hejazzman

Jawaban:

658

Gunakan Metaclass

Saya akan merekomendasikan Metode # 2 , tetapi Anda lebih baik menggunakan metaclass daripada kelas dasar. Berikut ini contoh implementasi:

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

Atau dalam Python3

class Logger(metaclass=Singleton):
    pass

Jika Anda ingin menjalankan __init__setiap kali kelas dipanggil, tambahkan

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

pada ifpernyataan dalam Singleton.__call__.

Beberapa kata tentang metaclasses. Metaclass adalah kelas suatu kelas ; yaitu, kelas adalah turunan dari metaclassnya . Anda menemukan metaclass dari objek dengan Python type(obj). Kelas gaya baru normal adalah tipe type. Loggerdalam kode di atas akan bertipe class 'your_module.Singleton', sama seperti (hanya) turunan Loggerbertipe class 'your_module.Logger'. Ketika Anda menelepon logger dengan Logger(), Python pertama meminta metaclass dari Logger, Singleton, apa yang harus dilakukan, sehingga contoh penciptaan untuk pra-empted. Proses ini sama dengan Python yang meminta kelas apa yang harus dilakukan dengan memanggil __getattr__ketika Anda mereferensikan salah satu atributnya dengan melakukan myclass.attribute.

Metaclass pada dasarnya memutuskan apa arti suatu kelas dan bagaimana mengimplementasikannya. Lihat misalnya http://code.activestate.com/recipes/498149/ , yang pada dasarnya menciptakan ulang gaya-C structdi Python menggunakan metaclasses. Utas Apa sajakah case penggunaan (konkret) untuk metaclasses? juga memberikan beberapa contoh, mereka umumnya tampaknya terkait dengan pemrograman deklaratif, terutama seperti yang digunakan dalam ORM.

Dalam situasi ini, jika Anda menggunakan Metode # 2 Anda , dan subkelas mendefinisikan __new__metode, itu akan dieksekusi setiap kali Anda menelepon SubClassOfSingleton()- karena bertanggung jawab untuk memanggil metode yang mengembalikan instance yang disimpan. Dengan metaclass, itu hanya akan dipanggil sekali , ketika satu-satunya instance dibuat. Anda ingin menyesuaikan apa artinya memanggil kelas , yang ditentukan oleh jenisnya.

Secara umum, masuk akal untuk menggunakan metaclass untuk mengimplementasikan singleton. Singleton adalah spesial karena dibuat hanya sekali , dan metaclass adalah cara Anda menyesuaikan penciptaan kelas . Menggunakan metaclass memberi Anda lebih banyak kontrol jika Anda perlu menyesuaikan definisi kelas singleton dengan cara lain.

Singletons Anda tidak memerlukan pewarisan berganda (karena metaclass bukan kelas dasar), tetapi untuk subclass dari kelas yang dibuat yang menggunakan pewarisan berganda, Anda perlu memastikan bahwa kelas singleton adalah yang pertama / paling kiri dengan metaclass yang mendefinisikan ulang __call__Ini sangat tidak mungkin menjadi masalah. Dict instance tidak ada dalam namespace instance sehingga tidak akan sengaja menimpanya.

Anda juga akan mendengar bahwa pola tunggal melanggar "Prinsip Tanggung Jawab Tunggal" - setiap kelas hanya boleh melakukan satu hal . Dengan begitu Anda tidak perlu khawatir mengacaukan satu hal yang dilakukan kode jika Anda perlu mengubah yang lain, karena mereka terpisah dan dienkapsulasi. Implementasi metaclass melewati tes ini . Metaclass bertanggung jawab untuk menegakkan pola dan kelas dan subclass yang dibuat tidak perlu menyadari bahwa mereka adalah lajang . Metode # 1 gagal tes ini, seperti yang Anda catat dengan "MyClass itu sendiri adalah fungsi, bukan kelas, jadi Anda tidak bisa memanggil metode kelas dari itu."

Versi Kompatibel Python 2 dan 3

Menulis sesuatu yang berfungsi baik di Python2 dan 3 membutuhkan menggunakan skema yang sedikit lebih rumit. Sejak metaclasses biasanya subclass dari jenis type, itu mungkin untuk menggunakan satu untuk secara dinamis membuat kelas dasar perantara pada waktu berjalan dengan sebagai metaclass dan kemudian menggunakan bahwa sebagai baseclass dari masyarakat Singletonkelas dasar. Lebih sulit untuk dijelaskan daripada melakukannya, seperti digambarkan selanjutnya:

# 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

Aspek ironis dari pendekatan ini adalah menggunakan subclass untuk mengimplementasikan metaclass. Satu keuntungan yang mungkin adalah bahwa, tidak seperti metaclass murni, isinstance(inst, Singleton)akan kembali True.

Koreksi

Pada topik lain, Anda mungkin sudah memperhatikan ini, tetapi implementasi kelas dasar dalam posting asli Anda salah. _instancesperlu direferensikan di kelas , Anda perlu menggunakan super()atau Anda berulang , dan __new__sebenarnya merupakan metode statis yang Anda harus lulus kelas , bukan metode kelas, karena kelas yang sebenarnya belum dibuat saat itu disebut. Semua hal ini akan berlaku juga untuk implementasi metaclass.

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

Penghias Mengembalikan Kelas A

Saya awalnya menulis komentar tetapi terlalu lama, jadi saya akan menambahkan ini di sini. Metode # 4 lebih baik daripada versi dekorator lainnya, tetapi lebih banyak kode daripada yang dibutuhkan untuk seorang singleton, dan tidak jelas apa fungsinya.

Masalah utama berasal dari kelas adalah kelas dasar itu sendiri. Pertama, bukankah aneh memiliki kelas menjadi subkelas dari kelas yang hampir identik dengan nama yang sama yang hanya ada di __class__atributnya? Ini juga berarti bahwa Anda tidak dapat menentukan metode yang memanggil metode dengan nama yang sama di kelas dasar mereka dengan super()karena mereka akan recurse. Ini berarti kelas Anda tidak dapat dikustomisasi __new__, dan tidak dapat berasal dari kelas apa pun yang perlu __init__dipanggil.

Kapan harus menggunakan pola singleton

Case use Anda adalah salah satu contoh yang lebih baik dari keinginan untuk menggunakan singleton. Anda mengatakan dalam salah satu komentar, "Bagi saya penebangan selalu tampak sebagai calon alami untuk Singletons." Anda benar sekali .

Ketika orang mengatakan lajang itu buruk, alasan paling umum adalah mereka adalah negara bagian implisit . Sementara dengan variabel global dan impor modul tingkat atas adalah keadaan bersama eksplisit , objek lain yang diedarkan pada umumnya dipakai. Ini adalah poin yang bagus, dengan dua pengecualian .

Yang pertama, dan yang disebutkan di berbagai tempat, adalah ketika lajang konstan . Penggunaan konstanta global, terutama enum, diterima secara luas, dan dianggap waras karena bagaimanapun juga, tidak ada pengguna yang dapat mengacaukannya untuk pengguna lain mana pun . Ini sama benarnya dengan singleton yang konstan.

Pengecualian kedua, yang disebutkan lebih sedikit, adalah kebalikannya - ketika singleton hanya merupakan data sink , bukan sumber data (langsung atau tidak langsung). Inilah sebabnya mengapa penebang merasa seperti penggunaan "alami" untuk lajang. Karena berbagai pengguna tidak mengubah penebang dengan cara yang dipedulikan oleh pengguna lain, sebenarnya tidak ada status bersama . Ini meniadakan argumen utama terhadap pola tunggal, dan menjadikannya pilihan yang masuk akal karena mudah digunakan untuk tugas tersebut.

Berikut ini kutipan dari http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :

Sekarang, ada satu jenis Singleton yang OK. Itu adalah singleton di mana semua objek yang dapat dijangkau tidak berubah. Jika semua objek tidak berubah daripada Singleton tidak memiliki keadaan global, karena semuanya konstan. Tetapi sangat mudah untuk mengubah jenis singleton ini menjadi yang bisa berubah, ini adalah lereng yang sangat licin. Karena itu, saya juga menentang para lajang ini, bukan karena mereka jahat, tetapi karena sangat mudah bagi mereka untuk menjadi jahat. (Sebagai catatan, enumerasi Java hanyalah lajang semacam ini. Selama Anda tidak memasukkan status ke enumerasi, Anda baik-baik saja, jadi tolong jangan.)

Jenis lajang lain, yang semi-dapat diterima adalah mereka yang tidak mempengaruhi eksekusi kode Anda, Mereka tidak memiliki "efek samping". Pencatatan adalah contoh sempurna. Itu dimuat dengan lajang dan negara global. Itu dapat diterima (karena itu tidak akan menyakiti Anda) karena aplikasi Anda tidak berlaku berbeda apakah logger yang diberikan diaktifkan atau tidak. Informasi di sini mengalir satu arah: Dari aplikasi Anda ke dalam logger. Bahkan penebang berpikir adalah keadaan global karena tidak ada informasi mengalir dari penebang ke aplikasi Anda, penebang dapat diterima. Anda harus tetap menyuntikkan logger Anda jika Anda ingin tes Anda untuk menegaskan bahwa ada sesuatu yang sedang login, tetapi secara umum Logger tidak berbahaya meskipun penuh dengan keadaan.

agf
sumber
5
Tidak, lajang tidak pernah baik. Penebangan mungkin menjadi kandidat yang baik untuk menjadi global (sama buruknya dengan mereka), tetapi tentu saja bukan singleton.
Cat Plus Plus
11
Lihatlah googletesting.blogspot.com/2008/08/… . Ini umumnya anti-singleton (untuk alasan yang baik) tetapi memiliki penjelasan yang baik tentang mengapa lajang dan lajang yang langgeng tanpa efek samping tidak memiliki masalah yang sama, jika Anda berhati-hati. Saya akan mengutipnya sedikit di akhir posting saya.
agf
4
Masalah saya dengan lajang adalah premis bodoh "hanya satu contoh". Itu dan banyak masalah keamanan benang. Dan ketergantungan bersembunyi. Global itu buruk, dan lajang hanya global dengan lebih banyak masalah.
Cat Plus Plus
4
@ Kucing Ada kegunaan yang sangat baik untuk lajang. Instansiasi malas dari modul perangkat keras (terutama dalam aplikasi berulir tunggal) adalah salah satunya (tetapi singleton yang aman juga ada).
Paul Manta
3
@Alcott __new__dalam metaclass adalah ketika kelas baru - ketika sudah ditentukan, bukan saat instance akan baru. Memanggil kelas ( MyClass()) adalah operasi yang ingin Anda timpa, bukan definisi kelas. Jika Anda benar-benar ingin memahami cara kerja Python, hal terbaik yang dapat Anda lakukan (selain tetap menggunakannya) adalah membaca docs.python.org/reference/datamodel.html . Referensi yang baik tentang metaclasses adalah eli.thegreenplace.net/2011/08/08/14/python-metaclasses-by-example . Artikel bagus tentang lajang adalah seri dari blog google yang saya tautkan dalam jawaban ini.
agf
91
class Foo(object):
     pass

some_global_variable = Foo()

Modul hanya diimpor sekali, yang lainnya terlalu banyak berpikir. Jangan gunakan lajang dan cobalah untuk tidak menggunakan global.

Cat Plus Plus
sumber
14
mengapa Anda mengatakan "Jangan gunakan lajang"? Alasan apapun?
Alcott
3
Ini tidak akan berhasil jika singleton harus diasamkan. Menggunakan contoh yang Anda berikan:s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
dividebyzero
4
@dividebyzero: isoperator menguji kesetaraan pointer. Saya akan agak terkejut --- sampai menyebutnya bug --- jika pickle.loadsmengembalikan referensi ke objek yang sudah ada daripada referensi ke yang baru dibuat. Jadi, menguji apakah s is s1tidak memberi tahu Anda tentang kesesuaian menggunakan modul sebagai lajang.
Jonas Kölker
1
@ JonasKölker pickle.loads()sudah melakukannya, misalnya untuk instance booldan NoneType. pickle.loads(pickle.dumps(False)) is FalsehasilTrue
Dan Passaro
2
@ leo-the-manic: titik adil; Namun, itu hanya efek samping dari Python menginternir objek True, Falsedan None, dan tidak ada hubungannya dengan kode di belakang pickle.loads. Juga, aman untuk dilakukan hanya untuk objek read-only. Jika pickle.loadsingin mengembalikan referensi ke objek yang sudah dimodifikasi yang sudah ada - seperti modul - itu akan menjadi bug. (Dan saya berdiri dengan implikasi saya bahwa contoh kode dividebyzero tidak membuktikan apa-apa.)
Jonas Kölker
69

Gunakan modul. Hanya diimpor sekali. Tentukan beberapa variabel global di dalamnya - mereka akan menjadi 'atribut' tunggal. Tambahkan beberapa fungsi - 'metode' tunggal.

warvariuc
sumber
11
Jadi yang Anda dapatkan adalah ... Bukan kelas. Anda tidak dapat menggunakannya sebagai kelas, Anda tidak dapat mendasarkan kelas lain di atasnya, Anda menggunakan sintaks impor, dan tiba-tiba Anda kehilangan semua manfaat OOP ...
theheadofabroom
16
jika Anda dapat mendasarkan kelas lain di atasnya, maka itu mungkin bukan singleton. Anda bisa membuat salah satu kelas turunan, tetapi juga salah satu kelas dasar, tetapi kelas turunan juga merupakan anggota dari basis, dan Anda memiliki dua basis, yang mana yang harus Anda gunakan?
SingleNegationElimination
Ini tidak berfungsi di seluruh modul. Dalam modul "utama" saya, saya menetapkan nilai. Saya kemudian referensi dalam modul lain dan nolnya. Mendesah.
Paul Kenjora
1
@PaulKenjora Anda harus memiliki kesalahan dalam kode Anda. Jika Anda mendefinisikan variabel global dalam sebuah modul, ketika Anda mengaksesnya dari modul lain, ia akan memiliki nilai.
warvariuc
Ini memiliki nilai tetapi ketika saya mengubahnya tidak terlindungi. Saya akhirnya menggunakan kelas dengan properti dalam modul yang berfungsi. Tipe sederhana sebagai global tidak bekerja untuk saya (mereka kehilangan nilai segera setelah ruang lingkup berubah).
Paul Kenjora
29

Anda mungkin tidak pernah membutuhkan singleton dalam Python. Cukup tentukan semua data dan fungsi Anda dalam sebuah modul dan Anda memiliki singleton de-facto.

Jika Anda benar-benar harus memiliki kelas tunggal maka saya akan pergi dengan:

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

my_singleton = My_Singleton()

Menggunakan:

from mysingleton import my_singleton
my_singleton.foo()

di mana mysingleton.py adalah nama file Anda yang didefinisikan My_Singleton. Ini berfungsi karena setelah pertama kali file diimpor, Python tidak menjalankan kembali kode.

Alan Dyke
sumber
4
Kebanyakan benar, tetapi kadang-kadang itu tidak cukup. Misalnya saya punya proyek dengan kebutuhan untuk mencatat instantiasi banyak kelas di tingkat DEBUG. Saya perlu opsi baris perintah yang diuraikan pada startup untuk mengatur level logging yang ditentukan pengguna sebelum kelas-kelas tersebut dipakai. Instansiasi tingkat modul membuat masalah. Mungkin saja saya dapat menyusun aplikasi dengan hati-hati sehingga semua kelas tersebut tidak dapat diimpor hingga pemrosesan CLI selesai, tetapi struktur alami aplikasi saya lebih penting daripada kepatuhan dogmatis terhadap "lajang yang buruk", karena mereka dapat dilakukan dengan cukup bersih.
CryingCyclops
jika Anda menguji kode Anda saat menambal my_singleton, apakah itu mungkin? karena my_singleton ini dapat dipakai di beberapa modul lainnya.
Naveen
@Naveen - my_singleton adalah objek tunggal. Jika Anda "menambal" perubahan itu akan memengaruhi semua referensi di masa mendatang, bahkan di modul lain.
Alan Dyke
16

Ini satu kalimat untuk Anda:

singleton = lambda c: c()

Begini cara Anda menggunakannya:

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

assert wat.get_x() == 1

Objek Anda akan instantiated dengan penuh semangat. Ini mungkin atau mungkin bukan yang Anda inginkan.

Jonas Kölker
sumber
5
-1. Ini sangat buruk dan konyol. Anda tidak mendefinisikan kelas untuk digunakan sebagai objek. Anda tidak dapat lagi mengakses kelas tanpa menjadi sangat jelek type(wat)atau wat.__class__. Jika Anda benar-benar ingin mencapai ini, lebih baik menentukan kelas dan instantiate segera, tidak perlu mengacaukan dekorator.
0xc0de
2
Mengapa Anda perlu tahu menggunakan kelas singleton? Cukup gunakan objek tunggal ..
Tolli
1
Ini bukan pola tunggal , jadi fungsi IMO harus diberi nama berbeda.
GingerPlusPlus
8
Wikipedia: "pola singleton adalah pola desain yang membatasi instantiasi kelas ke satu objek". Saya akan mengatakan bahwa solusi saya tidak hanya itu. Oke, saya kira orang bisa melakukannya wat2 = type(wat)(), tapi ini python, kita semua menyetujui orang dewasa dan semua itu. Anda tidak dapat menjamin bahwa hanya akan ada satu contoh, tetapi Anda dapat menjamin bahwa jika orang membuat yang kedua, itu akan terlihat jelek dan — jika mereka orang yang sopan dan terhormat — seperti tanda peringatan bagi mereka. Apa yang saya lewatkan?
Jonas Kölker
7

Lihat pertanyaan Stack Overflow Apakah ada cara sederhana dan elegan untuk mendefinisikan lajang di Python? dengan beberapa solusi.

Saya sangat merekomendasikan untuk menonton pembicaraan Alex Martelli tentang pola desain dengan python: bagian 1 dan bagian 2 . Secara khusus, di bagian 1 ia berbicara tentang lajang / objek negara bersama.

Anton
sumber
2
Meskipun ini bukan jawaban untuk pertanyaan saya, sumber daya yang Anda tunjuk sangat berguna. Dengan berat hati saya memberi Anda +1
theheadofabroom
3

Inilah implementasi lajang saya sendiri. Yang harus Anda lakukan adalah menghias kelas; untuk mendapatkan singleton, Anda kemudian harus menggunakan Instancemetode ini. Ini sebuah contoh:

   @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

Dan inilah kodenya:

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
sumber
2
Itu bukan singleton sejati. SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())harus mencetak Truedalam singleton sejati; dengan cetakan kode AndaFalse
GingerPlusPlus
@GingerPlusPlus Saya menyadari beberapa keterbatasan, tetapi bukan yang Anda tunjukkan. Terima kasih telah menyebutkannya. Sayangnya, saya tidak punya waktu saat ini untuk menipiskan solusi untuk ini.
Paul Manta
2

Metode 3 tampaknya sangat rapi, tetapi jika Anda ingin program Anda berjalan di Python 2 dan Python 3 , itu tidak berfungsi. Bahkan melindungi varian yang terpisah dengan tes untuk versi Python gagal, karena versi Python 3 memberikan kesalahan sintaksis dalam Python 2.

Terima kasih kepada Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . Jika Anda ingin program bekerja di Python 2 dan Python 3, Anda perlu melakukan sesuatu seperti:

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

Saya menganggap bahwa 'objek' dalam penugasan perlu diganti dengan 'BaseClass', tetapi saya belum mencobanya (saya telah mencoba kode seperti yang diilustrasikan).

Tim
sumber
tentunya ini bukan metaclass - dalam python3 untuk menggunakan metaclass untuk membangun MyClass yang akan Anda lakukanclass MyClass(metaclass=Singleton)
theheadofabroom
The mikewatkins.ca link (efektif) rusak.
Peter Mortensen
1

Nah, selain setuju dengan saran Pythonic umum tentang memiliki tingkat modul global, bagaimana dengan ini:

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)

Output adalah:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance
Menjaga
sumber
Apa gunanya _sealedatribut? Sejauh yang saya lihat ini tidak melakukan apa-apa? Ada sesuatu yang menggangguku tentang hal ini yang mengatakan itu seharusnya tidak berkinerja baik ... Aku akan menjalankan beberapa tes komparatif minggu ini.
theheadofabroom
_sealed memastikan init Anda dijalankan hanya sekali; Saya tidak melihat titik mengapa itu harus berkinerja lebih buruk daripada dekorator seperti fungsi biasa - fungsi dijalankan hanya sekali per kelas, dan mengembalikan kelas warisan baru
Penjaga
BTW, hasil edit Anda mengandung tab yang merusak indentasi Anda juga mengatakan 'kami membuat 2 kelas' - maksud Anda bahwa kami membuat '1 kelas tambahan'?
Penjaga
Ya satu kelas tambahan adalah yang saya maksud. Saya bermaksud memasukkan hal-hal __init__untuk dipanggil setiap kali diinisialisasi. Hanya 'Menjadi diinisialisasi di class.method' sederhana. Adapun lekukan - Anda menggunakan tab dan spasi - Saya memperbaiki sebagian besar, tetapi tampaknya telah melewatkan satu jika Anda ingin mendapatkannya (cukup periksa edit log)
theheadofabroom
kembali init : tentu saja terserah Anda, saya hanya mencoba meniru perilaku singleton dalam bahasa lain, di mana kode konstruktor (yang tidak persis init , tetapi sangat dekat artinya) hanya dipanggil satu kali jika Anda ingin init menjadi dipanggil setiap kali, bunuh saja semua referensi ke _sealed re spasi / tab - yah, maka emacs saya perlu diperbaiki. bagaimanapun, di atas adalah versi yang diperbaiki
Penjaga
1

Bagaimana dengan ini:

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

Gunakan itu sebagai dekorator di kelas yang seharusnya menjadi singleton. Seperti ini:

@singleton
class MySingleton:
    #....

Ini mirip dengan singleton = lambda c: c()dekorator di jawaban lain. Seperti solusi lainnya, satu-satunya contoh memiliki nama kelas ( MySingleton). Namun, dengan solusi ini Anda masih dapat "membuat" instance (benar-benar mendapatkan satu-satunya instance) dari kelas, dengan melakukan MySingleton(). Ini juga mencegah Anda membuat instance tambahan dengan melakukan type(MySingleton)()(itu juga mengembalikan instance yang sama).

Tolli
sumber
Anda tidak mendefinisikan kelas untuk menggunakannya sebagai objek.
0xc0de
1
Setiap kali Anda menelepon type(MySingleton)(), MySingleton.__init__()dipanggil dan objek akan diinisialisasi beberapa kali; Anda dapat memperbaikinya dengan menulis cls.__init__ = lambda self: passdi blog Anda singleton. Juga, mengesampingkan cls.__call__tampaknya tidak ada gunanya, dan bahkan berbahaya - __call__didefinisikan dalam konteks ini digunakan saat Anda menelepon MySingleton(any, list, of, arguments), bukan saat Anda menelepon type(MySingleton)(any, list, of, arguments).
GingerPlusPlus
@GingerPlusPlus, Terima kasih telah menunjukkan bahwa __init__()dipanggil lagi ketika melakukan type(MySingleton)(). Solusi yang Anda usulkan (tambahkan cls.__init__ = lambda self: pass) memberikan kesalahan sintaksis, karena bagian terakhir dari ekspresi lambda harus berupa ekspresi, bukan pernyataan. Namun, menambahkan cls.__init__ = lambda self: Nonekarya, jadi saya menambahkan itu ke jawaban saya.
Tolli
1
@GingerPlusPlus, Mengenai penggunaan __call__. niat saya adalah untuk membuat keduanya type(MySingleton)()dan MySingleton()mengembalikan contoh. Jadi itu adalah melakukan apa yang saya inginkan. Anda dapat menganggap MySingleton sebagai tipe dari singleton atau instance dari singleton (atau keduanya).
Tolli
1

Saya akan melemparkan milik saya ke atas ring. Ini dekorator sederhana.

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.

Manfaat yang menurut saya memiliki lebih dari beberapa solusi lain:

  • Jelas dan ringkas (di mata saya; D).
  • Aksinya benar-benar dienkapsulasi. Anda tidak perlu mengubah satu hal pun tentang penerapan YourClass. Ini termasuk tidak perlu menggunakan metaclass untuk kelas Anda (perhatikan bahwa metaclass di atas ada di pabrik, bukan kelas "nyata").
  • Itu tidak bergantung pada apa pun yang menambal monyet.
  • Ini transparan untuk penelepon:
    • Penelepon masih hanya mengimpor YourClass, sepertinya kelas (karena itu), dan mereka menggunakannya secara normal. Tidak perlu menyesuaikan penelepon ke fungsi pabrik.
    • YourClass()Instantiate apa yang masih menjadi contoh nyata dari YourClassAnda diimplementasikan, bukan proxy dalam bentuk apa pun, sehingga tidak ada kemungkinan efek samping yang dihasilkan dari itu.
    • isinstance(instance, YourClass) dan operasi serupa masih bekerja seperti yang diharapkan (meskipun bit ini memang memerlukan abc sehingga menghalangi Python <2.6).

Satu kelemahan terjadi pada saya: metode dan metode statis dari kelas nyata tidak dapat dipanggil secara transparan melalui kelas pabrik menyembunyikannya. Saya sudah menggunakan ini cukup jarang sehingga saya tidak pernah bertemu dengan kebutuhan itu, tetapi akan mudah diperbaiki dengan menggunakan metaclass khusus pada pabrik yang mengimplementasikan __getattr__()untuk mendelegasikan akses atribut all-ish ke kelas nyata.

Pola terkait yang sebenarnya saya temukan lebih berguna (bukan berarti saya mengatakan hal-hal semacam ini sangat sering diperlukan) adalah pola "Unik" di mana instantiasi kelas dengan argumen yang sama menghasilkan kembali contoh yang sama. Yaitu "tunggal per argumen". Di atas beradaptasi dengan baik ini dan menjadi lebih ringkas:

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

Semua yang dikatakan, saya setuju dengan saran umum bahwa jika Anda berpikir Anda membutuhkan salah satu dari hal-hal ini, Anda mungkin harus berhenti sejenak dan bertanya pada diri sendiri apakah Anda benar-benar melakukannya. 99% dari waktu, YAGNI.

CryingCyclops
sumber
1
  • Jika seseorang ingin memiliki beberapa instance dari kelas yang sama, tetapi hanya jika args atau kwargs berbeda, seseorang dapat menggunakan ini
  • Ex.
    1. Jika Anda memiliki serialkomunikasi penanganan kelas , dan untuk membuat contoh Anda ingin mengirim port serial sebagai argumen, maka dengan pendekatan tradisional tidak akan berfungsi
    2. Menggunakan dekorator yang disebutkan di atas, seseorang dapat membuat beberapa instance kelas jika args berbeda.
    3. Untuk argumen yang sama, dekorator akan mengembalikan instance yang sama yang sudah dibuat.
>>> 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
sumber
0

Kode berdasarkan jawaban 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)

Penjelasan:

  1. Buat kelas baru, mewarisi dari yang diberikan cls
    (itu tidak berubah clsjika seseorang ingin misalnya singleton(list))

  2. Buat contoh. Sebelum mengganti __new__itu sangat mudah.

  3. Sekarang, ketika kita telah dengan mudah membuat instance, menimpa __new__menggunakan metode yang ditentukan saat yang lalu.
  4. Fungsi kembali instancehanya ketika itu yang diinginkan penelepon, jika tidak memunculkan TypeError.
    Kondisi ini tidak terpenuhi ketika seseorang mencoba untuk mewarisi dari kelas yang didekorasi.

  5. Jika __new__()mengembalikan instance dari cls, maka metode instance baru __init__()akan dipanggil seperti __init__(self[, ...]), di mana diri adalah instance baru dan argumen yang tersisa adalah sama seperti yang diteruskan ke __new__().

    instancesudah diinisialisasi, jadi fungsi menggantikan __init__dengan fungsi tidak melakukan apa-apa.

Lihat bekerja online

GingerPlusPlus
sumber
0

Ini sedikit mirip dengan jawaban oleh fab tetapi tidak persis sama.

The kontrak tunggal tidak mengharuskan kami dapat memanggil konstruktor beberapa kali. Sebagai seorang singleton harus diciptakan hanya sekali dan sekali saja, tidakkah harus dilihat diciptakan hanya sekali? "Memalsukan" konstruktor bisa dibilang merusak keterbacaan.

Jadi saran saya hanyalah ini:

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

Ini tidak mengesampingkan penggunaan konstruktor atau bidang instancedengan kode pengguna:

if Elvis() is King.instance:

... jika Anda tahu pasti bahwa Elvisitu belum dibuat, dan itu Kingtelah.

Tapi itu mendorong pengguna untuk menggunakan themetode ini secara universal:

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

Untuk menyelesaikan ini, Anda juga dapat mengganti __delattr__()untuk menaikkan Pengecualian jika ada upaya untuk menghapus instance, dan menimpa __del__()sehingga menimbulkan Pengecualian (kecuali kami tahu program ini berakhir ...)

Perbaikan lebih lanjut


Terima kasih saya kepada mereka yang telah membantu dengan komentar dan pengeditan, yang lebih banyak lagi kami sambut. Sementara saya menggunakan Jython, ini seharusnya bekerja lebih umum, dan aman untuk thread.

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

Catatan:

  1. Jika Anda tidak subkelas dari objek di python2.x Anda akan mendapatkan kelas gaya lama, yang tidak digunakan __new__
  2. Saat mendekorasi, __new__Anda harus mendekorasi dengan @classmethod atau __new__akan menjadi metode instan yang tidak terikat
  3. Ini mungkin dapat ditingkatkan dengan menggunakan metaclass, karena ini akan memungkinkan Anda untuk membuat theproperti tingkat kelas, mungkin mengubah nama menjadiinstance
mike rodent
sumber
Meskipun ini adalah interpretasi yang sedikit berbeda dari pola tunggal, saya cukup yakin itu masih berlaku, meskipun saya mungkin tergoda untuk menggunakan __new__ daripada __init__, karena itu murni bertindak pada atribut kelas dan ini mencegah dari sana sebentar menjadi contoh kedua. Perbedaannya antara ini dan metode 2, adalah apakah mencoba menginisialisasi lebih dari satu kali mengembalikan instance tunggal atau menimbulkan pengecualian. Saya pikir saya senang bahwa baik memenuhi pola singleton, satu lebih mudah digunakan, sementara yang lain lebih eksplisit bahwa itu adalah singleton.
theheadofabroom
Jelas penggunaan nama kelas dalam __init__mencegah subclassing, tetapi sementara ini membuat segalanya lebih mudah, itu tidak diperlukan
theheadofabroom
Terima kasih ... ah ya, contoh kedua sesaat sebelum Pengecualian terlempar. Saya telah memodifikasi __init__agar mudah-mudahan ini subclassable ...
mike rodent
Keren, themungkin bisa mendapat manfaat dari menjadi metode kelas karena alasan yang sama
theheadofabroom
ya kau benar. Kemudian Anda dapat memiliki singleton subclass SuperElvis dan (misalnya) singleton subclass ImaginaryElvis ... dan mereka dapat hidup berdampingan. Lihat pemikiran tambahan. Silakan meningkatkan kode saya.
mike rodent
0

One liner (saya tidak bangga, tetapi berhasil):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify
polvoazul
sumber
Ini dapat melakukan pekerjaan selama kelas Anda belum diimpor ke modul lain ...
Aran-Fey
Benar. Jika Anda dapat membayar biaya untuk memulai kelas, Anda dapat menjalankan Myclass () segera setelah definisi kelas untuk menghindari hal ini.
polvoazul
0

Jika Anda tidak perlu inisialisasi malas dari instance Singleton, maka yang berikut harus mudah dan aman:

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

Cara ini Aadalah singleton yang diinisialisasi pada modul import.

Serge Rogatch
sumber
0

Mungkin saya salah paham tentang pola tunggal tetapi solusi saya sederhana dan pragmatis (pythonic?). Kode ini memenuhi dua tujuan

  1. Buat instance dapat Foodiakses di mana saja (global).
  2. Hanya satu contoh dari yang Foobisa ada.

Ini kodenya.

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

Keluaran

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
sumber
0

Setelah berjuang dengan ini selama beberapa waktu saya akhirnya datang dengan yang berikut, sehingga objek konfigurasi hanya akan dimuat sekali, ketika dipanggil dari modul yang terpisah. Metaclass memungkinkan instance kelas global untuk disimpan dalam dict bawaan, yang saat ini tampaknya menjadi cara paling rapi untuk menyimpan program global yang tepat.

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
sumber
-1

Saya tidak ingat di mana saya menemukan solusi ini, tetapi saya menemukan ini sebagai yang paling 'elegan' dari sudut pandang non-Python-expert saya:

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

Kenapa saya suka ini? Tidak ada dekorator, tidak ada kelas meta, tidak ada pewarisan berganda ... dan jika Anda memutuskan Anda tidak ingin menjadi Singleton lagi, hapus saja __new__metodenya. Karena saya baru mengenal Python (dan OOP secara umum), saya berharap seseorang akan meluruskan mengapa ini merupakan pendekatan yang mengerikan?

2cynykyl
sumber
2
mengapa ini merupakan pendekatan yang mengerikan? ketika Anda ingin membuat kelas singleton lain, Anda harus menyalin & menempel __new__. Jangan ulangi dirimu sendiri .
GingerPlusPlus
Juga, mengapa barang baru Anda mengambil *argsdan **kwargs, dan kemudian tidak melakukan apa pun dengan mereka? Lulus mereka ke dalam dict.__new__cara ini: dict.__new__(cls, *args, **kwargs).
GingerPlusPlus
Ini akan memanggil __init__metode setiap kali kelas dipanggil. Jika __init__metode Anda benar-benar melakukan sesuatu, Anda akan melihat masalahnya. Setiap kali Anda melakukannya SomeSingleton(), status singleton Anda diatur ulang oleh __init__metode.
Aran-Fey
-2

Ini adalah cara yang saya pilih untuk menerapkan lajang:

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
hebat
sumber
1
Ini bukan sepenuhnya singleton karena memungkinkan lebih dari satu instance kelas ada. Peningkatan akan membuat kelas tidak dapat diinisialisasi, dan memiliki semua metode kelas bertindak pada atribut kelas
theheadofabroom
-2

Jawaban ini kemungkinan bukan yang Anda cari. Saya ingin seorang singleton dalam arti bahwa hanya objek itu yang memiliki identitas, sebagai perbandingan. Dalam kasus saya itu sedang digunakan sebagai Nilai Sentinel . Yang jawabannya sangat sederhana, buat objek apa pun mything = object()dan berdasarkan sifat python, hanya benda itu yang akan memiliki identitasnya.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration
ThorSummoner
sumber
Saya telah belajar bahwa modul sebenarnya dapat diimpor beberapa kali, dalam hal ini ini hanya singleton lokal, yang sebenarnya bukan singleton dalam kapasitas apa pun.
ThorSummoner
Bisakah Anda menguraikan bagaimana modul dapat diimpor beberapa kali? Satu-satunya waktu saya telah melihat bahwa ketika pengecualian terjadi saat modul dimuat, pengguna mungkin masih memuat modul nanti, namun efek sampingnya sudah terjadi, sehingga beberapa tindakan dapat dieksekusi untuk kedua kalinya.
sleblanc
Setelah modul telah dimuat penuh, saya tidak melihat cara untuk menjalankan modul ini lagi, selain dengan memaksa penerjemah untuk melakukannya menggunakan evalatau importlib.reload.
sleblanc
-3

Solusi ini menyebabkan beberapa polusi namespace pada level modul (tiga definisi bukan hanya satu), tetapi saya merasa mudah untuk mengikuti.

Saya ingin dapat menulis sesuatu seperti ini (inisialisasi malas), tetapi sayangnya kelas tidak tersedia dalam definisi tubuh mereka sendiri.

# 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

Karena itu tidak mungkin, kami dapat memecah inisialisasi dan contoh statis di

Inisialisasi yang bersemangat:

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

Inisialisasi malas:

Inisialisasi yang bersemangat:

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
sumber