¿Cómo sé cuándo convertir algo en una clase en lugar de una instancia de una clase? Por ejemplo:
Si estoy creando un juego espacial (no lo estoy) y quiero que el entorno sea el Universo, escribiría:
class Universe: def __init__(self): pass
Ahora digamos que me gustaría llenar mi Universo con Galaxias, así que haría una subclase Galaxy, con parámetros de tamaño y nombre:
class Galaxy(Universe): def __init__(self, size, name): super().__init__(self): self.size = size self.name = name pass
A continuación, me gustaría hacer Planetas. Si este fuera el caso, debería hacer una subclase de la subclase Galaxy, ¿no?
class Planet(Galaxy): def __init__(self, type, climate): super().__init__(self, size, name): self.type = type self.climate = climate pass
A partir de aquí me gustaría hacer los tipos Planet, o "familias". ¿Cuál es la mejor manera de hacer esto? ¿Otra subclase de una subclase? Cada planeta contendrá recursos que son exclusivos de esa familia de planetas. ¿Debería entonces hacer esto:
class PlanetType(Planet): resources = [] def __init__(self, resources): super().__init__(self, type, climate) self.resources = resources pass
Entonces, esto no es específico de Python, es más una idea central de OOP (Programación orientada a objetos)
¿Cómo sé cuándo convertir algo en una clase en lugar de una instancia de una clase?
Respuesta: En su ejemplo, el Universo es la superclase y desea crear la noción de Planetas. Si Planet hereda (subclase) de Universe, eso significa que tienen una relación "es". Voy a traer otro ejemplo popular aquí para ilustrar mi punto.
Digamos que tienes un coche de clase. Maserati sería una subclase (heredaría) de esta superclase porque un maserati ES un automóvil. Si quisiera representar a Wheels como una clase, no tendría sentido decir que Wheels ES un automóvil. Un coche TIENE ruedas. Entonces Wheels Class debería ser una variable de instancia en Car. Así que volvamos a tu ejemplo. Un Universo TIENE galaxias y una Galaxia TIENE planetas. Pero un planeta NO ES una galaxia y una galaxia NO ES un Universo.
Entonces, su primer problema aquí es tratar de hacer una jerarquía de clases de algo que fundamentalmente no es adecuado para ello. Cuando esté heredando de otra clase, pregúntese "¿Es una Subclase un tipo de Clase ?". En tu caso, "¿Es una Galaxia un tipo de Universo?" y "¿Es un planeta un tipo de galaxia?" ambos son claramente falsos.
En los casos que tiene aquí, todas las clases deben contener los tipos de nivel inferior; un Universe
puede contener una list
de Galaxy
, que a su vez contienen una list
de Planet
. No sé exactamente qué quiere decir con PlanetType
aquí (parece perfectamente posible que un Planet
tenga un atributo, posiblemente un enum.Enum
, que describe el tipo y una list
de recursos), así que dudo que sea un caso para un subclase, pero si realmente es útil (por ejemplo, necesita métodos que aparecen en todos los Planet
pero se comportan de manera diferente según el tipo), podría ser razonable declarar:
class GasGiant(Planet): ... class RockyPlanet(Planet): ...
o similares, ya que "¿Es un Gigante Gaseoso un tipo de Planeta?" de hecho tiene una respuesta de "Sí". Dicho esto, desconfiaría de cambiar el prototipo del inicializador incluso entonces; Busque el principio de sustitución de Liskov . Básicamente, nunca querrás estar en un escenario donde algo espera un Planet
(que podría ser realmente una de las subclases) pero solo funciona en algunas de las tres clases de Planet
, rompiéndose si pasa una diferente.
Esto no tiene sentido por las razones enumeradas en las otras respuestas (la del automóvil es la que me gusta).
Sin embargo, podría tener sentido tener una clase base, digamos SpaceObject (nombre falso a propósito, nombrar es difícil ). Esto tendría un campo de posición y tamaño . Esos son comunes a Galaxy, Planet, etc.
En general, cuando sospeche fuertemente que tendrá cosas en común entre las clases de objetos, cree una clase base y luego mueva gradualmente campos/métodos a ella, desde subclases especializadas a medida que se hace evidente el caso de reutilización. No se exceda en esto, pero puede ser útil.
(El universo es más difícil de precisar a este respecto).
Las subclases también podrían rastrear una clase "ubicada_en". Galaxia-> Universo.
Sobre planetas y subclases... Esa es una posibilidad, tener subclases (a través de la herencia OOP). Otra es usar la composición OOP en su lugar. El planeta tiene campos numéricos de tamaño, masa, temperatura y gravedad. Además, digamos un campo Atmosphere. Y campo central. Difícil de decir con los planetas, pero si estuvieras modelando perros, las jerarquías de clase son rápidamente una forma pésima de rastrear el tamaño, el tipo de cabello, los ladridos, etc. entre razas.