site banner

Small-Scale Question Sunday for April 21, 2024

Do you have a dumb question that you're kind of embarrassed to ask in the main thread? Is there something you're just not sure about?

This is your opportunity to ask questions. No question too simple or too silly.

Culture war topics are accepted, and proposals for a better intro post are appreciated.

1
Jump in the discussion.

No email address required.

I've been doing it like that, where they're all together and reference each other, it's just that then when Agent has 15 methods because some of them are experimental variations on each other or niche things I wanted to do to see what would happen, then I make another class for graphing scatter plots, and I've got a bunch of methods for (Make a world, then modifier the parameters according to X, then execute Y, then graph the results, then repeat that N times) that would be nice to stick in their own class somewhere, and then I've got a bunch of useful static methods that do stuff like load and save data to CSVs that would be nice to have in their own class for organization purposes. And if I just lay them out linearly (which I mostly have, with a few rare exceptions that definitely have 0 recursive dependencies and I actually have moved them to their own .py file) then I have literally 2000 lines of code I have to scroll up and down just to find the right class whenever I want to check to see what the name of the method I want to call is or something, and then scroll back down to find the spot I'm working on.

There's nothing like the partial class concept from C#, though I agree it would be really nice if there were.

You can kinda fake it by exploiting the heck of out inheritance, in a couple different ways, depending on what level of composition you're aiming to be able to do. If you want selective import of behaviors (and to avoid the diamond inheritance problem, mostly), you can do something like :

agentInfectionLogic,py:

wasInfected = False
countedInfections = 0

def incrementInfection(self):
    self.world.totalInfections += 1
    if self.wasInfected:
        self.world.redundantInfections += 1
    self.wasInfected = True
    self.countedInfections += 1

def infectedCount(self):
    return self.countedInfections

agentFileLogic,py:

def loadInfectionInfo(self):
    temploadInfections = 20
    for x in range(temploadInfections):
        self.incrementInfection()
    # do an actual file load here.

def saveInfectionInfo(self):
    tempfile = self.infectedCount
    # save an actual file here.

agent,py:

class Agent:
    from agentInfectionLogic import infectedCount, incrementInfection, countedInfections, wasInfected
    from agentFileLogic import saveInfectionInfo, loadInfectionInfo

    def __init__(self, ownerWorld):
        self.world = ownerWorld

And then calls like world.knownAgents[0].loadInfectionInfo() or world.infectRandomAgent() would work as normal, and you can even swap between different experimental forms by having from agentInfectionLogic import infectedCount, incrementInfection, countedInfections, wasInfected or from testAgentInfectionLogic import infectedCount, incrementInfection, countedInfections, wasInfected (or even a mix-and-match between the two).

Agent.py has to know about what's going on, but to everywhere else, anything imported into agent.py looks identical to as if it were coded into that file or class. Eventually this turns into a full module, where the __init__.py file holds the glue and then you have better names for your actual logic .pys, but when that makes sense depends a lot on the scale of your project.