C'è un vantaggio nel definire una classe all'interno di un'altra classe in Python?


106

Quello di cui sto parlando qui sono classi annidate. In sostanza, ho due classi che sto modellando. Una classe DownloadManager e una classe DownloadThread. L'ovvio concetto di OOP qui è la composizione. Tuttavia, composizione non significa necessariamente nidificazione, giusto?

Ho un codice che assomiglia a questo:

class DownloadThread:
    def foo(self):
        pass

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

Ma ora mi chiedo se ci sia una situazione in cui la nidificazione sarebbe migliore. Qualcosa di simile a:

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

Risposte:


122

Potresti volerlo fare quando la classe "interna" è una tantum, che non verrà mai utilizzata al di fuori della definizione della classe esterna. Ad esempio, per utilizzare una metaclasse, a volte è utile farlo

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

invece di definire una metaclasse separatamente, se la usi solo una volta.

L'unica altra volta che ho usato classi nidificate in questo modo, ho usato la classe esterna solo come spazio dei nomi per raggruppare insieme un gruppo di classi strettamente correlate:

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

    class cls2(object):
       ...

Quindi da un altro modulo, puoi importare Group e fare riferimento a questi come Group.cls1, Group.cls2 ecc. Tuttavia si potrebbe obiettare che puoi ottenere esattamente lo stesso (forse in un modo meno confuso) usando un modulo.


13
Direi che nel secondo caso saresti sicuramente servito meglio usando un modulo o un pacchetto, che sono progettati per essere spazi dei nomi.
Matthew Trevor

5
Questa è una risposta fantastica. Semplice, al punto, e menziona il metodo alternativo di utilizzare i moduli. +1
CornSmith

5
Per la cronaca, per coloro che si rifiutano ostinatamente o non possono organizzare il proprio codice in un pacchetto gerarchico e moduli appropriati, usare le classi come spazio dei nomi a volte può essere meglio che non usare nulla.
Acumenus

19

Non conosco Python, ma la tua domanda sembra molto generale. Ignorami se è specifico per Python.

La nidificazione delle classi riguarda l'ambito. Se pensi che una classe avrà senso solo nel contesto di un'altra, allora la prima è probabilmente un buon candidato per diventare una classe annidata.

È un modello comune rendere le classi helper private e annidate.


9
Solo per notare per la cronaca, il concetto di privateclassi non si applica così tanto in Python, ma si applica comunque un ambito di utilizzo implicito.
Acumenus

7

C'è un altro utilizzo per la classe nidificata, quando si desidera costruire classi ereditate le cui funzionalità avanzate sono incapsulate in una specifica classe nidificata.

Guarda questo esempio:

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)

Nota che nel costruttore di foo, la linea self.a = self.bar()costruirà un foo.barquando l'oggetto che viene costruito è effettivamente un foooggetto e un foo2.baroggetto quando l'oggetto che viene costruito è effettivamente un foo2oggetto.

Se la classe barfosse invece definita al di fuori della classe foo, così come la sua versione ereditata (che verrebbe chiamata bar2ad esempio), la definizione della nuova classe foo2sarebbe molto più dolorosa, perché il costituente di foo2avrebbe bisogno di avere la sua prima riga sostituita da self.a = bar2(), il che implica la riscrittura dell'intero costruttore.


6

Non c'è davvero alcun vantaggio nel farlo, tranne se hai a che fare con metaclassi.

la classe: la suite non è davvero quello che pensi che sia. È uno scopo strano e fa cose strane. Davvero non fa nemmeno una lezione! È solo un modo per raccogliere alcune variabili: il nome della classe, le basi, un piccolo dizionario di attributi e una metaclasse.

Il nome, il dizionario e le basi vengono tutti passati alla funzione che è la metaclasse, e poi viene assegnato alla variabile 'nome' nell'ambito in cui si trovava la classe: suite.

Quello che puoi guadagnare scherzando con le metaclassi, e in effetti annidando le classi all'interno delle tue classi standard, è più difficile da leggere il codice, più difficile da capire il codice e gli errori strani che sono terribilmente difficili da capire senza avere familiarità con il motivo della "classe" l'ambito è completamente diverso da qualsiasi altro ambito Python.


3
Non sono d'accordo: la nidificazione delle classi di eccezione è molto utile, perché gli utenti della tua classe possono verificare le tue eccezioni personalizzate senza dover eseguire importazioni disordinate. Ad esempioexcept myInstanceObj.CustomError, e:
RobM

2
@Jerub, perché è così male?
Robert Siemer

5

Potresti usare una classe come generatore di classi. Mi piace (in alcuni codici del polsino :)

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'''

Sento che quando avrai bisogno di questa funzionalità ti sarà molto chiaro. Se non hai bisogno di fare qualcosa di simile, probabilmente non è un buon caso d'uso.


1

No, composizione non significa nidificazione. Avrebbe senso avere una classe annidata se vuoi nasconderla maggiormente nello spazio dei nomi della classe esterna.

Comunque, non vedo alcun uso pratico per l'annidamento nel tuo caso. Renderebbe il codice più difficile da leggere (capire) e aumenterebbe anche il rientro che renderebbe le righe più corte e più inclini alla divisione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.