Apakah ada manfaat untuk mendefinisikan kelas di dalam kelas lain dengan Python?

106

Yang saya bicarakan di sini adalah kelas bersarang. Pada dasarnya, saya memiliki dua kelas yang saya modelkan. Kelas DownloadManager dan kelas DownloadThread. Konsep OOP yang jelas di sini adalah komposisi. Namun, komposisi tidak selalu berarti bersarang, bukan?

Saya memiliki kode yang terlihat seperti ini:

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

Tapi sekarang saya bertanya-tanya apakah ada situasi di mana bersarang akan lebih baik. Sesuatu seperti:

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())
fuentesjr
sumber

Jawaban:

121

Anda mungkin ingin melakukan ini ketika kelas "dalam" adalah satu kali, yang tidak akan pernah digunakan di luar definisi kelas luar. Misalnya untuk menggunakan metaclass, terkadang berguna untuk dilakukan

class Foo(object):
    class __metaclass__(type):
        .... 

alih-alih menentukan metaclass secara terpisah, jika Anda hanya menggunakannya sekali.

Satu-satunya waktu lain saya menggunakan kelas bersarang seperti itu, saya menggunakan kelas luar hanya sebagai namespace untuk mengelompokkan sekelompok kelas yang terkait erat bersama-sama:

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

Kemudian dari modul lain, Anda dapat mengimpor Grup dan merujuk ini sebagai Group.cls1, Group.cls2 dll. Namun orang mungkin berpendapat bahwa Anda dapat mencapai hal yang sama persis (mungkin dengan cara yang tidak terlalu membingungkan) dengan menggunakan modul.

dF.
sumber
13
Saya -akan- berpendapat bahwa dalam kasus kedua, Anda akan -definitely- lebih baik dilayani menggunakan modul atau paket, yang dirancang untuk namespace.
Matthew Trevor
5
Ini jawaban yang luar biasa. Sederhana, to the point, dan menyebutkan metode alternatif dalam menggunakan modul. +1
CornSmith
5
Sekadar catatan, bagi mereka yang dengan keras kepala menolak atau tidak dapat mengatur kode mereka ke dalam paket hierarki dan modul yang sesuai, menggunakan kelas sebagai namespace terkadang lebih baik daripada tidak menggunakan apa pun.
Acumenus
19

Saya tidak tahu Python, tapi pertanyaan Anda sepertinya sangat umum. Abaikan saya jika itu khusus untuk Python.

Kelas bersarang adalah tentang ruang lingkup. Jika Anda berpikir bahwa satu kelas hanya akan masuk akal dalam konteks kelas lain, maka kelas yang pertama mungkin adalah kandidat yang baik untuk menjadi kelas bertingkat.

Ini adalah pola umum yang membuat kelas pembantu sebagai kelas privat, kelas bersarang.

André Chalella
sumber
9
Sebagai catatan sebagai catatan, konsep privatekelas tidak banyak diterapkan di Python, tetapi cakupan penggunaan tersirat masih berlaku.
Acumenus
7

Ada penggunaan lain untuk kelas bersarang, ketika seseorang ingin membangun kelas yang diwariskan yang fungsionalitasnya ditingkatkan dienkapsulasi dalam kelas bersarang tertentu.

Lihat contoh ini:

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

Perhatikan bahwa dalam konstruktor dari foo, garis self.a = self.bar()akan membangun foo.barketika objek yang sedang dibangun sebenarnya adalah fooobjek, dan foo2.barobjek saat objek yang sedang dibangun sebenarnya adalah foo2objek.

Jika kelas bardidefinisikan di luar kelas foosebagai gantinya, serta versi yang diwariskan (yang akan dipanggil bar2sebagai contoh), maka mendefinisikan kelas baru foo2akan jauh lebih menyakitkan, karena penyusun dari foo2harus mengganti baris pertamanya dengan self.a = bar2(), yang menyiratkan penulisan ulang seluruh konstruktor.

Thomas
sumber
6

Tidak ada manfaatnya melakukan ini, kecuali jika Anda berurusan dengan metaclass.

kelas: suite sebenarnya tidak seperti yang Anda pikirkan. Ini adalah ruang lingkup yang aneh, dan melakukan hal-hal aneh. Itu benar-benar bahkan tidak membuat kelas! Ini hanyalah cara untuk mengumpulkan beberapa variabel - nama kelas, basis, kamus kecil atribut, dan metaclass.

Nama, kamus, dan basis semuanya diteruskan ke fungsi yang merupakan metaclass, dan kemudian ditugaskan ke variabel 'name' dalam ruang lingkup di mana kelas: suite itu.

Apa yang bisa Anda peroleh dengan mengotak-atik metaclass, dan memang dengan menumpuk kelas dalam kelas standar stok Anda, lebih sulit untuk membaca kode, lebih sulit untuk memahami kode, dan kesalahan aneh yang sangat sulit untuk dipahami tanpa terlalu akrab dengan mengapa 'kelas' lingkup sama sekali berbeda dengan lingkup python lainnya.

Jerub
sumber
3
Tidak setuju: Kelas pengecualian bersarang sangat berguna, karena pengguna kelas Anda dapat memeriksa pengecualian khusus Anda tanpa harus melakukan impor yang berantakan. Misalnyaexcept myInstanceObj.CustomError, e:
RobM
2
@ Jerub, kenapa itu buruk?
Robert Siemer
5

Anda bisa menggunakan kelas sebagai generator kelas. Seperti (di beberapa kode manset :)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

Saya merasa ketika Anda membutuhkan fungsi ini, itu akan sangat jelas bagi Anda. Jika Anda tidak perlu melakukan sesuatu yang serupa daripada itu mungkin bukan kasus penggunaan yang baik.

tim.tadh
sumber
1

Tidak, komposisi tidak berarti bersarang. Masuk akal untuk memiliki kelas bersarang jika Anda ingin lebih menyembunyikannya di ruang nama kelas luar.

Bagaimanapun, saya tidak melihat penggunaan praktis untuk bersarang dalam kasus Anda. Ini akan membuat kode lebih sulit untuk dibaca (dipahami) dan itu juga akan meningkatkan lekukan yang akan membuat garis lebih pendek dan lebih mudah terpecah.

Cristian Ciupitu
sumber