¿Está bien tener varias clases en el mismo archivo en Python?
Si. Tanto desde una perspectiva filosófica como 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 directorio 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 collections
módulo y (de hecho, hay 25 en total) definidas en el módulo de biblioteca estándar en_collections_abc.py
Hay un par de problemas aquí que creo que hacen _collections_abc.py
que la estructura de directorio hipotética sea superior a la alternativa.
- Estos archivos están ordenados alfabéticamente. Puede ordenarlos de otras maneras, pero no conozco una característica que clasifique 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, que ocurren una vez en la memoria. Habría un mapeo biyectivo de módulos en clases, haciendo que los módulos sean redundantes.
- El creciente número de archivos hace que sea menos conveniente leer las clases de manera informal (a menos que tenga un IDE que lo simplifique), lo que lo hace menos accesible para las personas sin herramientas.
¿Se le impide dividir grupos de clases en diferentes módulos cuando lo considera deseable desde una perspectiva organizacional y de espacios 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 de eso!
Pero tengamos en cuenta que también dice:
Plano es mejor que anidado.
Python es increíblemente limpio y fácil de leer. Te alienta 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 diría que el código idiomático de Python está escrito en el mismo estilo que la biblioteca estándar de CPython.
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 cada uno tener su propio archivo?
Espero que no.
Estos archivos no son solo código, son documentación sobre la semántica de Python.
Son quizás 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 muy poco práctico. Además, habría importaciones casi idénticas en cada archivo, añadiendo líneas de código más redundantes.
Me resulta bastante útil saber que hay un solo módulo donde puedo encontrar todas estas clases base abstractas, en lugar de tener que revisar una lista de módulos. Verlos en contexto entre ellos me permite comprenderlos mejor. Cuando veo que un Iterador es Iterable, puedo revisar rápidamente en qué consiste un Iterable al mirar 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 contornos del archivo, por lo que no importa qué tan grande sea el archivo, puede ir rápidamente al objeto o método que esté buscando.
Conclusión
Mi dirección, en el contexto de Python, es preferir mantener 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.
class SomeException extends \Exception {}
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á 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 sirvan para el mismo propósito / lógica. Si el módulo dura demasiado, piense en subdividirlo rediseñando su lógica.
No olvide leer de vez en cuando sobre el índice de propuestas de mejora de Python .
fuente
La respuesta real a esto es general y no depende del lenguaje que se use: lo que debe estar en un archivo no depende principalmente de cuántas clases define. Depende de la conexión lógica y de la complejidad. Período.
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á estrechamente conectada a otra clase o si es demasiado compleja para ser incluida 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 auxiliar que es realmente solo el detalle de implementación de su única clase de usuario generalmente debe agruparse en el archivo de esa clase de usuario. Del mismo modo, si tiene tres clases
vector2
,vector3
yvector4
, es probable que haya pocas razones para implementarlas en archivos separados.fuente