Y a-t-il un avantage à définir une classe dans une autre classe en Python?

106

Ce dont je parle ici, ce sont des classes imbriquées. Essentiellement, j'ai deux classes que je modélise. Une classe DownloadManager et une classe DownloadThread. Le concept évident de la POO ici est la composition. Cependant, composition ne signifie pas nécessairement nidification, non?

J'ai du code qui ressemble à ceci:

class DownloadThread:
    def foo(self):
        pass

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

Mais maintenant je me demande s'il y a une situation où la nidification serait meilleure. Quelque chose comme:

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

Réponses:

122

Vous voudrez peut-être faire cela lorsque la classe "interne" est une classe unique, qui ne sera jamais utilisée en dehors de la définition de la classe externe. Par exemple, pour utiliser une métaclasse, il est parfois pratique de le faire

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

au lieu de définir une métaclasse séparément, si vous ne l'utilisez qu'une seule fois.

La seule autre fois où j'ai utilisé des classes imbriquées comme ça, j'ai utilisé la classe externe uniquement comme espace de noms pour regrouper un tas de classes étroitement liées:

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

    class cls2(object):
       ...

Ensuite, à partir d'un autre module, vous pouvez importer Group et y faire référence en tant que Group.cls1, Group.cls2 etc. Cependant, on pourrait dire que vous pouvez accomplir exactement la même chose (peut-être d'une manière moins déroutante) en utilisant un module.

dF.
la source
13
Je dirais que dans le second cas, vous seriez -définiment- mieux servis en utilisant un module ou un package, qui sont conçus pour être des espaces de noms.
Matthew Trevor
5
C'est une réponse fantastique. Simple, au point, et mentionne la méthode alternative d'utilisation des modules. +1
CornSmith
5
Juste pour mémoire, pour ceux qui refusent obstinément ou ne peuvent pas organiser leur code dans un package hiérarchique et des modules appropriés, utiliser des classes comme espace de noms peut parfois être mieux que de ne rien utiliser.
Acumenus
19

Je ne connais pas Python, mais votre question semble très générale. Ignorez-moi si c'est spécifique à Python.

L'imbrication de classes est une question de portée. Si vous pensez qu'une classe n'aura de sens que dans le contexte d'une autre, alors la première est probablement un bon candidat pour devenir une classe imbriquée.

C'est un modèle courant pour faire des classes d'assistance comme des classes privées et imbriquées.

André Chalella
la source
9
Pour mémoire, le concept de privateclasses ne s'applique pas tellement en Python, mais une portée d'utilisation implicite s'applique toujours.
Acumenus
7

Il existe une autre utilisation de la classe imbriquée, lorsque l'on veut construire des classes héritées dont les fonctionnalités améliorées sont encapsulées dans une classe imbriquée spécifique.

Voir cet exemple:

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)

Notez que dans le constructeur de foo, la ligne self.a = self.bar()construira un foo.barlorsque l'objet en cours de construction est en fait un fooobjet, et un foo2.barobjet lorsque l'objet en cours de construction est en fait un foo2objet.

Si la classe barétait définie en dehors de la classe à la fooplace, ainsi que sa version héritée (qui serait appelée bar2par exemple), alors la définition de la nouvelle classe foo2serait beaucoup plus pénible, car le constructeur de foo2aurait besoin de remplacer sa première ligne par self.a = bar2(), ce qui implique de réécrire tout le constructeur.

Thomas
la source
6

Il n'y a vraiment aucun avantage à faire cela, sauf si vous avez affaire à des métaclasses.

la classe: la suite n'est vraiment pas ce que vous pensez. C'est une lunette étrange et cela fait des choses étranges. Cela ne fait même pas un cours! C'est juste un moyen de collecter certaines variables - le nom de la classe, les bases, un petit dictionnaire d'attributs et une métaclasse.

Le nom, le dictionnaire et les bases sont tous passés à la fonction qui est la métaclasse, puis il est affecté à la variable «nom» dans la portée où se trouvait la classe: suite.

Ce que vous pouvez gagner en jouant avec les métaclasses, et en imbriquant des classes dans vos classes standard de stock, est plus difficile à lire le code, plus difficile à comprendre le code et des erreurs étranges qui sont terriblement difficiles à comprendre sans être intimement familier avec pourquoi la `` classe '' scope est entièrement différente de toute autre portée python.

Jerub
la source
3
Pas d'accord: l'imbrication des classes d'exceptions est très utile, car les utilisateurs de votre classe peuvent vérifier vos exceptions personnalisées sans avoir à effectuer d'importations compliquées. Par exempleexcept myInstanceObj.CustomError, e:
RobM
2
@Jerub, pourquoi est-ce mauvais?
Robert Siemer
5

Vous pourriez utiliser une classe comme générateur de classe. Comme (dans certains codes off the cuff :)

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

J'ai l'impression que lorsque vous aurez besoin de cette fonctionnalité, ce sera très clair pour vous. Si vous n'avez pas besoin de faire quelque chose de similaire, ce n'est probablement pas un bon cas d'utilisation.

tim.tadh
la source
1

Non, la composition ne veut pas dire nidification. Il serait judicieux d'avoir une classe imbriquée si vous souhaitez la masquer davantage dans l'espace de noms de la classe externe.

Quoi qu'il en soit, je ne vois aucune utilité pratique pour l'imbrication dans votre cas. Cela rendrait le code plus difficile à lire (à comprendre) et cela augmenterait également l'indentation, ce qui rendrait les lignes plus courtes et plus sujettes à la division.

Cristian Ciupitu
la source