0

I'm making a framework for an agent based model and I have a class called agents. Since there's going to be thousands of agents in the simulation, I want to use the __ slots__, which will replace the default __ dict__ and reduce memory consumption. I have my code structured so that it gets the agent's parameter from a data table and I want to assign the values stored in the table to an attribute with the table's header name.

If the following is the data table,

|  agent_name  |  location  |    attr1    |    attr2   |
|--------------|------------|-------------|------------|
|  agent smith |    NY      |  some value | some value |
|  Neo         |    NY      |  some value | some value |
| Morpheus     |    Zion    |  some value | some value |

then if I create the 3 agents, I want all of them to have a .agent_name, .location, .attr1, and .attr2 attributes.

# illustration of what I want
header_of_table = ["agent_name", "location", "attr1", "attr2"]

class agent:
    __slots__ = header_of_table
    def __init__(self, values_in_row):
        # what I ideally want, I know this syntax doesnt work, but this is to get the idea across
        for slotted_attribute, value in zip(self.__slots__, values_in_row):
            self.slotted_attribute = value

I know you can use the .eval method inside the for loop, but I don't find that clean and I feel like there has to be a better way. I'm wondering if there's a way to iterate over the slots and assign value to each attributes.

1 Answers1

2

A few pointers:

  • When using __slots__ you need to inherit from object. Python 2
  • A class should always be capitalized.
  • You can use setattr as Michael wrote in a comment.
  • It's called __slots__ not __slot__.

Here's a working example based on your code:

# illustration of what I want
header_of_table = ["agent_name", "location", "attr1", "attr2"]

class Agent:
    __slots__ = header_of_table
    def __init__(self, values_in_row):
        for i, slot in enumerate(self.__slots__):
            self.__setattr__(slot, values_in_row[i])

agent = Agent(["foo", "bar", "yellow", "green"])
print(agent.agent_name, agent.location, agent.attr1, agent.attr2)

>>> foo bar yellow green

Edit for comment: If I understand you correctly then I would do something like this to avoid polluting the global scope.

slotheaders.py:

class SlotHeaders:
    __slots__ = ["agent_name", "location", "attr1", "attr2"]

agent.py:

from slotheaders import SlotHeaders

class Agent(SlotHeaders):
    def __init__(self, values_in_row):
        for i, slot in enumerate(self.__slots__):
            self.__setattr__(slot, values_in_row[i])

agent = Agent(["foo", "bar", "yellow", "green"])
print(agent.agent_name, agent.location, agent.attr1, agent.attr2)
Mandera
  • 2,647
  • 3
  • 21
  • 26
  • 1
    Inheriting from `object` is only a thing in Python 2. In Python 3, that happens automatically, you don't need to specify it unless you need backwards compatibility with Python 2 (and you probably don't, by now). – Blckknght Jun 14 '20 at 05:54
  • I copied pasted from an editor so I didn't notice that I forgot to capitalize the class name, thank you for pointing that out, I'll make sure to correct it. – Kukai Nakahata Jun 14 '20 at 06:33
  • @Mandera I'm satisfied with the current answer, but the `header_of_row` is in the global scope and if I wanted to restrict the scope, then I would need to nest the Agent class inside a function that can store the `header_of_row`. This meta function's purpose is solely to assign header_of_row to `__slots__`. This is the only thing that I can come up with to restrict the scope, is there a better way to assign a variable to `__slots__`? Or should I create a new question because it's different from the original question? – Kukai Nakahata Jun 14 '20 at 10:29
  • @KukaiNakahata Edited my answer, what do you think? – Mandera Jun 14 '20 at 11:41
  • 1
    @Mandera that is an elegant solution and it’s better than wrapping it in a function. Thank you, it’s a great answer. – Kukai Nakahata Jun 14 '20 at 21:36