追加时多维数组的所有值都会更改 - Python

All values of multi-dimensional array change on append - Python

我遇到一个问题,我的“all_possible_cuts”数组在我向其附加另一个剪切时发生了变化。

我的代码中有问题的具体部分是:

    def append_to_all_possible_cuts(next_cuts):
        """Appends the next possible way to cut the material to the array \"all_possible_cuts\" """
        Order.all_possible_cuts.append(next_cuts[:])
        previous_cuts = next_cuts
        return(previous_cuts)
...
Print(Order.all_possible_cuts)

输出:[[[19.125, 0], [16.125, 0]], [[19.125, 0], [16.125, 0]], ...[[19.125, 0], [16.125, 0]]]

预期输出:[[19.125, 6], [16.125, 1]], [[19.125, 6], [16.125, 0]], [[19.125, 5], [16.125, 3]],...[[19.125, 0], [16.125, 0]]

我发现有人遇到同样问题的文章,但我尝试了他们的解决方案,但似乎没有帮助。

输入输入 144、2、19、15、16、30

这是我的完整代码:

class Order:
    purchase_lines = []
    all_possible_cuts = []

    def get_order_information():
        """Gathers all the starting bar length and cutting information from the order and sorts from largest cut to smallest cut."""
        Order.get_starting_material_length()
        Order.get_purchase_lines()
        Order.purchase_lines = sorted(Order.purchase_lines, reverse=True)

    def get_starting_material_length():
        """Gathers the length of the stock bar for the order.  Returns starting lenght of the stock material."""
        starting_material_length = int(input("What is the length of the stock (in inches)? "))
        Order.starting_material_length = starting_material_length
    def get_purchase_lines():
        """Gathers all the cut lengths and the quantities needed for each cut length.  Adds a tolerance of 0.125 inches to each cutting length.  Appends length + quantity needed to """
        tolerance = 0.125
        num_of_cut_lengths = int(input("How many different cut lengths are there? "))
        for cut_length in range(num_of_cut_lengths):
            length = float(input("What is the length of  the cut (in inches)? "))
            qty = int(input("How many " + str(length) + " in. cuts are needed? "))
            Order.purchase_lines.append([(length + tolerance), qty])

    def generate_all_possible_cuts():
        """Generates an array of every possible way to cut a single piece of stock material"""
        first_possible_cut = Order.generate_first_possible_cut(Order.purchase_lines)
        another_cut_possible = Order.determine_if_another_cut_possible(first_possible_cut)
        previous_cuts = first_possible_cut
        while another_cut_possible:
            next_cuts = Order.generate_next_possible_cut(previous_cuts)
            previous_cuts = Order.append_to_all_possible_cuts(next_cuts)
            another_cut_possible = Order.determine_if_another_cut_possible(previous_cuts)

    def generate_first_possible_cut(purchase_lines):
        """Using the cut lengths and quantities need, cutting the most pieces possible out of the stock material, starting with the first cut length entered, until the material has remainder of 0 or all cutting lengths have been attempted."""
        possible_cut_length_and_qtys = []
        remaining_material_length = Order.starting_material_length
        for line in purchase_lines:
            possible_cut_length = line[0]
            max_qty = line[1]
            possible_qty = int(remaining_material_length // possible_cut_length)
            if possible_qty >= 0 and remaining_material_length - (possible_cut_length * possible_qty) >= 0:
                if possible_qty > max_qty:
                    possible_cut_length_and_qtys.append([possible_cut_length, max_qty])
                    remaining_material_length -= possible_cut_length * max_qty
                else:
                    possible_cut_length_and_qtys.append([possible_cut_length, possible_qty])
                    remaining_material_length -= possible_cut_length * possible_qty
        Order.remaining_material_length = remaining_material_length
        return(possible_cut_length_and_qtys)

    def determine_if_another_cut_possible(previous_cuts):
        """Determines if another cut is possible.  Returns True if possible, False if not possible."""
        for cut in previous_cuts:
            cut_qty = cut[1]
            if cut_qty > 0:
                return(True)
                break
        else:
            return(False)
    def generate_next_possible_cut(previous_cuts):
        """Using the previous counts, generates the next possible cut."""
        index_of_lowered_digit, lowered_cuts = Order.lower_smallest_digit_possible(previous_cuts)
        Order.increase_remaining_material_length(index_of_lowered_digit)
        next_cuts = Order.use_remaining_stock(index_of_lowered_digit, lowered_cuts)
        return(next_cuts)

    def append_to_all_possible_cuts(next_cuts):
        """Appends the next possible way to cut the material to the array \"all_possible_cuts\" """
        Order.all_possible_cuts.append(next_cuts[:])
        previous_cuts = next_cuts
        return(previous_cuts)

    def lower_smallest_digit_possible(previous_cuts):
        """Using the previous counts, generates the next possible cut."""
        current_cut_index = len(previous_cuts) - 1
        while current_cut_index >= 0:
            if previous_cuts[current_cut_index][1] > 0:
                previous_cuts[current_cut_index][1] -= 1
                break
            else:
                current_cut_index -= 1
        return(current_cut_index, previous_cuts)

    def increase_remaining_material_length(index_of_lowered_digit):
        """Using the previous counts, generates the next possible cut."""
        Order.remaining_material_length += Order.purchase_lines[index_of_lowered_digit][0]


    def use_remaining_stock(index_of_lowered_digit, lowered_cuts):
        """Using the previous counts, generates the next possible cut."""
        current_index = index_of_lowered_digit + 1
        while current_index < len(lowered_cuts):
            if Order.remaining_material_length >= lowered_cuts[current_index][0]:
                qty = int(Order.remaining_material_length // lowered_cuts[current_index][0])
                lowered_cuts[current_index][1] += qty
                Order.remaining_material_length -= lowered_cuts[current_index][0] * qty
            current_index += 1
        return(lowered_cuts)
Order.get_order_information()
Order.generate_all_possible_cuts()

看起来您正在重用数组(a.k.a。Python 列表),而您打算复制它们。

为了快速证明这一点,我将 return 数组代码中的每个 return 语句更改为 return copy.deepcopy(a),其中 a是数组的名称。更改后的代码随后产生了预期的输出。

完整更改的代码包含在这个答案的底部。

为了帮助解释一般问题,请考虑以下示例代码:

a = [0, 1, 2, 3, 4]
b = [a, a, a, a]
print(b)
a[0] = 123
print(b)

示例代码的输出为:

[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
[[123, 1, 2, 3, 4], [123, 1, 2, 3, 4], [123, 1, 2, 3, 4], [123, 1, 2, 3, 4]]

原因是因为 a 数组从未被复制到 bb 数组仅获得 name a 的 4 个副本,而不是名为 a 的整个数组。更正式地说,这意味着 a 被使用 'by reference'.

完全更改的代码:

import copy

class Order:
    purchase_lines = []
    all_possible_cuts = []

    def get_order_information():
        """Gathers all the starting bar length and cutting information from the order and sorts from largest cut to smallest cut."""
        Order.get_starting_material_length()
        Order.get_purchase_lines()
        Order.purchase_lines = sorted(Order.purchase_lines, reverse=True)

    def get_starting_material_length():
        """Gathers the length of the stock bar for the order.  Returns starting lenght of the stock material."""
        starting_material_length = int(input("What is the length of the stock (in inches)? "))
        Order.starting_material_length = starting_material_length
    def get_purchase_lines():
        """Gathers all the cut lengths and the quantities needed for each cut length.  Adds a tolerance of 0.125 inches to each cutting length.  Appends length + quantity needed to """
        tolerance = 0.125
        num_of_cut_lengths = int(input("How many different cut lengths are there? "))
        for cut_length in range(num_of_cut_lengths):
            length = float(input("What is the length of  the cut (in inches)? "))
            qty = int(input("How many " + str(length) + " in. cuts are needed? "))
            Order.purchase_lines.append([(length + tolerance), qty])

    def generate_all_possible_cuts():
        """Generates an array of every possible way to cut a single piece of stock material"""
        first_possible_cut = Order.generate_first_possible_cut(Order.purchase_lines)
        another_cut_possible = Order.determine_if_another_cut_possible(first_possible_cut)
        previous_cuts = first_possible_cut
        while another_cut_possible:
            next_cuts = Order.generate_next_possible_cut(previous_cuts)
            previous_cuts = Order.append_to_all_possible_cuts(next_cuts)
            another_cut_possible = Order.determine_if_another_cut_possible(previous_cuts)

    def generate_first_possible_cut(purchase_lines):
        """Using the cut lengths and quantities need, cutting the most pieces possible out of the stock material, starting with the first cut length entered, until the material has remainder of 0 or all cutting lengths have been attempted."""
        possible_cut_length_and_qtys = []
        remaining_material_length = Order.starting_material_length
        for line in purchase_lines:
            possible_cut_length = line[0]
            max_qty = line[1]
            possible_qty = int(remaining_material_length // possible_cut_length)
            if possible_qty >= 0 and remaining_material_length - (possible_cut_length * possible_qty) >= 0:
                if possible_qty > max_qty:
                    possible_cut_length_and_qtys.append([possible_cut_length, max_qty])
                    remaining_material_length -= possible_cut_length * max_qty
                else:
                    possible_cut_length_and_qtys.append([possible_cut_length, possible_qty])
                    remaining_material_length -= possible_cut_length * possible_qty
        Order.remaining_material_length = remaining_material_length
        return copy.deepcopy(possible_cut_length_and_qtys)

    def determine_if_another_cut_possible(previous_cuts):
        """Determines if another cut is possible.  Returns True if possible, False if not possible."""
        for cut in previous_cuts:
            cut_qty = cut[1]
            if cut_qty > 0:
                return(True)
                break
        else:
            return(False)

    def generate_next_possible_cut(previous_cuts):
        """Using the previous counts, generates the next possible cut."""
        index_of_lowered_digit, lowered_cuts = Order.lower_smallest_digit_possible(previous_cuts)
        Order.increase_remaining_material_length(index_of_lowered_digit)
        next_cuts = Order.use_remaining_stock(index_of_lowered_digit, lowered_cuts)
        return copy.deepcopy(next_cuts)

    def append_to_all_possible_cuts(next_cuts):
        """Appends the next possible way to cut the material to the array \"all_possible_cuts\" """
        Order.all_possible_cuts.append(next_cuts[:])
        previous_cuts = next_cuts
        return copy.deepcopy(previous_cuts)

    def lower_smallest_digit_possible(previous_cuts):
        """Using the previous counts, generates the next possible cut."""
        current_cut_index = len(previous_cuts) - 1
        while current_cut_index >= 0:
            if previous_cuts[current_cut_index][1] > 0:
                previous_cuts[current_cut_index][1] -= 1
                break
            else:
                current_cut_index -= 1
        return(current_cut_index, copy.deepcopy(previous_cuts))

    def increase_remaining_material_length(index_of_lowered_digit):
        """Using the previous counts, generates the next possible cut."""
        Order.remaining_material_length += Order.purchase_lines[index_of_lowered_digit][0]


    def use_remaining_stock(index_of_lowered_digit, lowered_cuts):
        """Using the previous counts, generates the next possible cut."""
        current_index = index_of_lowered_digit + 1
        while current_index < len(lowered_cuts):
            if Order.remaining_material_length >= lowered_cuts[current_index][0]:
                qty = int(Order.remaining_material_length // lowered_cuts[current_index][0])
                lowered_cuts[current_index][1] += qty
                Order.remaining_material_length -= lowered_cuts[current_index][0] * qty
            current_index += 1
        return copy.deepcopy(lowered_cuts)

Order.get_order_information()
Order.generate_all_possible_cuts()
print(Order.all_possible_cuts)
# test with: 144, 2, 19, 15, 16, 30