1

I want to make a for loop where I search all the class objects' self.name values to match a user input. Here's the code:

import json

class Planets(object):
    def __init__(self, name, radius, sgp, soi, atmo_height, moons=()):
        self.name = name
        self.radius = radius
        self.sgp = sgp
        self.soi = soi
        self.atmo_height = atmo_height
        self.moons = moons
    
    def __repr__(self):
        return  f"{self.name}, {self.radius} m, {self.sgp}, {self.soi}, {self.atmo_height}, Moons: {', '.join([moon['name'] for moon in self.moons])}"

class Moons(Planets):
    def __init__(self, name, radius, sgp, soi, atmo_height, apoapsis, periapsis):
        super().__init__(name, radius, sgp, soi, atmo_height)
        self.apoapsis = apoapsis
        self.periapsis = periapsis

    def __repr__(self):
        return  f"{self.name}, {self.radius} m, {self.sgp}, {self.soi}, {self.atmo_height}, {self.apoapsis}, {self.periapsis}"

with open('Planets.json', 'r') as p:
    planets = json.load(p)

with open('Moons.json', 'r') as m:
    moons = json.load(m)

# Planet Objects
moho = Planets(**planets["Planets"]["Moho"])
eve = Planets(**planets["Planets"]["Eve"])
kerbin = Planets(**planets["Planets"]["Kerbin"])
duna = Planets(**planets["Planets"]["Duna"])
dres = Planets(**planets["Planets"]["Dres"])
jool = Planets(**planets["Planets"]["Jool"])

#Moon Objects
gilly = Moons(**moons["Moons"]["Gilly"])
mun = Moons(**moons["Moons"]["Mun"])
minmus = Moons(**moons["Moons"]["Minmus"])
ike = Moons(**moons["Moons"]["Ike"])
laythe = Moons(**moons["Moons"]["Laythe"])
vall = Moons(**moons["Moons"]["Vall"])
tylo = Moons(**moons["Moons"]["Tylo"])
bop = Moons(**moons["Moons"]["Bop"])
pol = Moons(**moons["Moons"]["Pol"])

So, basically, the idea would be something like, user inputs the name "Jool", the for loop iterates through all objects in the Planets and Moons classes until they find an object with the value "Jool" for the "name" key.

How can I do this?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
AnFa
  • 47
  • 2
  • 9
  • Does this answer your question? [Printing all instances of a class](https://stackoverflow.com/questions/328851/printing-all-instances-of-a-class) – cryptolake Mar 18 '22 at 14:04
  • You'll want a container of objects, not individually named variables, just as you currently have for the data used to create the objects. (`planet['moho'] = planets["Planets"]["Moho"]`, etc.) – chepner Mar 18 '22 at 14:04
  • Unrelated, an instance of `Planets` represents exactly *one* planet; it should be named `Planet`. (Same for `Moons` being renamed to `Moon`.) – chepner Mar 18 '22 at 14:06

2 Answers2

1

You put all your Moons and Planets in a list.

for item in moons_planets_list:
    if item.name == user_input:
        #Do sth
Don CorliJoni
  • 223
  • 1
  • 8
1

Ordinarily, taking note of all instances of a class is not done automatically by Python or other languages.

However, you can do that by adding code in the __init__ method to register each instance as it is created in a "registry"of your choice. This "registry" is any data structure which will track your instances - a simple dictionary can be enough for most cases. You can then just keep this dictionary at the module level, or as a class attribute: an attribute that is present on the class and shared across all instances.

If one is not doing some "production level code" which would have to handle all sorts of random events and misuses (for example, if one creates a Planet with a name that is already registered), the code can be quite simple.

Since the code would be common across Planets and Moons, it is a case for using class inheritance, and share the common functionality, without the functionality getting in the way of what you want to handle in Planets

class Registerable:
    registry = {}
    def __init__(self, name):
        cls = self.__class__ # optional for readability
        cls.registry[name] = self
        super().__init__()
    

class Planets(Registerable):
    registry = {}
    def __init__(self, name, radius, sgp, soi, atmo_height, moons=()):
        super().__init__(name)
        self.name = name
        self.radius = radius
        self.sgp = sgp
        self.soi = soi
        self.atmo_height = atmo_height
        self.moons = moons

...
class Moons(Planeys):
    registry = {}  # each class have to declare its own registry, otherwise they are all shared with the base class

And then you can iterate on your instances using Planets.registry and Moons.registry as normal Python dictionaries.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 1
    The problem with this (and it might not be a huge problem) is that is messes up garbage collection, as every instance has a "hidden" reference to it keeping it alive. Better to explicitly maintain such a registry rather than having the class do it automatically. (Also, this implementation has a single registry shared by all subclasses of `Registerable`, which means keys might overlap between subclasses.) – chepner Mar 18 '22 at 14:14
  • 1
    @chepner: those are the parts where "If one is not doing some 'production level code' " goes. The leaking problem coulbd trivialy solved by a two-liner `__del__` method, or by using a weakref data structure, to automate registry, one could use a two-liner `__init_subclass__` . But we have to keep the simple cases simple for people learning and doing great things for themselves. The disclaimers being there, I think this is enough. – jsbueno Mar 18 '22 at 14:28
  • 1
    The simple case is to not embed this kind of object tracking in the class itself. – chepner Mar 18 '22 at 14:29
  • 1
    I mean: it would be simple enough for me to create a "registry" automatically - but given the level of the understanding of the OP, it is my opinion it is _far_ more important they learn about how it works - the `__init_subclass__` would be copy-paste boiler plate at this point. – jsbueno Mar 18 '22 at 14:30