看似独立的对象在初始化时相互干扰

Seemingly seperate objects interfere with eachother during initialization

我 100% 确定这是我的错。我敢打赌这是非常明显的事情。因此,当我说我在发布之前已经检查了我的代码一段时间时,请相信我。 我只需要某人的帮助来找到这些错误的原因,我相信我能够从那里解决它。也许只要用一双新鲜的眼睛检查代码就足以快速发现错误。

使用 python 3.9

问题

我第一次创建 Command 对象时,所有输出看起来都很好。 但是如果我创建第二个 Command 对象,第一个 Command 对象的输出会改变。

如果我例如运行这个:

test_command = Command("Power to comms", {REGULAR_RESOURCE_NAMES["power"]: Power(value=1)},
                               {REGULAR_RESOURCE_NAMES["comms"]: Comms(value=2)})

我在调试器中得到以下输出:

Power to comms [Command]
    Input resources: {'Power': <data_structure.Power object at 0x7f17e56e78b0>}
    Output resources: {'Comms': <data_structure.Comms object at 0x7f17e56e77c0>}

但如果我之后 运行 这个:

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)})

我在调试器中得到以下输出:

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>}

首先,我们可以看到两个Command对象中的Power对象共享同一个地址。这不是我想要的。所以这是第一个错误。 其次,为什么第一个命令的资源突然比以前多了input/output?

我在下面包含了相关代码:

我的代码

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)

其他资源,如 Comms、Navs、Data,与 Power class 完全相同,除了名称。

下面是在全局范围内实例化的:

REGULAR_RESOURCE_NAMES = {
    "comms": "Comms",
    "navs": "Navs",
    "data": "Data",
    "power": "Power"
}

当您像这样为 class 定义变量时

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)] = {}

这些变量被分配给 class 而不是一个实例。在您的 __init__ 函数中,您分配给 class 的变量而不是实例拥有的变量。如果您删除 class 级别定义,并按照弗兰克在下面提到的那样在 __init__ 中单独初始化它们,它应该可以工作。