¿Existe algún beneficio al definir una clase dentro de otra clase en Python?

106

De lo que estoy hablando aquí son clases anidadas. Esencialmente, tengo dos clases que estoy modelando. Una clase DownloadManager y una clase DownloadThread. El concepto obvio de OOP aquí es la composición. Sin embargo, la composición no significa necesariamente anidar, ¿verdad?

Tengo un código que se parece a esto:

class DownloadThread:
    def foo(self):
        pass

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

Pero ahora me pregunto si existe una situación en la que anidar sería mejor. Algo como:

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

Respuestas:

122

Es posible que desee hacer esto cuando la clase "interna" sea única, que nunca se utilizará fuera de la definición de la clase externa. Por ejemplo, para usar una metaclase, a veces es útil hacer

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

en lugar de definir una metaclase por separado, si solo la usa una vez.

La única otra vez que usé clases anidadas como esa, usé la clase externa solo como un espacio de nombres para agrupar un montón de clases estrechamente relacionadas:

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

    class cls2(object):
       ...

Luego, desde otro módulo, puede importar Group y referirse a ellos como Group.cls1, Group.cls2, etc. Sin embargo, se podría argumentar que puede lograr exactamente lo mismo (quizás de una manera menos confusa) usando un módulo.

dF.
fuente
13
Yo diría que en el segundo caso, definitivamente, estaría mejor servido usando un módulo o paquete, que están diseñados para ser espacios de nombres.
Matthew Trevor
5
Esta es una respuesta fantástica. Simple, al grano, y menciona el método alternativo de usar módulos. +1
CornSmith
5
Solo para que conste, para aquellos que se niegan obstinadamente o no pueden organizar su código en un paquete jerárquico y módulos adecuados, el uso de clases como espacio de nombres a veces puede ser mejor que no usar nada.
Acumenus
19

No conozco Python, pero tu pregunta parece muy general. Ignórame si es específico de Python.

El anidamiento de clases tiene que ver con el alcance. Si cree que una clase solo tendrá sentido en el contexto de otra, entonces la primera probablemente sea una buena candidata para convertirse en una clase anidada.

Es un patrón común hacer clases auxiliares como clases privadas anidadas.

André Chalella
fuente
9
Solo para que quede constancia, el concepto de privateclases no se aplica tanto en Python, pero definitivamente se aplica un ámbito de uso implícito.
Acumenus
7

Existe otro uso para la clase anidada, cuando se quiere construir clases heredadas cuyas funcionalidades mejoradas están encapsuladas en una clase anidada específica.

Vea este ejemplo:

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)

Tenga en cuenta que en el constructor de foo, la línea self.a = self.bar()construirá a foo.barcuando el objeto que se está construyendo es en realidad un fooobjeto, y un foo2.barobjeto cuando el objeto que se está construyendo es en realidad un foo2objeto.

Si la clase barse definió fuera de la clase en su foolugar, así como su versión heredada (que se llamaría bar2por ejemplo), entonces definir la nueva clase foo2sería mucho más doloroso, porque el constructor de foo2necesitaría tener su primera línea reemplazada por self.a = bar2(), lo que implica reescribir todo el constructor.

Thomas
fuente
6

Realmente no hay ningún beneficio en hacer esto, excepto si se trata de metaclases.

la clase: suite realmente no es lo que crees que es. Es un alcance extraño y hace cosas extrañas. ¡Realmente ni siquiera hace una clase! Es solo una forma de recopilar algunas variables: el nombre de la clase, las bases, un pequeño diccionario de atributos y una metaclase.

El nombre, el diccionario y las bases se pasan a la función que es la metaclase, y luego se asigna a la variable 'nombre' en el ámbito donde estaba la clase: suite.

Lo que puede ganar jugando con metaclases, y de hecho anidando clases dentro de sus clases estándar de valores, es más difícil de leer el código, más difícil de entender el código y errores extraños que son terriblemente difíciles de entender sin estar íntimamente familiarizado con el por qué de la 'clase' El alcance es completamente diferente a cualquier otro alcance de Python.

Jerub
fuente
3
En desacuerdo: Anidar clases de excepción es muy útil, porque los usuarios de su clase pueden verificar sus excepciones personalizadas sin tener que realizar importaciones desordenadas. Por ejemploexcept myInstanceObj.CustomError, e:
RobM
2
@Jerub, ¿por qué es tan malo?
Robert Siemer
5

Podrías estar usando una clase como generador de clases. Como (en algunos códigos improvisados ​​:)

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

Siento que cuando necesites esta funcionalidad te quedará muy claro. Si no necesita hacer algo similar, probablemente no sea un buen caso de uso.

tim.tadh
fuente
1

No, la composición no significa anidar. Tendría sentido tener una clase anidada si desea ocultarla más en el espacio de nombres de la clase externa.

De todos modos, no veo ningún uso práctico para anidar en su caso. Haría que el código fuera más difícil de leer (entender) y también aumentaría la sangría, lo que haría que las líneas fueran más cortas y más propensas a dividirse.

Cristian Ciupitu
fuente