¿Está bien tener varias clases en el mismo archivo en Python?

13

Recién estoy llegando al mundo Python después de años de Java y PHP. Si bien el lenguaje en sí es bastante sencillo, estoy luchando con algunos problemas "menores" que no puedo entender, y para los cuales no pude encontrar respuestas en los numerosos documentos y tutoriales que he leído hasta ahora. .

Para el practicante experimentado de Python, esta pregunta puede parecer una tontería, pero realmente quiero una respuesta para poder continuar con el lenguaje:

En Java y PHP ( aunque no sea estrictamente necesario ), se espera que escriba cada class en su propio archivo, con el nombre del archivo es el de class como una buena práctica.

Pero en Python, o al menos en los tutoriales que he comprobado, es correcto tener varias clases en el mismo archivo.

¿Se mantiene esta regla en el código de producción, listo para la implementación o se hace solo por brevedad en el código educativo?

    
pregunta Olivier Malki 04.01.2016 - 16:06

3 respuestas

11
  

¿Está bien tener varias clases en el mismo archivo en Python?

Sí. Tanto desde una perspectiva filosófica como desde la práctica.

En Python, los módulos son un espacio de nombres que existe una vez en la memoria.

Supongamos que tenemos la siguiente estructura de directorios hipotética, con una clase definida por archivo:

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

Todas estas clases están disponibles en el módulo collections y (de hecho, hay un total de 25) definidas en el módulo de biblioteca estándar en _collections_abc.py

Hay un par de problemas aquí que creo que hacen que _collections_abc.py sea superior a la estructura de directorio hipotética alternativa.

  • Estos archivos están ordenados alfabéticamente. Puede ordenarlos de otras maneras, pero no conozco una característica que ordene los archivos por dependencias semánticas. La fuente del módulo _collections_abc está organizada por dependencia.
  • En casos no patológicos, tanto los módulos como las definiciones de clase son singletons, y ocurren una vez en memoria. Habría una asignación biyectiva de módulos a clases, haciendo que los módulos sean redundantes.
  • El número creciente de archivos hace que sea menos conveniente leer de manera informal las clases (a menos que tenga un IDE que lo haga simple), lo que hace que sea menos accesible para las personas sin herramientas.

¿Se le impide dividir grupos de clases en módulos diferentes cuando lo encuentra conveniente desde una perspectiva organizativa y de espacio de nombres?

No.

Del Zen de Python , que refleja la filosofía y los principios bajo los cuales creció y evolucionó :

  

Los espacios de nombres son una gran idea, ¡hagamos más!

Pero tengamos en cuenta que también dice:

  

Plano es mejor que anidado.

Python es increíblemente limpio y fácil de leer. Te anima a leerlo. Poner cada clase separada en un archivo separado desalienta la lectura. Esto va en contra de la filosofía central de Python. Mire la estructura de la Biblioteca estándar , la gran mayoría de los módulos son módulos de un solo archivo, no paquetes . Le enviaría a usted que el código idiomático de Python está escrito en el mismo estilo que el CPython estándar lib.

Aquí está el código real del módulo de clase base abstracta . Me gusta usarlo como referencia para la denotación de varios tipos abstractos en el lenguaje.

¿Diría que cada una de estas clases debería requerir un archivo separado?

class Hashable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            try:
                for B in C.__mro__:
                    if "__hash__" in B.__dict__:
                        if B.__dict__["__hash__"]:
                            return True
                        break
            except AttributeError:
                # Old-style class
                if getattr(C, "__hash__", None):
                    return True
        return NotImplemented


class Iterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if _hasattr(C, "__iter__"):
                return True
        return NotImplemented

Iterable.register(str)


class Iterator(Iterable):

    @abstractmethod
    def next(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if _hasattr(C, "next") and _hasattr(C, "__iter__"):
                return True
        return NotImplemented


class Sized:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if _hasattr(C, "__len__"):
                return True
        return NotImplemented


class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented


class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            if _hasattr(C, "__call__"):
                return True
        return NotImplemented

Entonces, ¿deberían tener cada uno su propio archivo?

Espero que no.

Estos archivos no son solo código, son documentación sobre la semántica de Python.

Son tal vez de 10 a 20 líneas en promedio. ¿Por qué debería tener que ir a un archivo completamente separado para ver otras 10 líneas de código? Eso sería altamente impráctico. Además, habría importaciones casi idénticas en cada archivo, agregando más líneas de código redundantes.

Me resulta muy útil saber que hay un solo módulo donde puedo encontrar todas estas Clases básicas abstractas, en lugar de tener que revisar una lista de módulos. Verlos en contexto unos con otros me permite entenderlos mejor. Cuando veo que un Iterador es un Iterable, puedo revisar rápidamente en qué consiste un Iterable mirando hacia arriba.

A veces termino teniendo un par de clases muy cortas. Permanecen en el archivo, incluso si necesitan crecer con el tiempo. A veces los módulos maduros tienen más de 1000 líneas de código. Pero ctrl-f es fácil, y algunos IDE facilitan la visualización de los esquemas del archivo, por lo que no importa el tamaño del archivo, puede ir rápidamente a cualquier objeto o método que esté buscando.

Conclusión

Mi dirección, en el contexto de Python, es mantener las definiciones de clase relacionadas y semánticamente similares en el mismo archivo. Si el archivo crece tanto que se vuelve difícil de manejar, considere una reorganización.

    
respondido por el Aaron Hall 04.01.2016 - 17:10
4

Al estructurar su aplicación en Python, debe pensar en términos de paquetes y módulos.

Los módulos son sobre los archivos de los que estás hablando. Está bien tener un montón de clases dentro del mismo módulo. El objetivo es que todas las clases dentro del mismo módulo tengan el mismo propósito / lógica. Si el módulo es demasiado largo, piense en subdividirlo rediseñando su lógica.

No olvide leer de vez en cuando el Índice de Propuestas de mejora de Python .

    
respondido por el user132583 04.01.2016 - 17:03
2

La respuesta real a esto es general, y no depende del idioma que se usa: lo que debería estar en un archivo no depende principalmente de cuántas clases define. Depende de la conectividad lógica y de la complejidad. Periodo.

Por lo tanto, si tiene algunas clases muy pequeñas que están altamente interconectadas, deben agruparse en el mismo archivo. Debería dividir una clase si no está conectada estrechamente a otra clase o si es demasiado compleja para incluirla en otra clase.

Dicho esto, la regla de una clase por archivo suele ser una buena heurística. Sin embargo, hay excepciones importantes: una pequeña clase de ayuda que en realidad es solo el detalle de implementación de su única clase de usuario generalmente debe incluirse en el archivo de esa clase de usuario. Del mismo modo, si tiene tres clases vector2 , vector3 y vector4 , es probable que haya pocas razones para implementarlas en archivos separados.

    
respondido por el cmaster 04.01.2016 - 19:25

Lea otras preguntas en las etiquetas