I'm 100% sure this is my fault. And I bet it's something really obvious. So believe me when I say I've looked over my code for a while before posting. I just need some help from someone to find the cause of these errors, and I'm sure I'll be able to solve it from there. Maybe just a fresh pair of eyes looking over the code is enough to spot the error quickly.
Using python 3.9
The problem
The first time I create a Command object, all the outputs seem fine. But if I create a second Command object, the output of the first Command object change.
If I for example run this:
test_command = Command("Power to comms", {REGULAR_RESOURCE_NAMES["power"]: Power(value=1)},
{REGULAR_RESOURCE_NAMES["comms"]: Comms(value=2)})
I get the following output i the debugger:
Power to comms [Command]
Input resources: {'Power': <data_structure.Power object at 0x7f17e56e78b0>}
Output resources: {'Comms': <data_structure.Comms object at 0x7f17e56e77c0>}
But if I afterwards run this:
test_command2 = Command("Comms and power to navs", {REGULAR_RESOURCE_NAMES["comms"]: Comms(value=2),
REGULAR_RESOURCE_NAMES["power"]: Power(value=1)},
{REGULAR_RESOURCE_NAMES["navs"]: Navs(value=5)})
I get the following output in the debugger:
Power to comms [Command]
Input resources: {'Power': <data_structure.Power object at 0x7f17e57135b0>, 'Comms': <data_structure.Comms object at 0x7f17e5713610>}
Output resources: {'Comms': <data_structure.Comms object at 0x7f17e56e77c0>, 'Navs': <data_structure.Navs object at 0x7f17e56e78b0>}
Comms and power to navs [Command]
Input resources: {'Power': <data_structure.Power object at 0x7f17e57135b0>, 'Comms': <data_structure.Comms object at 0x7f17e5713610>}
Output resources: {'Comms': <data_structure.Comms object at 0x7f17e56e77c0>, 'Navs': <data_structure.Navs object at 0x7f17e56e78b0>}
First of all, we can see that the Power object in both Command objects share the same address. This is not what I have intended. So that's the first error. Secondly, why does the first command suddenly have more input/output resources than before?
I've included the relevant code below:
My code
class Command:
"""
Contains a ratio for exchanging input resources into output resources
"""
name: str
input_resources: dict[str, type(Resource)] = {}
output_resources: dict[str, type(Resource)] = {}
def __init__(self, name: str,
input_resources: dict[str, type(Resource)], output_resources: dict[str, type(Resource)]):
self.name: str = name
for input_resource_name, input_resource in input_resources.items():
self.input_resources[input_resource_name]: type(Resource) = input_resource.copy()
for output_resource_name, output_resource in output_resources.items():
self.output_resources[output_resource_name]: type(Resource) = output_resource.copy()
def __str__(self) -> str:
output: str = self.name " [Command]" "\n\t" \
"Input resources: " str(self.input_resources) "\n\t" \
"Output resources: " str(self.output_resources) "\n"
return remove_trailing_newlines(output)
def copy(self) -> type(__name__):
input_resources_copy: dict[str, type(Resource)] = {}
for input_resource_name, input_resource in self.input_resources.items():
input_resources_copy[input_resource_name]: type(Resource) = input_resource.copy()
output_resources_copy: dict[str, type(Resource)] = {}
for output_resource_name, output_resource in self.output_resources.items():
output_resources_copy[output_resource_name]: type(Resource) = output_resource.copy()
return Command(self.name, input_resources_copy, output_resources_copy)
class Resource:
"""
The base class for all resource types, such as Comms, Navs, Data, Heat, Drift, Thrust
"""
name: str
value: int
min_value: int
max_value: int
def __init__(self, name: str, value: int = 0, min_value: int = 0, max_value: int = 999):
self.name: str = name
self.value: int = value
self.min_value: int = min_value
self.max_value: int = max_value
def is_valid_value(self) -> bool:
"""
Checks if the numerical validity of the resource value based on init parameters
"""
return self.min_value <= self.value <= self.max_value
class Power(Resource):
"""
This subclass of Resource, contains variables and methods specific to this particular in-game resource.
This is what's considered a regular resource.
"""
def __init__(self, value: int = 0):
super().__init__(REGULAR_RESOURCE_NAMES["power"], value=value)
def next_turn(self) -> bool:
return self.is_valid_value()
def is_valid_end_of_route(self) -> bool:
return self.is_valid_value()
def copy(self) -> type(__name__):
return Power(value=self.value)
The other resources like Comms, Navs, Data, are exactly the same as the Power class, except for the name.
And the following is instantiated in the global scope:
REGULAR_RESOURCE_NAMES = {
"comms": "Comms",
"navs": "Navs",
"data": "Data",
"power": "Power"
}
CodePudding user response:
When you define variables for a class like this
class Command:
"""
Contains a ratio for exchanging input resources into output resources
"""
name: str
input_resources: dict[str, type(Resource)] = {}
output_resources: dict[str, type(Resource)] = {}
these variables are assigned to the class rather than an instance. In your __init__
function, you assign to the class's variable rather than a variable owned by the instance. If you remove the class level definitions, and initialize these separately in the __init__
as Frank mentions below, it should work.